test.ical.ly | getting the web by the balls

Sep/11

1

Beware of the timezone! Working with PHP DateTime and Doctrine for MongoDB can cause conflicts

Doctrine ODM for MongoDB is awesome! Even if you have no experience with NoSQL databases you’ll find the API intuitive and nice to work with.

Dealing with dates though can potentially cause confusion.

I really like Doctrines way of dealing with dates and times. Instead of passing timestamps around or strings with ISO date format it simply makes use of the PHP native type DateTime which provides all you really need to work with.

PHPs interface to MongoDB however comes with its own MongoDate type which provides hardly any functionality compared to DateTime and it’s not compatible with it.

So what’s the difference?

Well MongoDB is not aware of timezones and so is MongoDate. It simply holds the number of seconds passed since the beginning of Unix time to whatever you told it is now.

As long as you are in the GMT timezone (±00:00) you’ll probably never notice but when you are outside of it you can get confusing results especially for times around midnight.

So as a pro tip: check your dates and times for the values you query for and for the values you get as results and compare. If they are just a few hours off you should rework your code.

· · ·



  • http://blog.vworld.at/ David

    This is actual a fare bigger issue, both in Doctrine and Propel. The source of the troubles are RDBMS that don’t store timezones (most popular: MySQL). And even if the database supports timezones (such as Postgres), the ORMs ignore them by transforming all DateTimes to something like “Y-m-d H:i:s”. (At this point I must admit that I don’t know if Doctrine2 keeps the timezone when using Postgres. Propel doesn’t).

    The only valid solution is quite easy in theory, but somewhat cumbersome when implemented: set the server timezone to UTC (no excuses), and filter all DateTime in- and output. This is the ONLY way to keep all DateTime information accurate (think DST!).

    I once implemented such a layer for sf1 (together with generic configurable datetime rendering logic), but it’s not ready for publication yet. I eased the usage by providing accessors through a Propel behavior and form widgets and validators to convert user input.

    Beware of the timezone! Always…

  • Malte Blättermann

    Yes, David, handling Date and Time is always a pain!

    Your way doing it with UTC on Storage and Server seems a good solution to mee. Same as using UTF-8 for Chars…

    Even if your code isn’t finished and only for SF1, I want ask you to bring it up on Github!?

    Maybe some others will find it helpful, too..

    @Chris: Great Articles this week, Thanks again!

    Malte

  • Frank

    Mmh, i think it is no mongodb specific problem too. When you have to work with timezones, switch to UTC as system timezone and store every timestamp in UTC then. The application has to decide then how the data has to be transformed to fullfil the user’s needs.

    Check this blog post from Kris http://kriswallsmith.net/post/136226720/doctrine-timestamps-and-user-timezones. I did some equal stuff for one symfony1 project and it worked quite well.

  • http://test.ical.ly Christian

    @Frank thanks a lot for the link!

  • Spacemonkey

    The article is wrong, in that MongoDB *is* aware of timezones and stores it as part of the date object. Here’s an example, from a one-year-old blog post:

    http://www.lightcubesolutions.com/blog/?p=373

    The 1.8 series started representing the date objects as objects, whereas the old 1.6 used to make them look like strings (when they were not, causing a ton of confusion). I’m assuming this is what led the author to assume that MongoDB wasn’t aware of timezones.

    Using the mongo shell, you can create a standard javascript date() object, just to demonstrate:

    > thingie = {one: "foo", two: "bar", when: new Date()};
    {
            "one" : "foo",
            "two" : "bar",
            "when" : ISODate("2011-09-02T09:09:30.588Z")
    }
    > db.deleteme.save(thingie);
    > db.deleteme.findOne();
    {
            "_id" : ObjectId("4e609d63c632112c8fb4fae7"),
            "one" : "foo",
            "two" : "bar",
            "when" : ISODate("2011-09-02T09:09:30.588Z")
    }
    

    Hope this clears things up, the issue isn’t MongoDB but the way doctrine is forced to generalize date formats across many disparate systems (some of which do a really poor job of timezone support).

  • Spacemonkey

    Here’s a note on UTC DateTime, BSON and MongoDB:

    http://www.mongodb.org/display/DOCS/UTC+DateTime+data+type

    That said, the best approach (and the one I have taken for years now) is the one offered by David above: Standardize all dates as UTC, and then display in local timezones based on the client.

  • http://test.ical.ly Christian

    @Spacemonkey thanks for the comments. I wasn’t aware of this. Might be a PHP or Doctrine thing I experienced but I will definitely check on it at some point. So far I can confirm that using the MongoDB console it appears to be timezone aware.

<<

>>

Theme Design by devolux.nh2.me