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:
- the
availabilitymap on the episode endpoints, both sent and returned - the
$tier_datetimesargument of benecaster_availability_set - the values benecaster_availability_datetimes receives and returns
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. Usegmdate(), orcurrent_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 againstUTC_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 →