Madivad Madivad - 3 years ago 153
PHP Question

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:


  • create multiple
    DateTime
    objects and change each and every one of them when the due date arrives in the widget, or

  • use the one
    DateTime
    object but iterate through each timezone required, actively change it to each timezone, display it, rinse and repeat.



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?

Answer Source

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.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download