38

Using javascript I know that my users timezone is UTC +3.

Now I want to create DateTime object with this knowledge:

$usersNow = new DateTime('now', new DateTimeZone("+3"));

I receive as a respsonse:

'Unknown or bad timezone (+2)'

What am I doing wrong? How can I fix?

10 Answers 10

47

how about this...

$original = new DateTime("now", new DateTimeZone('UTC'));
$timezoneName = timezone_name_from_abbr("", 3*3600, false);
$modified = $original->setTimezone(new DateTimezone($timezoneName));
Sign up to request clarification or add additional context in comments.

5 Comments

+1 just to make another +3^^ (just kidding, your answer is correct).
This is wrong, if PHP processes daylight savings.
Function timezone_name_from_abbr return false when called for +11 offset: timezone_name_from_abbr("", 11*3600, false);
This broke my production machine when passed Rio de Janeiro (-2*3600), I got Uncaught Exception: DateTimeZone::__construct(): Unknown or bad timezone. I'll paste my fix in an answer below.
It is worth noting that this will find the first matching location time zone for the offset (i.e. timezone_type = 3), whereas new DateTimeZone('+01:00') would create a time-based offset (i.e. timezone_type = 1). The former is affected by daylight savings, the latter is not. I am also unsure if this will work on systems without tzdata, but I think PHP has its own time zone database from perl.
18

You said:

Using javascript I know that my users timezone is UTC +3.

You probably ran something like this:

var offset = new Date().getTimezoneOffset();

This returns the current offset from UTC in minutes, with positive values falling west of UTC. It does not return a time zone!

A time zone is not an offset. A time zone has an offset. It can have multiple different offsets. Often there are two offsets, one for standard time and one for daylight saving time. A single numeric value cannot represent this alone.

  • Example of a time zone: "America/New_York"
    • Corresponding standard offset: UTC-5
    • Corresponding daylight offset: UTC-4

Besides the two offsets, also wrapped up in that time zone are the dates and times for transitioning between the two offsets so you know when they apply. There's also a historical record of how the offsets and transitions may have changed over time.

See also "Time Zone != Offset" in the timezone tag wiki.

In your example case, you probably received a value of -180 from javascript, representing a current offset of UTC+3. But that's just the offset for that particular point in time! If you follow minaz's answer, you will get a time zone that makes the assumption that UTC+3 is always the correct offset. That would work if the real time zone is something like "Africa/Nairobi" which has never used anything except UTC+3. But for all you know your user could be in "Europe/Istanbul", which uses UTC+3 in the summer and UTC+2 in the winter.

3 Comments

This answer deserves way more upvotes. Very detailed explanation of the challenge @shealtiel is facing.
@RobinvanBaalen I would agree, if a soltuion was proided. All I see here, is a (possible) explanation of why the user was having difficulty. Fine. But what to do about it?
As explained in the answer, use an actual time zone identifier, such as "America/New_York". If you need to get that from JS, you can now do that. (in 2014 when this answer was written, you could not.). See stackoverflow.com/a/34602679/634824
11

Modern answer:

$usersNow = new DateTime('now', new DateTimeZone('+0300'));

Documentation:

http://php.net/manual/en/datetimezone.construct.php

Comments

8

Since PHP 5.5.10, DateTimeZone accepts an offset like "+3" :

https://3v4l.org/NUGSv

Comments

2

This one takes Matthew's answer a step further to change the timezone of a date to any integer offset.

public static function applyHourOffset(DateTime $dateTime, int $hourOffset):DateTime
{
    $dateWithTimezone = clone $dateTime;

    $sign = $hourOffset < 0 ? '-' : '+';
    $timezone = new DateTimeZone($sign . abs($hourOffset));
    $dateWithTimezone->setTimezone($timezone);

    return $dateWithTimezone;
}

Note: I had a breakage in production due to the accepted answer.

Comments

2

Did you try using strtotime()?

 <?php
    echo strtotime("now"), "\n";
    echo strtotime("10 September 2000"), "\n";
    echo strtotime("+5 hours");
    echo strtotime("+1 day"), "\n";
    echo strtotime("+1 week"), "\n";
    echo strtotime("+1 week 2 days 4 hours 2 seconds"), "\n";
    echo strtotime("next Thursday"), "\n";
    echo strtotime("last Monday"), "\n";

1 Comment

this can work. I don't really need the timezones name, just to compute the correct time. So "+3 hours" will do the trick fine
1

As far as I can tell from the docs on DateTimeZone, you need to pass a valid time zone and here are the valid ones. Check the others, something there may help you.

Comments

1

DateTimeZone requires a timezone not an offest

3 Comments

And how can I set a timezone knowing only the utc offset?
@shealtiel you can't
This is how you can make the timezone if you have offset only. Your welcome: stackoverflow.com/a/74054485/6907703
1

For anyone who comes across this, I was facing the same problem, so in the end I extended the DateTime class and overrode the __construct() method to accept an offset (in minutes) instead of a timezone.

From there, my custom __construct() works out what the offset is in hours and minutes (e.g. -660 = +11:00) and then uses parent::__construct() to hand my date, custom formatted to include my offset, back to the original DateTime.

Because I'm always dealing with UTC times in my application, my class also modifies the UTC time by subtracting the offset, so passing Midnight UTC and an offset of -660 will show me 11am

My solution is detailed here: https://stackoverflow.com/a/35916440/2301484

Comments

0

I was directed to a solution thanks to Joey Rivera's link. Like what the others have said here, timezone is not an offset you do need a valid timezone.

This is what I am using for myself

$singapore_time = new DateTime("now", new DateTimeZone('Asia/Singapore'));


var_dump( $singapore_time );

I myself have found it to be much more convenient to work with YYYY-MM-DD HH:MM format. example.

$original = new DateTime("2017-05-29 13:14", new DateTimeZone('Asia/Singapore'));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.