midgard_datetime
Posted on 2009-01-22 18:39:20 EET.
I started to work on midgard_datetime class. Pure PHP class which should represent any property of Midgard MgdSchema object which holds datetime string. A pure, means that it focuses on PHP language only, and in fact, it's implemented in midgard extension.
Why midgard_datetime? And not just simple DateTime class, as suggested in related ticket? Answer is simple. PHP has limits. Imagine, every datetime property is propagated as DateTime object. And when you want to read it, you simply invoke format() method. But what to do when you need to change date or time? You invoke setDate() method and what next? Did underlying GObject's property value has been changed as well? No. There's no features like signal emission or simple event notification on PHP level. So, even if you write own callback, no single line of code will trigger it (As a decent framework Midgard supports events).
I wrote midgard_datetime class (DateTime derived) with own underlying C structure and assigned own hooks for it. All in all, at some point I need to know what is the object and what is the property a midgard_datetime holds value for. And this failed because DateTime objects hold own underlying C structures, so when all Zend OO related hooks return void type, you never know what you really get: DateTime structure, midgard_datetime one or maybe mather-in-law? Implementation simplified. But it doesn't mean it became more elegant. I had to register private properties. Yes, this is common for PHP code, but on C level you have armory of possibilities, and had to forget about it.
Examples
In Ragnaroek, you used to print datetime property:
echo $object->metadata->created;
and in vinland, what you wanted to print is an object, so you need to invoke its method:
echo $object->metadata->created->format("c");
midgard_datetime is automagically initialized with DateTimeZone object, so if you need timezone you invoke getTimezone method. Of course, the only one correct timezone for Midgard datetimes is UTC. And you can not change it.
For more flexibility, every DateTime (or derived one) class' object is converted to GValue which holds ISO 8601 formatted string, so ot only objects are "affected" by such conversion. You can pass such objects as Query Builder constraints' values:
$md = new midgard_datetime();
$md->setDate(2009, 01, 02);
$qb = new midgard_query_builder("midgard_article");
$qb->add_constraint("metadata.published", ">", $md);
Performance
I made two simple tests. Both have a loop (100 steps) and assign string datetime. First one uses:
$a = new midgard_article();
$b = $a->title;
and takes from 0,0060 to 0,0080 seconds. This test is almost the same as assigning datetime property the "old" way.
Second one uses midgard_datetime:
$a = new midgard_article();
$d = $a->metadata->created->format("c");
and takes from 0,0180 to 0,0230 seconds.
Looks like midgard_datetime implementation is up to 4 times slower. But as it extends DateTime, there's many features implemented "for free". And if you take into account, that first test requires additional function calls, like strtotime() and date() (to format output), midgard_datetime implementation will be up to 2 times slower. And I think it's not a big problem. midgard_datetime object is created on demand, only when you need to read property. And in practice, you do not need thousands of datetimes available at the same time.
Issues
MySQL doesn't work with constraint used in QB's example. It simply treats string verbatim, so SQL part looks like this:
WHERE metadata_published = '2019-01-01T00:00:00+00:00'
It works, if you remove timezone offset. So I believe fixing the issue is a matter of date formatting.
Also there's no possibility to convert datetime values (returned by collector for example) to midgard_datetime objects. Such values are initialized as GValue values of string type. And this should be fixed by wider midgard timestamp values usage in core.
You can install latest packages, if you would like to try those.