moment.js timezones .valueOf() not returning expected values

13,881

Solution 1

Moment is calculating this correctly.

Tue, 21 Apr 2015 20:06:06 GMT, Tue, 21 Apr 2015 21:06:06 BST, and Tue, 21 Apr 2015 16:06:06 EDT all refer to the same time and will all have the same unix timestamp. When you call .tz() you are just changing how that time will be formatted. You aren't changing the actual time.

Note: To get the unix time stamp you can use .unix() e.g.

moment(tick.TimeStamp).unix()

Or this will return the same value

moment(tick.TimeStamp).tz("Europe/London").unix()

Solution 2

I've updated a JS fiddle to provide a sample. http://jsfiddle.net/x0z90vqg/ (Updated fiddle showing type property on xAxis if not using HighStock)

I believe your issue is that you're not using the Highcharts global object's properties useUTC and timezoneOffset properties appropriately. Using the highcharts-ng control masks some of the capabilities of the Highcharts library, but you're still able to access the features you need pretty easily.

The relevant piece of the fiddle is:

Highcharts.setOptions({
    global : {
        useUTC : false,
        timezoneOffset: -5
    }
});
$scope.chartConfig.getHighcharts().redraw();

The above example sets the Highcharts global object to not use UTC for the date/time series and set's the offset to -5 hours (you can obtain the needed offset using the moment.js like you already are), and then telling the chart to redraw through the highcharts-ng's exposed getHighcharts() method. That method returns the actual chart object and from there it's like you're using highcharts directly and not through any intermediary component.

Edit

@Matt brought up a very good point. Setting the timezoneOffset like this isn't quite the same thing as setting a true timezone. A true timezone would take into account DST changes and such, this is just a static offset from UTC. Setting the UTC offset like this also affects the entire graph, not just one series. If you require displaying (and comparing) two or more series on the same graph in different timezones, and displaying that data as their respective timezones, you can enable multiple X-axis and in the format label logic for each axis, take the X value for the tick and convert it via javascript function into the timezone'd value and label you want to display. This should result in two X-axis with labels in two different timezones, but the data in the central part of the graph running off the same UTC scale. If doing this, you would likely also want to override the formatter for the tool tip popup as well so that you can convert the value displayed in the tool tip to show the timezone'd value for each point if you didn't want it to display UTC.

All of this still doesn't solve the problem of displaying a time series of data that crosses over the point where DST switches. I don't believe Highcharts has any way of representing that, and I'm not aware of another charting library that does either. It seems like it would be a fairly common problem though, so I'm sure it's been solved somewhere...

Share:
13,881
Corey
Author by

Corey

Updated on June 04, 2022

Comments

  • Corey
    Corey almost 2 years

    I have an angular application using highcharts-ng to make a line graph. The y-axis is numbers and the x-axis is datetime's.

    I am trying to properly account for Daylight Savings Time changes when converting between the two timezones of "America/New_York" and "Europe/London" using moment.js.

    London is currently in BST (as of the time of posting this), so it is +1:00.

    tick.TimeStamp
    > "2015-04-21T16:06:06.0786392-04:00"
    

    tick.TimeStamp is my "America/New_York" time (currently in EDT). I convert that to London time using...

    moment(tick.TimeStamp).tz("Europe/London").format()
    > "2015-04-21T21:06:06+01:00"
    

    I need my result in Unix Epoch ticks to plot them for the x-axis in highcharts-ng, so I use...

    var d = moment(tick.TimeStamp).tz("Europe/London").format()
    moment(d).valueOf()
    

    which yields

    1429646766000

    The issue is that this tick value result as a datetime is

    Tue, 21 Apr 2015 20:06:06 GMT

    where it should be

    Tue, 21 Apr 2015 21:06:06 GMT

    since London is currently in BST +1:00

    Am I doing something wrong, or is moment just calculating this incorrectly?

    Any help would be greatly appreciated. Thank you!

    EDIT: I should mention that my moment-timezones.js is the most recent from their site with all of the timezone info.

  • Matt Johnson-Pint
    Matt Johnson-Pint almost 9 years
    +1. Also, moment(tick.TimeStamp).valueOf() would work if he needs the timestamp in milliseconds instead of seconds. You're correct that tz is redundant and unnecessary for this purpose.
  • Matt Johnson-Pint
    Matt Johnson-Pint almost 9 years
    That's interesting, except it would mean that the offset would need to be stable over the range covered by the chart. See "Time Zone != Offset" in the timezone tag wiki. If the offset is stable, then that would work fine. Otherwise, converting to UTC is probably a good idea.
  • sohjsolwin
    sohjsolwin almost 9 years
    @Matt, very valid point. Highcharts, in my experience, doesn't handle (or even seem to know about) legitimate time zones. The closest functionality seems to be the offset. Using a library like moment.js to find the appropriate offset from UTC for the timezone you want to display on the graph and placing it in the offset, either prior to rendering the graph or dynamically like above, seems to be the only way to make it show a non-UTC, non-current locale timezone. It does affect the entire graph, like you mentioned, so all data should be UTC'd first for proper comparison.
  • Corey
    Corey almost 9 years
    @sohjsolwin - This looks promising, but the issue here is that I'm not using highstocks, so as soon as you set "useHighStocks: false" this method no longer works.
  • sohjsolwin
    sohjsolwin almost 9 years
    @Corey That's due to some defaults that Highstocks sets automatically. The xAxis being a datetime type is one of them. You just need this statement to tell it that the x-point data is meant to be times and not numbers. I'll update the example with the changes too. xAxis: { type:'datetime' }
  • Corey
    Corey almost 9 years
    @rob - thanks for the reply, it was helpful. I did find a solution, you can view my comment below. Thanks!