Dates are not stored in GMT for event instances

Created on 10 July 2025, 3 months ago

Problem/Motivation

When creating an event series, the inherited start and end dates for the event are stored in the database with the GMT timezone offset already applied.

This makes querying that value, manupulating its output, or otherwise working with it difficult.

Steps to reproduce

- Set your site timezone to America/New York
- Create an event series that starts at 5 pm (17:00)
- Witness that the data is stored as 22:00 (17:00 + the 5 hour GMT offset)

Proposed resolution

- Store the date in GMT as a UTC, like Drupal core does.

Remaining tasks

- Determine where the date gets altered and stop it.

User interface changes

- Unknown

API changes

- Unknown

For reference, here's what I had to do to format the upcoming date of an event:

    $items = $variables['items'];
    foreach ($items as $index => $item) {
      // Extract the event instance date and URL.
      $event_instance = $item['content']['#eventinstance'];
      // Extract the event instance date and URL.
      // The date is stored in the 'date' property of the event instance, but
      // that is offset by the timezone, so we need to format it correctly.
      $date = date_create($event_instance->date->value);
      $offset = date_offset_get($date);
      $datetime = strtotime($event_instance->date->value) + $offset;
      $items[$index]['content']['formatted_date'] = \Drupal::service('date.formatter')->format($datetime, 'custom', 'M j, Y g:i A');
    }

Data model changes

- Dates are stores as UTC

πŸ› Bug report
Status

Active

Version

2.0

Component

Recurring Events (Main module)

Created by

πŸ‡ΊπŸ‡ΈUnited States agentrickard Georgia (US)

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @agentrickard
  • πŸ‡ΊπŸ‡ΈUnited States agentrickard Georgia (US)

    Here is perhaps a better example of the issue.

    The date is being stored in the database as an incomplete ISO Timestamp (e.g. "2025-01-14T22:30:00"). Storing in this way instead of as a Unix Timestamp -- which assumes GMT -- doesn't give a developer enough context to trust the value.

    Consider:

    $event_instance->date->value =>  "2025-01-14T22:30:00"
    
    \Drupal::service('date.formatter')->format(strtotime($event_instance->date->value), 'custom', 'M j, Y g:i A') => "Jan 14, 2025 10:30 PM"
    

    To work around that, we either have to add the TZ offset to the ISO string before conversion, or calculate it afterwards.

    Here's what I ended up with (which should be daylight savings compatible):

          $event_instance = $item['content']['#eventinstance'];
          // Extract the event instance date and URL.
          // The date is stored in the 'date' property of the event instance, but
          // that is offset by the timezone, so we need to format it correctly.
          $timezone = \Drupal::config('system.date')->get('timezone.default');
          $date = date_create($event_instance->date->value, timezone_open($timezone));
          $offset = date_offset_get($date);
          $datetime = strtotime($event_instance->date->value) + $offset;
    

    This is very painful DX and should be corrected at the module level. Storing a full ISO date string or a Unix timestamp are both valid options.
    Reference: https://www.php.net/manual/en/datetime.format.php

  • πŸ‡§πŸ‡¬Bulgaria pfrenssen Sofia

    We're using the regular DateRangeItem field type, you can get a DrupalDateTime object for the start time and end time which can be used to format the date range for the desired time zone. For example:

    $event_instance = $item['content']['#eventinstance'];
    $start_date = $event_instance->date->start_date->format('M j, Y g:i A', ['timezone' => 'America/New_York']);
    $end_date = $event_instance->date->end_date->format('M j, Y g:i A', ['timezone' => 'America/New_York']);
    

    I just checked how this works in core and the data is stored in the same format when I add a daterange field to a node type. I checked with the ISO 8601 standard and indeed, the date format used by the datetime_range module is ambiguous. The dates should have a Z suffix to properly indicate this is a UTC time.

    I think the most relevant core issue is ✨ [PP-1] Add ability to select a timezone for datetime field Postponed .

  • πŸ‡§πŸ‡¬Bulgaria pfrenssen Sofia
Production build 0.71.5 2024