Release 1.6.2 introduced a new Helper class for the handling of date variables. The helper was written to make it easier to handle the various dates that are handled by the component. Suppose we have a situation where a User A fills out a form, to raise an issue. The issue stores a date variable that represents the date that the issue was opened. We wish to save this date for display when ever the issue is displayed, either back to the user or to any other authorised viewer. The best thing to do when the date is stored in the database is to convert it to UTC. You might ask why store it as UTC? The reason is that we are then providing ourselves with a starting point and when we present the output to the user, we can add different timezones depending on the users place or origin/display.
The technique of converting the date to and from UTC means that a helper class is an ideal place to locate the code,which will be used over and over again. For example an issue has a number of dates when an issue is stored. For example we have the identified date, an estimated completion date, a closure (resolution)date and also dates used in the auditing, for when the issue was first saved, and when it was last modified.
The helper routine makes use of the Joomla JDate function. Inspection of the JDate class in libraries/joomla/utilities/date.php shows that the constructor actually expects 2 parameters -> the date and the timezone. Thus when one saves a date one would generally want to do something like this:
$date = new JDate($myDate, $myTimezone);
Quite often however the timezone would not be specified and this would result in the date variable be treated as being in UTC. The question then becomes how to properly calculate a timezone? Fortunately Joomla provides a means to do that as well. Within out helper class we have a small routine as follows:
/** * Return the date with the correct specified timezone offset. * If a user timezone is specified use that otherwsie use the server timezone. * * param : raw date string (date with no offset yet) * return : JDate object */ public static function dateWithOffSet($str='') { $userTZ = self::getOffSet(); $date = new JDate( $str ); $user = JFactory::getUser(); if($user->id != 0) { $userTZ = $user->getParam('timezone'); } if (empty($userTZ)) { $jversion = new JVersion(); if ( version_compare( $jversion->getShortVersion(), '3.2', 'ge' ) ) { $userTZ = JFactory::getApplication()->get('offset'); } else { $config = JFactory::getConfig(); $userTZ = $config->get('offset'); } } $tmp = new DateTimeZone( $userTZ ); $date->setTimeZone( $tmp ); return $date; }
As can be seen in the code above we first try to obtain the user's timezone, and then we get the global config timezone. If the user has set a timezone in their configuration, then the value of it is passed to the DateTimeZone object. If the user on the other hand has not set a timezone, or the guest user has not set a timezone, then we use the global one. Now that we have the correct time zone we can format the date to the MySQL format and store it in the database.
To save the date in the database we now want to convert it to the correctly formatted string. Depending upon the version of PHP in use will influence how we do this. In PHP 5.4 and above method chaining is possible so we could use a function as follows:
$tz = 'EUROPE/LONDON'; $d11 = (new JDate($odate), $tz)->format('Y-m-d H:i:s', false, false);
In PHP 5.3 and below we have to call the method upon the variable instead. Since we do not know what versions of PHP is in use we assume that the PHP 5.3. syntax is the most appropriate as follows:
$tz = 'EUROPE/LONDON'; $d11 = new JDate($odate, $tz); $d12 = $d11->format('Y-m-d H:i:s', false, false);
The first parameter to the format function is 'Y-m-d H:i:s' - this is the format we want our date to be saved in the db. The second parameter tells the function that we want to have the GMT/UTC time and the third parameter tells the format function that there is no need to translate the date. We can then save the date/time variable ($d12) in the database in the standard way.
Note | |
---|---|
There are also a few other things worthy of note:
|
The Issue Tracker component has a fallback auditing feature such that if the 'creation date' is null when a record is saved, or if the 'modified date' is null when a record is updated, then a database trigger will automatically populate the fields when the record is saved. Prior to release 1.6.2 the triggers made use of the database function SYSDATE() which returns the current date and time in YYYY-MM-DD HH:MM:SS or YYYYMMDDHHMMSS.uuuuuu format depending on the context of the function. The SYSDATE function date/time value is the time when the actual statement executed. This is slightly different from the NOW function (described below) which is the time when the statement started executing. The distinction may seem imprecise and for most purposes the difference is likely to be slight if any.
The database function NOW() which returns the value of current date and time in ‘YYYY-MM-DD HH:MM:SS’ format or YYYYMMDDHHMMSS.uuuuuu format depending on the context (numeric or string) of the function. The database functions CURRENT_TIMESTAMP, CURRENT_TIMESTAMP(), LOCALTIME, LOCALTIME(), LOCALTIMESTAMP, LOCALTIMESTAMP() are all synonyms of NOW(). The NOW() returns the constant time when the statement began to work.
There are a few specific UTC date time functions available in MySQL. These are UTC_DATE, UTC_TIME and UTC_TIMESTAMP. The most appropriate of these is the UTC_TIMESTAMP function since we wish to have the date and the time available. As of release 1.6.2 the triggers now use the database function UTC_TIMESTAMP which returns the current UTC (Coordinated Universal Time) date as a value in 'YYYY-MM-DD HH:MM:SS' or 'YYYYMMDDHHMMSS.uuuuuu' format depending on the context of the function i.e. in a string or numeric context.