How to correctly handle “/Date(…-0700)/” date form

2019-07-23 17:41发布

问题:

I'm using the Bing Routes API, and it's returning dates in this kind of format:

/Date(1538245980000-0700)/

It looks like Unix timestamp in milliseconds, followed by a timezone. The Moment docs claim to be able to handle these correctly, but they also say that

Unix timestamps and Date objects refer to specific points in time, thus it doesn't make sense to use the time zone offset when constructing.

Based on other context (it's the time a bus leaves Stawell, a few hours from Melbourne, on Saturday morning), I'm pretty sure that the above time should be 11:33AM in Australia/Melbourne AEST (+10:00).

But using moment.timezone:

console.log(moment.parseZone('/Date(1538245980000-0700)/').format('h:mma Z'));

"11:33am -07:00"

That seems really wrong: this is referring to a moment in time which is:

  • 11:33 AM Pacific Time,
  • 6:33 PM UTC
  • 4:33 AM Melbourne time

Is Bing wrong? Why is this timezone included?

Or am I using Moment wrong? In which case, what is the correct way to convert this format into the correct time with my local timezone?

回答1:

This is the antiquated "ASP.NET JSON Date Format", produced by the JavaScriptSerializer class (and others). Scott Hanselman did a blog post back in 2012 on it, as have others. (Generally, modern systems should prefer the ISO 8601 / RFC 3339 format instead.)

The ASP.NET JSON date format has two parts:

  • The first part is always the Unix timestamp in milliseconds. In other words, the number of milliseconds elapsed since 1970-01-01 00:00:00.000 UTC, not considering leap seconds. It doesn't adjust for time zone - it's always UTC based.

  • The second part is optional, and often ommited. When present, it reflects a time zone offset that should be meaningful to the receiver. However, it is usually generated by serializing a DateTime in .NET whose .Kind property is DateTimeKind.Local, in which case it will reflect the time zone offset for that date based on the server's local time zone. Often that time zone is irrelavent. Note that unlike ISO 8601, the base value is not adjusted to reflect this offset. In other words, the offset is extraneous.

Thus, the timestamp you gave indeed represents the values you listed. (Though note that Melbourne is UTC+10 at this time, not UTC+11.)

With regard to Moment, keep in mind that parseZone is intended to set the time zone offset of the moment object to the one provided in the input (-0700 in this case). If you don't care about that offset, then you can use any of the other parsing functions:

moment.parseZone('/Date(1538245980000-0700)/').format()
//=> "2018-09-29T11:33:00-07:00"  (always)

moment('/Date(1538245980000-0700)/').format()
//=> "2018-09-29T14:33:00-04:00"  (example based on the local time zone being US Eastern time)

moment.utc('/Date(1538245980000-0700)/').format()
//-> "2018-09-29T18:33:00Z"       (always, since parsing as UTC)

moment.tz('/Date(1538245980000-0700)/', 'Australia/Melbourne').format()
//=> "2018-09-30T04:33:00+10:00"  (always - since time zone provided)

In all the above examples, only in the first one was the -0700 used at all.

So - if you expected the value to represent the time in Melbourne, then yes - Bing is wrong. Perhaps there is some other setting or aspect to the data to consider. Or perhaps it's a bug and if so you should report it as such.

If so, and you need to compensate, then do this:

moment.parseZone('/Date(1538245980000-0700)/').tz('Australia/Melbourne', true).format()
//=> "2018-09-29T11:33:00+10:00"