JavaScript Time Zone is wrong for past Daylight Saving Time transition rules
It's actually specified behavior to use the current DST rules, and to ignore the ones in place at the particular date/time being examined. See ES5 15.9.1.8:
"The implementation of ECMAScript should not try to determine whether the exact time was subject to daylight saving time, but just whether daylight saving time would have been in effect if the current daylight saving time algorithm had been used at the time. This avoids complications such as taking into account the years that the locale observed daylight saving time year round."
The rules are: apply the current DST rules, to whatever time was specified. This results in arrant nonsense behavior, but it's what ECMAScript requires.
It's possible -- likely, even -- that this behavior will change in a future version of ECMAScript, to require actual DST rules at all points in time be used. This wasn't required initially because of the burden of shipping tzdata that it imposes on implementers. The language has become important enough, however, that probably everyone will just have to suck it up in the long run. But the change could be years away for all I know, so don't hold your breath on it.
Invalid times at the start of Daylight Saving Time
Good question! You are correct that this depends onwhat is the expected behavior according to the ECMAScript specification?
DaylightSavingTA
which is strictly undefined, however we can derive implications about DaylightSavingTA
from the way it works when used in the inverse function:For this to convert UTC t to a local time correctly, DaylightSavingTA(hour_at_beginning_of_dst) must be 1 hour and DaylightSavingTA(hour_after_end_of_dst) must be 0.LocalTime(t) = t + LocalTZA + DaylightSavingTA(t)
The same function is called in the UTC() function using a t
that in that function represents a DST-adjusted time. So in the non-existent-local-hour at the start of DST, DaylightSavingTA(the-dst-adjusted-time-which-is-one-hour-ahead) is 1 hour. Consequently:
Yes.Can new Date(2013, 9, 20) legitimately return October 19?
Indeed! Use UTC dates for that kind of calculation—I'm asking because I (and probably others) have written code like new Date(year, month, day) to construct a date without a time, and evidently this doesn't always work!
new Date(Date.UTC(2013, 9, 20))
and the getUTC*
methods. In general, it's best to stick to UTC for everything except the final user-facing presentation of times. Eliminating Javascript daylight saving time gap, a cross-browser solution
Using moment.js will save you lots of headache, and is the easiest way to achieve cross-browser compatibility for this sort of thing.
var m = moment.utc("3/22/2015","M/D/YYYY")
var s = m.format("YYYY-MM-DD HH:mm:ss")
Using UTC for this is important since you don't want to be affected by the user's time zone. Otherwise, if your date fell into a DST transition, it could be adjusted to some other value. (You're not really intersted in UTC, you're just using it for stability.)In response to this part of your updated question:
While it's now clear what you are asking for, you must understand it is not possible to achieve your exact requirements, at least not in a cross-browser, cross-region, cross-timezone manner.To simplify the question, I'm looking for a function like this:
function date_decomposition(d) {
...
}
console.log(date_decomposition(new Date("3/22/2015 00:00:00")));
=> [2015, 3, 22, 0, 0, 0]
Each browser has it's own way of implementing the string-to-date parsing. When you use either the constructor
new Date(string)
or theDate.parse(string)
method, you're invoking functionality that is implementation specific.There's a chart showing many of the formatting differences here.
Even if the implementation were consistent across all environments, you'd have regional formatting and time zone differences to contend with.
In the case of regional formatting issues, consider
01/02/2015
. Some regions usemm/dd/yyyy
ordering and will treat this as January 2nd, while other regions usedd/mm/yyyy
ordering and will treat this as February 1st. (Also, some parts of the world useyyyy/mm/dd
formatting.)Wikipedia has a list and map of where in the world different date formats are used.
In the case of time zones, consider that October 19th, 2014 at Midnight (00:00) in Brazil did not exist, and November 2nd, 2014 at Midnight (00:00) in Cuba existed twice.
The same thing happens on other dates and times in different time zones. From the information you provided, I can deduce that you are in Iran time zone, which uses UTC+03:30 during standard time, and UTC+04:30 during daylight time. Indeed, March 22, 2105 at Midnight (00:00) did not exist in Iran.
When you try to parse these invalid or ambiguous values, each browser has its own behavior, and there indeed differences between the browsers.
For invalid times, some browsers will jump forward an hour, while others will jump backwards an hour.
For ambiguous times, some browsers will assume you meant the first (daylight-time) instance, while others will assume you meant the second (standard-time) instance.
Date
object and deconstruct its parts, quite simply:function date_decomposition(d) {
return [d.getFullYear(), d.getMonth()+1, d.getDate(),
d.getHours(), d.getMinutes(), d.getSeconds()];
}
But this will always be based on the local time zone where the code is running. You see, inside the Date
object, there is just one value - a number representing the elapsed milliseconds since 1970-01-01T00:00:00Z
(without leap seconds being considered). That number is UTC-based.So, in recap, all of the issues you are having are related to the way the string was parsed into the Date
object to begin with. No amount of focusing on the output functions will help you to resolve that in a completely safe manner. Whether you use a library or write your own code, you'll need to obtain that original string of data to get the result you are looking for. By the time it's in a Date
object, you've lost the information you need to make this work.
By the way, you might consider watching my Pluralsight course, Date and Time Fundamentals, which covers much of this in even greater detail. Module 7 is entirely about JavaScript and these sorts of gotchas.
How to check if DST (Daylight Saving Time) is in effect, and if so, the offset?
This code uses the fact that getTimezoneOffset
returns a greater value during Standard Time versus Daylight Saving Time (DST). Thus it determines the expected output during Standard Time, and it compares whether the output of the given date the same (Standard) or less (DST).
Note that getTimezoneOffset
returns positive numbers of minutes for zones west of UTC, which are usually stated as negative hours (since they're "behind" UTC). For example, Los Angeles is UTC–8h Standard, UTC-7h DST. getTimezoneOffset
returns 480
(positive 480 minutes) in December (winter, Standard Time), rather than -480
. It returns negative numbers for the Eastern Hemisphere (such -600
for Sydney in winter, despite this being "ahead" (UTC+10h).
Date.prototype.stdTimezoneOffset = function () {
var jan = new Date(this.getFullYear(), 0, 1);
var jul = new Date(this.getFullYear(), 6, 1);
return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
}
Date.prototype.isDstObserved = function () {
return this.getTimezoneOffset() < this.stdTimezoneOffset();
}
var today = new Date();
if (today.isDstObserved()) {
alert ("Daylight saving time!");
}
How to ignore user's time zone and force Date() use specific time zone
A Date object's underlying value is actually in UTC. To prove this, notice that if you type new Date(0)
you'll see something like: Wed Dec 31 1969 16:00:00 GMT-0800 (PST)
. 0 is treated as 0 in GMT, but .toString()
method shows the local time.
Big note, UTC stands for Universal time code. The current time right now in 2 different places is the same UTC, but the output can be formatted differently.
What we need here is some formatting
var _date = new Date(1270544790922);
// outputs > "Tue Apr 06 2010 02:06:30 GMT-0700 (PDT)", for me
_date.toLocaleString('fi-FI', { timeZone: 'Europe/Helsinki' });
// outputs > "6.4.2010 klo 12.06.30"
_date.toLocaleString('en-US', { timeZone: 'Europe/Helsinki' });
// outputs > "4/6/2010, 12:06:30 PM"
This works but.... you can't really use any of the other date methods for your purposes since they describe the user's timezone. What you want is a date object that's related to the Helsinki timezone. Your options at this point are to use some 3rd party library (I recommend this), or hack-up the date object so you can use most of it's methods.Option 1 - a 3rd party like moment-timezone
moment(1270544790922).tz('Europe/Helsinki').format('YYYY-MM-DD HH:mm:ss')
// outputs > 2010-04-06 12:06:30
moment(1270544790922).tz('Europe/Helsinki').hour()
// outputs > 12
This looks a lot more elegant than what we're about to do next.Option 2 - Hack up the date object
var currentHelsinkiHoursOffset = 2; // sometimes it is 3
var date = new Date(1270544790922);
var helsenkiOffset = currentHelsinkiHoursOffset*60*60000;
var userOffset = _date.getTimezoneOffset()*60000; // [min*60000 = ms]
var helsenkiTime = new Date(date.getTime()+ helsenkiOffset + userOffset);
// Outputs > Tue Apr 06 2010 12:06:30 GMT-0700 (PDT)
It still thinks it's GMT-0700 (PDT), but if you don't stare too hard you may be able to mistake that for a date object that's useful for your purposes.I conveniently skipped a part. You need to be able to define currentHelsinkiOffset
. If you can use date.getTimezoneOffset()
on the server side, or just use some if statements to describe when the time zone changes will occur, that should solve your problem.
Conclusion - I think especially for this purpose you should use a date library like moment-timezone.
How to convert time correctly across timezones?
10:00-8 and 10:00-7 are two different moments in time. They are equal to 18:00Z and 17:00Z respectively (Z = UTC). When you are measuring in terms of an offset, daylight saving time does not enter the picture. Ever.Is there a way, in javascript, without external web services, to do a correct conversion? That is, to detect that 10am UTC-08:00 should actually be 10am UTC-07:00, since it is Daylight Saving.
In general, people just think in "Pacific Time", and that means both PST in the winter, and PDT in the summer. But computers are more precise. When you see PST, it means UTC-8. When you see PDT, it means UTC-7. It would be invalid to label using one form while simultaneously referring to the offset of the other.I assume that since the standard timezone in CA is PST, people don't switch to thinking in PDT in summer time. Or do they?!
Time zone abbreviations can be ambiguous. Ideally, when referencing the zone programmatically, you should use the IANA zone name, such as America/Los_Angeles
. However, this is not currently possible in all JavaScript runtimes without a library. (They are working on this though.)
Correct. They could be either 8, 9, or 10 hours apart. They switch at completely different times though, so don't try to manage this yourself.In central Europe, standard date is UTC+01:00, Daylight Saving date is UTC+02:00. So that difference between CA and Europe should be 9 hours, except for two periods in a year, when one or the other area switches between Standard and Daylight Saving modes.
Moment-timezone is a great library. However, from the scenario you described, I don't think you need to worry about time zone conversion as much as you are thinking. See if you can follow this logic:So far, it looks like the moment.js/timezone plugin, suggested by Guido Preite is capable of doing this (more or less).
- The user in California enters a date and time into a textbox.
You read that textbox value into a string, and parse it into a date:
or using moment.js:var dt = new Date("8/15/2013 10:00");
var m = moment("8/15/2013 10:00", "M/D/YYYY HH:mm");
Because this is being done on the user's computer, JavaScript will automatically assume that this is a local date and time. You do not need to provide any offset or time zone information.
This does mean that because of the DST transitions that the time entered might be invalid or ambiguous. JavaScript doesn't do such a great job at handling that, in fact - you will get different results on different browsers. If you want to be unambiguous, then you would provide an offset.
// PST
var dt = new Date("3/11/2013 1:00 UTC-08:00");
// PDT
var dt = new Date("3/11/2013 1:00 UTC-07:00");Once you have a
Date
(or amoment
), then you can evaluate its UTC equivalent:
it's the same with moment.js, but you will have better browser support:var s = dt.toISOString(); // 2013-08-15T17:00:00Z
var s = m.toISOString(); // 2013-08-15T17:00:00Z
You store that UTC value in your database.
The other user in Central Europe comes along and loads the data.
You feed it in to a
Date
ormoment
in JavaScript:
or with moment.js (again, better browser support)var dt = new Date("2013-08-15T17:00:00Z");
var m = moment("2013-08-15T17:00:00Z")
Because JavaScript knows the time zone rules of the local computer, you can now display this date and it will be presented with the Central Europe time zone:
or with moment.js, you can control the output format bettervar s = dt.ToString(); // browser specific output
// ex: "Thu Aug 15 2013 19:00:00 GMT+0200 (Central Europe Daylight Time)"
you could also let moment.js decide what localized format should be output:var s = m.format("DD/MM/YYYY HH:mm"); // "15/08/2013 19:00"
var s = m.format("llll"); // "Thu, 15 Aug 2013 19:00"
Date
. Moment.js will make things easier for parsing and formatting, but it isn't absolutely required.There are only a few scenarios that require a time zone library (such as moment-timezone or others).
You want to convert to or from a zone that is not the local time zone or UTC.
You are working with dates that are in the past, and there has been a change to the time zone rules or daylight saving time rules since then, and you have dates that would be interpreted differently under the new rules than with the old ones. This is a bit technical, but it does happen. Read more here and here.
Related Topics
Angular 2 Karma Test 'Component-Name' Is Not a Known Element
Quickest Way to Pass Data to a Popup Window I Created Using Window.Open()
How to Achieve Dynamic Scoping in JavaScript Without Resorting to Eval
Why Are Exceptions Used for Rejecting Promises in Js
JavaScript When to Use Prototypes
Execute an Exe File Using Node.Js
Subject VS Behaviorsubject VS Replaysubject in Angular
How to Sum the Values of a JavaScript Object
JavaScript Contenteditable - Set Cursor/Caret to Index
How to Take Advantage of Callback Functions for Asynchronous Xmlhttprequest
How to Reload/Refresh Jquery Datatable
Mocking a Useragent in JavaScript
Access Non-Numeric Object Properties by Index
Why Do I Need to Copy an Array to Use a Method on It
JavaScript Toisostring() Ignores Timezone Offset
JavaScript .Replace Only Replaces First Match