Richard Hamilton
Richard Hamilton

Reputation: 26434

Strange behavior when creating new dates in JavaScript

I was playing around with the Date object in JavaScript and found a very strange behavior.

If you create a new date with 1 parameter and call the getFullYear method, it will return 1 less than what you passed in

new Date("2014").getFullYear()
=> 2013

To test a theory, that it would return "2014-1-1", and in the previous timezone, the year before, I did another test.

new Date("2014").getDate()
=> 31

new Date("2014").getMonth()
=> 11

This would support the timezone theory, but it still doesn't explain the next two examples.

If you type in an alphanumeric string, you will get the correct year. It even works for gibberish like this

new Date("hsdkjfuweirsdf 2014").getFullYear();
=> 2014

For the first example, I assumed the function started counting at 0, so the year would be 1 less. However, that doesn't explain why my second function returned the correct year.

Edit:

I found another strange behavior with the new Date() constructor. If you pass in multiple parameters, it will return the correct year as well

new Date("2014", "9").getFullYear()
=> 2014

All of the above tests were done multiple times in Google Chrome.

Upvotes: 0

Views: 200

Answers (2)

Jonathan Lonowski
Jonathan Lonowski

Reputation: 123423

In the first case, the date string is being detected as partially following the one official Date Time String Format:

ECMAScript defines a string interchange format for date-times based upon a simplification of the ISO 8601 Extended Format. The format is as follows: YYYY-MM-DDTHH:mm:ss.sssZ

And, at least with the 5th edition of ECMAScript, that your browser is currently adhering to:

The value of an absent time zone offset is “Z”.

With this, the date string is assumed to be in UTC/GMT time for parsing.

Date.parse("2014") === Date.parse("2014-01-01T00:00:00Z");

Then, getFullYear() provides the year for the same "moment" in your local time zone, compared to its equivalent UTC method.

new Date("2014").getFullYear()    // 2013 (or 2014 for users in UTC+N timezones)
new Date("2014").getHours()       // (not 0, unless you live in UTC+0)

new Date("2014").getUTCFullYear() // 2014
new Date("2014").getUTCHours()    // 0

The 2nd example, by including the gibberish before the year, fails to match the above format, even partially.

Since that's the only format officially defined in the specification, the browser is now on its own to determine if and how it should parse the date string.

If the String does not conform to [the Date Time String Format] the function may fall back to any implementation-specific heuristics or implementation-specific date formats.

And, it chooses to use your local time zone by default and largely ignore the gibberish.

new Date("hsdkjfuweirsdf 2014").getFullYear() // 2014
new Date("hsdkjfuweirsdf 2014").getHours()    // 0

Note: The most recent, 6th edition of ECMAScript has changed the assumed value for date strings lacking timezones:

If the time zone offset is absent, the date-time is interpreted as a local time.

Upvotes: 2

Jeff Meatball Yang
Jeff Meatball Yang

Reputation: 39017

This is definitely an effect of Date parsing for UTC vs local time zone.

Generally, if you give any string that can be "fit" to ISO 8601 format the Date() constructor will try to parse you a GMT-located object. Otherwise it will give you your own local (with your browser's timezone info) object.

// how many minutes to add to get GMT?
var tzOffset = new Date().getTimezoneOffset()
// 300    // Eastern Standard Time, GMT-5

// will be parsed as GMT:
var gmt = new Date("2011");

// what year is it for me?
gmt.getFullYear()
// 2010

This is the local year when GMT experiences January 1, 2011. (It's still 2010 in NYC when London celebrates the New Year)

Notably, there are UTC functions:

// what year is it in London?
gmt.getUTCFullYear()
// 2011

Let's see what happens when we "add" hours so that our local time catches up:

// add tzOffset, voila, "correct" year
var localyear = new Date(gmt.getTime() + tzOffset*60*1000).getFullYear()
// 2011

Also, Chrome does a "best effort" to parse junk (non-standard) strings. This is browser-dependent behavior, and rely on it at your own risk. This explains the local-time-located behavior for strings such as "hsdkjfuweirsdf 2014".

Upvotes: 1

Related Questions