Skip to content

Instantly share code, notes, and snippets.

@dfwood
Last active March 6, 2017 20:24
Show Gist options
  • Save dfwood/6196705 to your computer and use it in GitHub Desktop.
Save dfwood/6196705 to your computer and use it in GitHub Desktop.
Datetime conversion functions for WordPress using the ISO8601 standard.
<?php
/**
* Why does this exist? http://xkcd.com/1179/ also because it makes sense to have a standard way in WordPress to work with
* dates and times so that plugin developers can create plugins that will work in any timezone worldwide.
*
* WordPress sets the default timezone during execution to UTC, reguardless of what the server is set to.
* This means that date( 'FORMAT' ) will always give you the current time in UTC, not the timezone you have set
* in the WP admin settings. Use date( 'FORMAT', current_time( 'timestamp' ) ) instead to get WP local time. Likewise,
* use current_time( 'timestamp' ) instead of time() for the local time.
*/
/**
* Changes the timezone on an ISO 8601 formatted datetime string to GMT
*
* @param string $date_string ISO 8601 formatted date string (e.g. 2012-12-21T13:52-05:00 would be Dec. 21, 2012, at 1:52PM EST)
* @return string Returns ISO 8601 formatted date string in GMT/UTC time (+00:00 timezone)
*/
function iso8601_to_gmt( $date_string ) {
// Split the datetime into individual items
preg_match( '#([0-9]{4})-?([0-9]{2})-?([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(Z|[\+|\-][0-9]{2}:?[0-9]{2}?){0,1}#', $date_string, $date_bits );
if ( ! empty( $date_bits[7] ) ) { // we have a timezone, so let's compute an offset
$date_bits[7] = str_replace( ':', '', $date_bits[7] ); // Make sure this is in the right format for the iso8601_timezone_to_offset() function ( no colon ( : ) )
$offset = iso8601_timezone_to_offset( $date_bits[7] );
} else { // we don't have a timezone, so we assume user local timezone (not server's!)
$offset = HOUR_IN_SECONDS * get_option( 'gmt_offset' );
}
$timestamp = gmmktime( $date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1] );
$timestamp -= $offset;
return gmdate( 'c', $timestamp );
}
/**
* Changes an ISO 8601 formatted date from GMT/UTC (+00:00) to WordPress' set time or the timezone supplied.
*
* @param string $date_string ISO 8601 formatted datetime in GMT/UTC
* @param string $format The format you want the date to be returned in, defaults to ISO 8601. See http://php.net/manual/en/function.date.php for formatting options
* @param string|null $tzID The timezone identifier of the timezone to convert to, see http://php.net/manual/en/timezones.php, defaults to what is set in the WP admin
* @return string Returns formatted date string in either WordPress' local time or the specified offset
*/
function iso8601_gmt_to_local( $date_string, $format = 'c', $tzID = null ) {
if ( null === $tzID ) {
$tzID = get_option( 'timezone_string' );
}
if ( null === $tzID ) {
$offset = ( get_option( 'gmt_offset' ) * -1 );
$tzID = 'Etc/GMT' . ( ( 0 > $offset ) ? $offset : '+' . $offset ); // This line is here for compatibility reasons
}
$tz = new DateTimeZone( $tzID );
$date = DateTime::createFromFormat( 'U', strtotime( $date_string ) );
$date->setTimezone( $tz );
return $date->format( $format );
}
/**
* Changes an ISO 8601 formatted date to WordPress' set time or the timezone supplied.
*
* @param string $date_string ISO 8601 formatted datetime
* @param string $format The format you want the date to be returned in, defaults to ISO 8601. See http://php.net/manual/en/function.date.php for formatting options
* @param string|null $tzID The timezone identifier of the timezone to convert to, see http://php.net/manual/en/timezones.php, defaults to what is set in the WP admin
* @return string Returns formatted date string in either WordPress' local time or the specified offset
*/
function iso8601_to_local( $date_string, $format = 'c', $tzID = null ) {
return iso8601_gmt_to_local( iso8601_to_gmt( $date_string ), $format, $tzID );
}
@wpscholar
Copy link

I think we need a separate function that reliably gets the correct timezone string. This function should be called in the place of lines 41 - 47 in your code above if the user doesn't explicitly set the timezone.

This is what I have come up with:

function get_timezone() {
    $timezone = get_option( 'timezone_string' );
    if( empty( $timezone ) ) {
        $timezone = sprintf( 'UTC%+.4g', get_option( 'gmt_offset', 0 ) );
    }
    return $timezone;
}

@wpscholar
Copy link

Lines 48 - 50 can be condensed to this:

$date = DateTime::createFromFormat( 'U', strtotime( $date_string ), new DateTimeZone( $tzID ) );

NOTE: In actual testing, this didn't seem to properly set the timezone.

@wpscholar
Copy link

So rewriting the iso8601_gmt_to_local() function would look like this:

function iso8601_gmt_to_local( $date_string, $format = 'c', $timezone = null ) {
    $timezone = is_null( $timezone ) ? get_timezone() : $timezone;
    $date = DateTime::createFromFormat( 'U', strtotime( $date_string ) );
    $date->setTimezone( new DateTimeZone( $timezone ) );
    return $date->format( $format );
}

@wpscholar
Copy link

https://gist.github.com/dfwood90/6196705#file-wp-iso8601-datetime-php-L46

I believe this should be 'Etc/GMT' . ( ( $offset < 0 ) ? $offset : '+' . $offset ); should it not?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment