Skip to main content

Dates, Times and Time Zones

Benecaster stores every date and time in UTC. It converts to the site’s time zone only when it shows a date to a person. Code that reads, writes or compares Benecaster’s dates has to follow the same rule. If it doesn’t, it works on a server set to UTC and is off by a few hours everywhere else.

The Rule

  • Stored: UTC, as Y-m-d H:i:s, with no offset attached.
  • Shown: in the site’s time zone (Settings → General → Timezone), converted when the value is displayed.
  • Typed in by a podcaster: in the site’s time zone, converted to UTC when Benecaster saves it.

Why UTC

In a time zone with daylight saving, one hour each autumn happens twice and one hour each spring never happens. A stored local time of 01:30 on the autumn change night could mean either of two moments, and nothing in the value says which. UTC has no gaps or repeats, so a stored UTC value always means exactly one moment.

What Your Code Receives

REST API. Benecaster’s own datetime fields in responses are UTC Y-m-d H:i:s strings, such as last_accessed_at on the Subscribers REST API. A field in ISO 8601 format carries its own offset, as exported_at in an export file does. A date taken straight from a WordPress post, such as an episode’s post_date, is in the site’s time zone, because that is how WordPress stores it. Each endpoint’s page states which format a field uses.

Hooks. A time passed to an action is either a Unix timestamp or a UTC datetime string. Each hook’s page states which. A Unix timestamp counts seconds from a fixed instant, so it has no time zone to get wrong.

The Exception: Episode Availability

Episode availability dates are the one place where Benecaster’s developer surfaces use the site’s time zone, because they are the values a podcaster types into the episode editor. These are all site-local:

Benecaster converts them to UTC once, when it saves them. Pass site-local values. If you convert to UTC first, the value is converted twice, and every affected episode moves by the site’s offset with no error to show for it.

benecaster_availability_unlocked is not an exception. It passes a Unix timestamp, like any other action.

Writing Your Own Code

Write the current time in UTC:

$now = gmdate( 'Y-m-d H:i:s' );

Show a stored UTC value in the site’s time zone:

// A stored datetime string.
echo esc_html( get_date_from_gmt( $row->last_accessed_at, get_option( 'date_format' ) ) );

// A Unix timestamp.
echo esc_html( wp_date( get_option( 'date_format' ), $timestamp ) );

Three WordPress and MySQL functions return the wrong clock for this:

  • current_time( 'mysql' ) returns site-local time. Use gmdate(), or current_time( 'mysql', true ), which returns UTC.
  • date_i18n() is not a display function for a UTC value. It assumes the offset has already been applied, so it shows a UTC time as if it were local.
  • NOW() in SQL uses the MySQL session’s time zone. WordPress never sets it, so it matches UTC on some servers and not on others. Compare against UTC_TIMESTAMP().

Testing

Test on a site set to a city time zone that observes daylight saving, such as New York or Sydney, rather than UTC. On a site set to UTC, every mix-up between UTC and local time gives the correct answer, so none of them show up.

See Also

Need this built rather than just documented? See our services →