I use this method using js on the frontend to get the utc offset in minutes. It gives me a result like -300 for UTC+500
const d = new Date(); let diff = d.getTimezoneOffset();
Now how do I make the date in php using this offset? More specifically, how do I convert this offset to something acceptable by a solution like this:
$usersNow = new DateTime('now', new DateTimeZone('+0300'));
Do I have to manually get my offset to a string like this?
Advertisement
Answer
The DateTimeZone
class is a wrapper around functions in a library by Derick Rethans called “timelib”, so we can check the source code to confirm our options.
The constructor first calls timelib_parse_zone which recognises “GMT+”, “GMT-“, “+”, and “-” as prefixes for offsets, and passes the remaining string to timelib_parse_tz_cor.
As pointed out in a comment by Paolo, the JS Date.getTimezoneOffset function returns its offset with the opposite sign from most other notations: -300 represents an offset of 300 minutes ahead of UTC. We can therefore begin our string with “+” for a negative input, and “-” for a positive input, then format the rest of the value ignoring sign.
That function handles the following formats, based on the length of the string:
- length 1: /* H */
- length 2: /* HH */
- length 3: /* H:M */
- length 4: /* H:MM, HH:M, HHMM */
- length 5: /* HH:MM */
- length 6: /* HHMMSS */
- length 8: /* HH:MM:SS */
If you only care about whole-hour offsets, the simplest format would be to include only the hours (the “H” and “HH” cases). However, there are no formats that only have minutes.
To cover all timezones, you’ll need one of the formats that includes both hours and minutes.
Looking at the code, a value of “+123” will be interpreted the same as “+01:23”, but it could theoretically mean “+12:03” instead, so it’s best to avoid that ambiguity. We can either insert zeroes to make a fixed length format like “HHMM”, or we can include the ‘:’ separator, which allows one or two digits on either side.
A verbose implementation could therefore look like this:
// Sign is mandatory in the string; it's the opposite of what JS gave us $sign = ($jsOffset < 0) ? '+' : '-'; // Discard sign from offset for remaining calculations $absOffsetMins = abs($jsOffset); // Hours is offset divided by 60, discarding fractions $offsetHourPart = intdiv($absOffsetMins, 60); // Minutes is the remainder of that division: offset modulo 60 $offsetMinPart = $absOffsetMins % 60; // Put it together; depending on value, result will be in format // +H:M, +H:MM, +HH:M, or +HH:MM, all of which are supported $timezoneString = $sign . $offsetHourPart . ':' . $offsetMinPart; // Make the object $timezone = new DateTimeZone($timezoneString);