Madivad
Madivad

Reputation: 3337

DateTime object and displaying date/time in alternate timezone

Every question I have read deals with converting a DateTime object from one timezone to another and the process is usually the same:

$date->setTimezone($newTimezone);
$date->format($format);
$date->setTimezone($oldTimezone);

or as a batch process, looping through the first two items as necessary, or changing date_default_timezone_set as necessary.

What I have is a widget that displays times of appointments in multiple timezones and I am finding that I have to either:

What I am trying to do is have ONE DateTime object set to the users timezone and never changing it, but displaying it relevant to the other timezones. I know this is not supported out-of-the-bag but how can I best achieve this?

I don't like managing multiple DateTime objects, it's simply not needed. What I would prefer is a method like:

DateTime::formatForTimezone($format, $timezone)

or

DateTime::inTimezone($timezone)

which I could then use:

$date->inTimezone($timezone)->format($format)

and $date's timezone would remain unchanged. I could just iterate through the required timezones and the original object would be preserved.

I did write a function to achieve this, but I had forgotten that all objects are passed by reference so that when my function returned the objects timezone had changed which I was required to revert and so the function generated more work (so I scrapped it). But it looked something like:

function DateInTimezone(DateTime $date, DateTimeZone $tz, $format){
    $backup = $date->getTimezone();
    $date->setTimezone($tz);
    $output = $date->format($format);
    $date->setTimezone($backup);
    return $output;
}

which I have just now made a class extends. Basically the same thing, just cutting out one variable.

class DateTimeForTimezone extends DateTime {
    public function formatForTimezone($format, DateTimeZone $tz){
        $backup = $this->getTimezone();
        $this->setTimezone($tz);
        $output = $this->format($format);
        $this->setTimezone($backup);
        return $output;
    }
}

I am the first to admit, I don't fully comprehend OOP, any pointers would be helpful

For example, this does what I'm after:

CODE:

echo '<pre>';

function p($arg){
    echo "$arg\n";
}
class DateTimeForTimezone extends DateTime {
    public function formatForTimezone($format, DateTimeZone $tz){
        $backup = $this->getTimezone();
        $this->setTimezone($tz);
        $output = $this->format($format);
        $this->setTimezone($backup);
        return $output;
    }
}
$f = "Y-m-d H:i:s A T O e";
$date = new DateTimeForTimezone('now',new DateTimeZone('UTC'));

$tzAUS = new DateTimeZone("Australia/Sydney");
$tzBKK = new DateTimeZone("Asia/Bangkok");
$tzLOS = new DateTimeZone("America/Los_Angeles");

// output ending with '*' should all be UTC
p($date->format($f."*"));
p($date->formatForTimezone($f,$tzLOS));
p($date->format($f."*"));
p($date->formatForTimezone($f,$tzAUS));
p($date->format($f."*"));
p($date->formatForTimezone($f,$tzBKK));
p($date->format($f."*"));

OUTPUT:

2017-09-29 23:37:20 PM UTC +0000 UTC*
2017-09-29 16:37:20 PM PDT -0700 America/Los_Angeles
2017-09-29 23:37:20 PM UTC +0000 UTC*
2017-09-30 09:37:20 AM AEST +1000 Australia/Sydney
2017-09-29 23:37:20 PM UTC +0000 UTC*
2017-09-30 06:37:20 AM +07 +0700 Asia/Bangkok
2017-09-29 23:37:20 PM UTC +0000 UTC*

I have achieved my goal but technically the timezone is changed, it's just reverted back.

Am I just looking at this wrong? Is there a better way to achieve what I'm after?

Upvotes: 1

Views: 96

Answers (1)

bishop
bishop

Reputation: 39434

You're doing it correctly. Zone isn't just about display, it's an integral part of a time's state. You can't work with a time value, like "2017-05-13T21:54:00", without knowing its zone. Forget calculations, forget comparisons, until you know the zone. And don't for a minute think that changing the time zone is trivial. It's notoriously complicated. So, keep on as you are.

If you want to loop this without managing objects, just do:

$date  = new DateTime('2017-05-13T21:54:00', new DateTimeZone('America/New_York'));
$zones = [
    new DateTimeZone('Antarctica/Casey'),
    new DateTimeZone('Europe/Helsinki'),
];
foreach ($zones as $zone) {
    echo (clone $date)->setTimezone($zone)->format('c') . PHP_EOL;
}

See this live on 3v4l.org.

Upvotes: 1

Related Questions