Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

converting dates between timezones in AppModel->afterFind (cakePHP)

I have a cakePHP application that is pulling data from two different databases, which store dates and times in their data from different timezones. One database's timezone is Europe/Berlin, and the other's is Australia/Sydney. To make things more complicated, the application is hosted on a server in the US, and times must be presented to the end user in their local timezone.

It's easy enough to tell which database I have to access, and so I set the appropriate timezone (using date_default_timezone_set()) in my beforeFind so that the query is sent with a date in the correct timezone.

My problem is then converting the dates in the afterFind to the timezone of the user. I'm passing this timezone through as a named parameter, and to access this in the model I'm using Configure::write() and Configure.read(). This works fine.
The problem is that it seems to be applying my timezone conversion multiple times. For example, if I'm querying the Australia/Sydney database from Australia/Perth the time should be two hours behind, but it's coming out as six hours behind. I tried echoing the times from my function before and after converting them, and each conversion was working correctly, but it was converting the times more than once, and I can't figure out why.

The methods I am currently using (in my AppModel) to convert from one timezone to another is as follows:

function afterFind($results, $primary){
    // Only bother converting if the local timezone is set.
    if(Configure::read('TIMEZONE'))
        $this->replaceDateRecursive($results);
    return $results;
}

function replaceDateRecursive(&$results){
    $local_timezone = Configure::read('TIMEZONE');

    foreach($results as $key => &$value){
        if(is_array($value)){
            $this->replaceDateRecursive($value);
        }
        else if(strtotime($value) !== false){
            $from_timezone = 'Europe/Berlin';
            if(/* using the Australia/Sydney database */)
                $from_timezone = 'Australia/Sydney';
            $value = $this->convertDate($value, $from_timezone, $local_timezone, 'Y-m-d H:i:s');
        }
    }
}

function convertDate($value, $from_timezone, $to_timezone, $format = 'Y-m-d H:i:s'){
    date_default_timezone_set($from_timezone);
    $value = date('Y-m-d H:i:s e', strtotime($value));
    date_default_timezone_set($to_timezone);
    $value = date($format, strtotime($value));

    return $value;                    
}

So does anyone have any ideas as to why the conversion is happening multiple times? Or does anyone have a better method for converting the dates? I'm obviously doing something wrong, I'm just stuck as to what that is.

like image 306
death_au Avatar asked Nov 05 '22 08:11

death_au


1 Answers

I worked out a solution. I didn't really understand what the $primary parameter in the afterFind was for until now. So to fix my code above, all I have to do is change the if in the afterFind to the following:

function afterFind($results, $primary){
    // Only bother converting if these are the primary results and the local timezone is set.
    if($primary && Configure::read('TIMEZONE'))
        $this->replaceDateRecursive($results);
    return $results;
}

As a side note, I am no longer using the date_default_timezone_set() function to do the timezone conversion, either. My convertDate function has changed as follows:

function convertDate($value, $from_timezone, $to_timezone, $format = 'Y-m-d H:i:s'){
    $dateTime = new DateTime($value, new DateTimeZone($from_timezone));
    $dateTime->setTimezone(new DateTimeZone($to_timezone));
    $value = $dateTime->format($format);

    return $value;                      
}
like image 122
death_au Avatar answered Nov 09 '22 04:11

death_au