Skip to content

Instantly share code, notes, and snippets.

@nerrad
Last active August 29, 2015 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nerrad/a758d31557d3d8c1681f to your computer and use it in GitHub Desktop.
Save nerrad/a758d31557d3d8c1681f to your computer and use it in GitHub Desktop.
Datetime Overview

Prep for datetime meeting

First some semantic things:

###GMT vs UTC.

At times you may hear me or other devs interchangelabely use GMT or UTC. It's important to note that when we say GMT, we usually are just talking about the equivalent of UTC +0.

However, technically the two terms are not the same thing. GMT is an actual timezone, whereas UTC is a time standard. GMT is in used as the official timezone in some European and African countries, where as UTC isn't officially used for local time anywhere. As a time standard UTC is what one uses to calculate offsets for timezones.

Unixtimestamp

A Unixtimestamp is a way of describing a particular moment in time with no concern for timezone. It represents the number of seconds that have elapsed since UTC Thursday, 1 January 1970. So if you were able to simultaneously request and display the unixtimestamp right now in multiple places all over the world, the value for that timestamp will be the same. This is very important to remember because I'll be referring to it. Anytime you use the function time() in PHP it spits out the unixtimestamp and that value will be the same no matter WHAT timezone your server (or PHP) is set to.

Try it:

var_dump(time()); date_default_timezone_set('Asia/Singapore'); var_dump(time());

Second

Event Espresso is primarily about events and events always involve dates and times, in fact dates and times are a integral part of our product. So any failures with dates and times, are critical fails.

End goals of Datetimes in EE (what they have been from the beginning)

1. Simple

Datetimes are a complex subject when you throw timezones and all the different formats into the mix

  • Developers need a simple way to interact with dates and times
  • End Users need a simple way to modify how dates and times are displayed on their site.

2. Clear and Consistent

It should be easy to point to a date and/or time and know what timezone its in, and what format it is in.

  • Developers need to have a clear understanding of what timezone a given date and time string is in, and what format it is in, and have a clear way to interact with the system where things are NOT ambiguous.
  • End Users shouldn't be having to have the date and time interface explained to them. They should also be able to have the dates and times in their language and display to their site visitors in their language.

3. Flexible

Dates and Times can be displayed in many different formats, timezones and languages (both human and machine).

  • Developers should be able to interact with our system using a variety of formats, and timezones and via different machine languages (php, js, mysql, json etc).
  • Users should be able change the way dates and times are displayed on the frontend of their site and in their administrative pages. End users should have flexibility in displaying dates and times in creative ways (i.e. calendar type boxes) (Eventually), The visitor to users sites, should be able to control how dates and times are formatted and modify them so they are in their own timezone.

Background of how dates and times work in WordPress.

1. WordPress sets the offset for all php date related processing to UTC +0 (or GMT).

This means that when you use a php related date function like date() it returns the result in UTC+0.

2. In the General Settings page for WordPress, users are able to set the format for how dates and times are displayed on their site.

This means whenever dates and times are displayed on their WordPress site, they will be in the given format and given timezone.

Very important: WordPress makes the assumption that once people set their timezone on this page, they will never be changing it (even though you CAN change it).

3. WordPress saves dates in the database for posts in four columns.

Two for the date in the current timezone (post_date, post_modified_date) and two for the date in gmt (utc+0) (post_date_gmt, post_modified_date_gmt).

In BOTH cases, the date is saved in the mysql timestamp format (Y-m-d H:i:s).

When post dates are retrieved by default from the WP database using their date functions. The default date retrieved on the frontend is post_date or post_modified_date, unless wp is doing internal date and time calculations in which case it uses the gmt values (eg publishing a post that was set on a schedule for being published) or unless the others are explicitly requested.

The advantages to doing things this way, is because wp can then use php date functions to just transform and display the save "post_date" and it will always be in the correct set timezone for the end user. Then they still have GMT for falling back on when doing date and time calculations.

