Using Date and Datetime Elements, and Avoiding the Timezone Trapfor Drupal 11 , 10 , 9 , and 8

Last updated :  

Dates look simple until you actually have to store, display, and compare them across timezones — and Drupal's Form API gives you two closely related but distinct element types to handle that: date for a date alone, and datetime for a date paired with a time.

What you'll learn in this lesson

  • The difference between #type => 'date' and #type => 'datetime'
  • The submitted value's actual format for each
  • Why timezone handling is the detail most likely to bite you

A date-only field

$form['event_date'] = [
  '#type' => 'date',
  '#title' => $this->t('Event date'),
  '#default_value' => '2026-06-15',
];

#default_value and the submitted value are both plain YYYY-MM-DD strings — no time component, no timezone involved at all, since a date-only field is genuinely just a calendar date with no notion of a specific moment.

A date-and-time field

$form['appointment'] = [
  '#type' => 'datetime',
  '#title' => $this->t('Appointment'),
  '#date_time_element' => 'time',
];

datetime submits a \Drupal\Core\Datetime\DrupalDateTime object, not a plain string — a real, timezone-aware PHP object wrapping the value, ready to format or compare however your code needs.

$date = $form_state->getValue('appointment');
// $date instanceof DrupalDateTime
$formatted = $date->format('Y-m-d H:i');

The timezone gotcha

A datetime field's widget displays and accepts the value in the current user's configured timezone — but you'll usually want to convert it to UTC before storing it, so the stored value means the same instant regardless of which timezone reads it back later.

$date->setTimezone(new \DateTimeZone('UTC'));
$stored_value = $date->format('Y-m-d\TH:i:s');

Skip this conversion and you'll get a genuinely confusing bug: an appointment that was set correctly for one user displays at the wrong hour for a colleague in a different timezone, because the raw local-time string was stored with no timezone context attached at all.

Storage format matters as much as the timezone: Drupal's own convention for storing datetime values in config or a plain database column is ISO 8601 in UTC (2026-06-15T14:30:00) — matching this convention, rather than inventing your own format, keeps your data compatible with core's own date-handling utilities and with any contrib module that expects the standard shape.

Quick check: a user in India (UTC+5:30) sets an appointment for 3:00 PM local time. If you store the raw local-time string with no timezone conversion, what does a user in New York see when the value is displayed? (Also "3:00 PM" — wrongly, since the stored string carries no timezone context. Without converting to UTC before storage, the exact same string gets displayed as-is regardless of the reader's actual timezone, silently producing the wrong time for anyone not in the original timezone.)

Key takeaways

  • date submits a plain YYYY-MM-DD string with no time or timezone component at all.
  • datetime submits a real DrupalDateTime object, displayed in the current user's timezone.
  • Convert to UTC before storing any datetime value — skipping this produces values that silently mean different things to users in different timezones.

Coming up next

You've now covered the full catalog of common input elements. The last one in this section isn't for collecting data at all — it's for finishing the form: the submit button.