Corey
Corey

Reputation: 21

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

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.

Upvotes: 2

Views: 14862

Answers (3)

Corey
Corey

Reputation: 21

just wanted to post a quick update of what I figured out. Since I ran into lots of quirks trying to do this on the client side, I found a nice way to handle this on the server side in my Controller code (.NET). Instead of just returning the timestamp (tick.TimeStamp), I now return EasternTimeStamp and LondonTimeStamp. I was able to accomplish this using a nice method off of the TimeZoneInfo class.

    /// <summary>
    /// Converts the time to eastern standard time.
    /// This should properly account for DST, putting the time in EST (-5:00) or EDT (-4:00)
    /// </summary>
    public static DateTime ConvertTimeToEasternStandardTime(DateTime inputDateTime)
    {
        // US eastern timezone=Eastern Standard Time
        string targetTimeZoneId = "Eastern Standard Time";
        DateTime outputDateTime = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(inputDateTime, targetTimeZoneId);
        return outputDateTime;
    }

    /// <summary>
    /// Converts the time to GMT standard time.
    /// This should properly account for DST, putting the time in BST (+1:00) or GMT (+0:00)
    /// </summary>
    public static DateTime ConvertTimeToGMTStandardTime(DateTime inputDateTime)
    {
        // London timezone=GMT Standard Time
        string targetTimeZoneId = "GMT Standard Time";
        DateTime outputDateTime = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(inputDateTime, targetTimeZoneId);
        return outputDateTime;
    }

Hopefully anyone who runs into this issue can find this useful. I've found it to be quite stressful trying to find a nice way to deal with DST and timezones like this over the past week.

Upvotes: 0

sohjsolwin
sohjsolwin

Reputation: 271

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...

Upvotes: 2

rob
rob

Reputation: 18513

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()

Upvotes: 4

Related Questions