1

I'm writing an API that will retrieve readings from a sensor and return a list of times and values, offset using Javascript's `new Date(). (see below for the reason)

I've been able to get the time addition / subtraction working well enough, using $date->sub(DateInterval::createFromDateString($offset . " minutes")), but the time and date returned have an offset of +00:00 (e.g. 2016-02-26T13:32:28+00:00 instead of 2016-02-26T13:32:28+11:00 for Australia).

This causes issues for things like the Pebble, or AngularJS, which apply their own offsets on top of my own, after seeing that the offset is +00:00

How can I correctly set an offset when calling $date->format("c")? Should I compile the date myself (e.g. $date->format("Y-m-d\TH:i:s" . $plusOrMinus . $myoffset->format("h:i"))) or is there a better way to do it?

EDIT: Due to platform limitations (e.g. the Pebble smartwatch) I can't, or don't want to, use timezone names, as implementing a timezone menu in my watch app would either break UX or drive the filesize up if I add an offset to timezone table / library

Grayda
  • 1,870
  • 2
  • 25
  • 43

2 Answers2

1

I got this sorted in the end. This bit of info from the PHP manual tipped me off:

The $timezone parameter and the current timezone are ignored when the $time parameter either is a UNIX timestamp (e.g. @946684800) or specifies a timezone (e.g. 2010-01-28T15:00:00+02:00).

So my eventual solution was to extend the DateTime class and override the __construct() method. My modified class is as follows:

<?php

  class timezone extends DateTime {

    // Override our __construct method
    function __construct($date = "now", $offset = null) {
      // If we've not specified an offset
      if(is_null($offset)) {
          // Assume UTC
          $offsetFormat = "+00:00";
          $offset = 0;
      // Otherwise..
      } else {
        // Create a new DateTime, and get the difference between that, and another new DateTime that has $offset minutes subtracted from it. Format the results as something like +11:00 or -03:00
        $offsetFormat = (new DateTime($date, new DateTimeZone("UTC")))->diff((new DateTime($date, new DateTimeZone("UTC")))->sub(DateInterval::createFromDateString($offset . " minutes")))->format("%R%H:%I");
      }

      // Next, we get the offset from our $date. If this offset (divided by 60, as we're working in minutes, not in seconds) does NOT equal our offset
      if((new DateTime($date))->getOffset() / 60 !== $offset) {
        // Overwrite $date, and set it to a new DateTime with $offset minutes subtracted from it
        $date = (new DateTime($date, new DateTimeZone("UTC")))->sub(DateInterval::createFromDateString($offset . " minutes"));
      // If $date's offset equals $offset
      } else {
        // An offset has already been applied (we know this because all our pre-offset dates will be in UTC), and we don't need to do it again
        $date = (new DateTime($date));
      }

      // Finally, hand this back to the original DateTime class. This format works out to be something like: 2016-03-10T23:16:37+11:00
      parent::__construct($date->format("Y-m-d\TH:i:s") . $offsetFormat, null);
    }

  }


  echo (new timezone())->format("c") . "<br />"; // Will output something like 2016-03-10T12:17:44+00:00
  echo (new timezone(null, -660))->format("c") . "<br />"; // Will output something like 2016-03-10T23:17:44+11:00
  echo (new timezone("midnight", -660))->format("c") . "<br />"; // Will output 2016-03-10T11:00:00+11:00
  echo (new timezone("midnight"))->format("c") . "<br />"; // Will output 2016-03-10T00:00:00+00:00
  echo (new timezone("2016-01-01T00:00+00:00", -660))->format("c") . "<br />"; // Will output 2016-01-01T11:00:00+11:00
  echo (new timezone("2016-01-01T00:00+11:00", -660))->format("c") . "<br />"; // Will output 2016-01-01T11:00:00+11:00. Note that the offset isn't applied twice!

 ?>

EDIT: This is now a library that I've open-sourced. Check it out over on GitHub

Grayda
  • 1,870
  • 2
  • 25
  • 43
0

is this?

$date = new DateTime('now', new DateTimeZone('Australia/Adelaide'));
var_dump($date);
var_dump($date->format('c'));

the output is

object(DateTime)[2]
  public 'date' => string '2016-02-26 13:35:48.000000' (length=26)
  public 'timezone_type' => int 3
  public 'timezone' => string 'Australia/Adelaide' (length=18)
string '2016-02-26T13:35:48+10:30' (length=25)
  • I'm retrieving the offset from Javascript due to platform limitations (e.g. Pebble smartwatch, where asking for a timezone breaks UX), so the offset will be in minutes (e.g. Melbourne is 660 minutes ahead of UTC). EDIT: There is this option: http://stackoverflow.com/questions/7276304/php-setting-a-timezone-by-utc-offset but apparently it doesn't handle daylight savings very well – Grayda Feb 26 '16 at 03:18