However remembmer WP is assuming people will never change their timezone (which in reality is actually a fairly safe assumption to make in most cases for simple blogs). Why is this a gotcha? Because IF someone changes their timezone then all of their existing posts will have the date displayed for those posts as originally saved (which is for the date in the old timezone). Which for all intents and purposes is accurate, but to the end user it may appear incorrect because its not displaying it in the date and time for the current timezone. If the end user wants to update things, then they need to go into each post and modify the publish date (or write a script to automatically convert existing post_date and post_modified_date to the current timezone values using the saved gmt values.

My guess is that WP did this because it saves a having to convert things to and from GMT time and thus makes things efficient (while still being able to query against that gmt time). The huge downside to this approach in my opinion, is the assumption that people will rarely change the timezone for their website. Frankly, I don't think that's an assumption we can make with Event Espresso, especially if we are ever going to offer the feature where people can list Event dates and times for specific timezones (separate from the set site timezone).

4. current_time().

current_time() is a function in WordPress that is intended to make it easier to quickly get a correct date and time string for NOW on this website, without having to worry about timezone stuff.

It returns two possible formats mysql and unixtimestamp. It has a flag that allows you to indicate you want the date returned in.

Here's the incredible gotcha with WordPress.

If you do current_time( 'timestamp' ), WordPress will return a unixtimestamp. But its actually NOT equivalent to what you'd get with time(). In fact, what WordPress does, is they ADD the offset for the set timezone on the site TO that unixtimestamp. The reason they do this, is so if you do something like date( 'Y-m-d H:i:s', current_time('timestamp' ) ), it will just output the date as is because remember internally, WP has set all php date functions to UTC. Since UTC+0 has no offset, it will take an incoming unixtimestamp and not apply any offset. So WordPress APPLIES the offset ahead of time. If one was to do date( 'Y-m-d H:i:s', time() ) that returns the date for UTC+0. This is a HUGE gotcha, because developers not familiar with what WP is doing here will think it is returning an actual unixtimestamp, its not.

You CAN get the actual unixtimestamp by doing current_time( 'timestamp', true ).

5. Localization.

WordPress has a neat system in place for localizing Date and time display via the function date_i18n(). The way it works is you send it a format you want the result in a "unixtimestamp", and whether it should be in GMT (UTC+0) or not, then it spits out that format localized in the set language for the site.

Here's the gotcha, when you send it a "unixtimestamp" (regardless of what you set the gmt flag to), WordPress considers that unixtimestamp to be their specially computed timestamp with the offset applied NOT an actual unixtimestamp. The GMT flag just indicates whether you want the returned string to be CONVERTED to GMT before returning. However IF you send in an actual unixtimestamp, then that will get "converted" to GMT and thus will be returned with what appears to be a double offset from the ACTUAL time in the set timezone for the current site.

6. Summary

There's more, but the reason I wanted to very briefly go over how Dates and Times work in WordPress is to illustrate that as long as you operate on the assumptions WordPress makes, and are aware of how things work internally, everything works out great.

However, the way WordPress does things, conflicts with the needs of EE (see the goals).

Background of initial Datetime implementation

Keep in mind, in the initial implementation of the datetime system, we still supported PHP5.2 . This meant a lot of useful php date functions that would have made our lives easier at that time were NOT available to us because they required PHP5.3 - at the time... we didn't think that was a big deal....

1. In effort to keep it simple.

Users:

  • Allow them to use a familiar interface for setting how their dates and times display (the WordPress general settings) and the timezone for how everything displays. So really for users with existing sites this would "just work".

Developers:

  • EE_Datetime_Field handles the complex conversions necessary to make sure data gets to and from the database as expected. Throw any format at it for incomign dates and it should "just work".

The problems

  • we discovered that we couldn't just let people set the format to whatever they wanted (even one of the suggested DEFAULT formats (d/m/Y) provided by WordPress), because for our internal conversions, we had to use strtotime(), and strtotime() has a limited number of formats it can convert correctly. So as a "fix" we didn't allow people to set their date/time formats to something that EE couldnt' convert.
  • the ticking BOMB with this, is that our "preventative" measures (that I'm aware of) do not catch users who install EE on a pre-existing WordPress site that is using one of those formats. They may have had their site up for a while using one of those formats with no problems and all of a sudden they install EE and it doesn't work correctly. Then they complain to us and our response to them will be, "Sorry, you can't use that format".
  • we also discovered that WordPress allows users to set their sites to a UTC offset that PHP doesn't recognize as valid. Our workaround was to behind the scenes set that offset to the closest valid one and use it instead (this doesn't change even with the refactor).

2. Keep things Clear and Consistent

Users:

  • for the most the format for all our date-pickers in the backend was fairly consistent (although users are not able to change that format).
  • For the most part anywhere in dates and times were just displayed (and not interacted with), they followed the format that was set by them in their WordPress settings, and in the timezone they expected

Developers:

  • For the most part what was made accessible to developers was useful. They had a clear set of methods to get dates and times from the database, and a clear set of methods to send dates and time into the database.
  • Clear zones describing how dates and times "live" in those zones.
    • DB - mysql timestamp UTC+0
    • Models - unixtimestamp (no offset applied).
    • Client Code - whatever WordPress was set at.

the problems

  • localization? What localization? In the initial iteration of the datetime system I completely forgot about setting things up so dates and times would be localized when displayed.
  • As fixes were implemented to make localization possible, all the gotchas I mentioned above regarding how WordPress does "unixtimestamp" camp into play.
  • Date calculations by client code were not easy to do and a lot of conversions necessary
  • EE_Datetime_Field had a TON of code handling conversions to cover different scenarios that would get thrown at it and to account for the deficiencies with PHP5.2. Flaws in these conversions were found and bandaid fixes applied. There started to be a multitude of different functions and methods for developers to use as a result of fixing some of these deficiencies. It became ambigous to the user not familiar with the system how queries should be setup in the different zones for datetime related items... should I use current_time('timestamp', true)? Should I use current_time('timestamp')? Should I use date('Y-m-d H:i:s', strtotime( $date_string ) )? Will this query still work when we build a feature for people to change the timezone for how things are displayed? Or when they can have events saved in multiple timezones?
  • things have become increasingly unclear and inconsistent.

3. Flexibility

Users

Developers

  • Still some flexibility if you are willing to ignore using our system and just build your own tools for interacting with dates and times (which also applies to any addons we build that use datetimes (Here's looking at you REM) ) )
  • Be very careful what you send via queries otherwise you will get unintended results.

Which brings us to the refactor

See https://events.codebasehq.com/projects/event-espresso/tickets/7685#update-22053095

Except... now we want to take it one step further.

Even with the refactor I did, there is still some ambiguity due to wanting to keep backward compatability in place. This stems from the fact that the refactor still assumes every unixtimestamp coming INTO our models is the special calculated timestamp that WordPress creates with an offset applied. Thus we do conversions to account for that.

Frankly, that creates more code (and more chances for bugs) than necessary and also makes the system more complicated than it needs to be. However, it was done to make the changes required in existing client code (including our addons) as minimal as possible (althought there are STILL some changes needed).

After talking it over with the devs, we are in agreement that IF we are doing this refactor, lets do it right, and not potentially keep having to fix things and create maintainance headaches (and third party developer headaches) by operating under that assumption. Instead, treat a unixtimestamp EXACTLY like what a unixtimestamp is SUPPOSED to be!! In other words, anytime you send a unixtimestamp into our models, its a unixtimestamp, period. That means devs can use the php time() function, or current_time('timestamp', true) and it will JUST work.

They STILL will be able to use the provided helper methods for when they want to send things in as a formatted string in a set timezone, however the default to provide a unixtimestamp will always work AS EXPECTED.

So the implications of doing this however:

1. This does NOT affect saving of dates/times to the database because that is handled by core and core has been updated to do that correctly.

Nor does this affect how dates and times are currently saved in the database (mysql UTC+0). We still do the same. So the impact to actual data IN the database is nil. What this impacts is the retrieval and displaying of that data in the frontend and through queries for things around datetimes.

2. Existing addons and client code has to be modified to ensure anytime a unixtimestamp is used, it is not using a timestamp with offset applied.

3. This means that any addons in the wild, will need to have their code updated so that dates display properly on their sites (i.e. in the timezone for their blogs) and so that any queries involving datetimes return the expected results.

  • My approach to this would be to try and update existing addon code so it works both pre datetime fix and post datetime fix and get that release out asap. That will hopefully minimize the number of customers impacted by the changes.
  • For customers who never update, we will need to implement a mechanism for warning customers with addons in older versions (when the version of core with the datetime fixes is active) that their addons will not function as expected until they update them. We COULD deactivate these addons, but I think in most cases a persistent warning is sufficient because no database destruction will happen, they just won't get expected results.
  • We'll need to publicize and communicate this to customers and devs via our blog, sticky forum posts and other means ahead of the core release. Yes I know most people don't read, but sometimes the ATTEMPT to communicate things is just as important as the actual communication itself. We still must communicate.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment