Eliminating Javascript daylight saving time gap, a cross-browser solution - javascript

After lots of hassle I finally found the actual problem. It's the gap induced by daylight saving and the fact that different browsers act differently if timezone is set on UTC+3:30 (I'm not sure of other timezones).
Here's a snippet to generate the problem (the problem is reproducible if your system's TZ is set to UTC+3:30):
function zeroPad(n) {
n = n + '';
return n.length >= 2 ? n : new Array(2 - n.length + 1).join('0') + n;
}
document.write("<table border='1' cellpadding='3'><tr><td>Input date</td><td>Parsed timestamp</td><td>Output date</td></tr>");
var m = 22 * 60;
for (var i=0; i<8; i++) {
var input = "3/21/2015 " + zeroPad(Math.floor(m / 60)) + ":" + zeroPad(m % 60) + ":00";
var d = new Date(input);
var output = d.getFullYear()
+'-'+zeroPad(d.getMonth()+1)
+'-'+zeroPad(d.getDate())
+' '+zeroPad(d.getHours())
+':'+zeroPad(d.getMinutes())
+':'+zeroPad(d.getSeconds());
document.write("<tr><td>" + input + "</td><td>" + d.getTime() + "</td><td>" + output + "</td></tr>");
m = m + 15;
}
m = 0;
for (var i=0; i<7; i++) {
var input = "3/22/2015 " + zeroPad(Math.floor(m / 60)) + ":" + zeroPad(m % 60) + ":00";
var d = new Date(input);
var output = d.getFullYear()
+'-'+zeroPad(d.getMonth()+1)
+'-'+zeroPad(d.getDate())
+' '+zeroPad(d.getHours())
+':'+zeroPad(d.getMinutes())
+':'+zeroPad(d.getSeconds());
document.write("<tr><td>" + input + "</td><td>" + d.getTime() + "</td><td>" + output + "</td></tr>");
m = m + 15;
}
document.write("</table>");
I've run it on Firefox and Chromium and here are what they say:
The parts within the red boxes are the range of times in which the gap happens. My problem is that components like calendars usually depend on date objects with time part set to "00:00:00" and they've got a loop generating new dates by adding a day worth of timestamp to the previous date. So once an object falls into 3/22/2015 00:00:00 it will be considered 3/22/2015 01:00:00 or 21/3/2015 23:00:00 (depending on the browser) and hence the generated dates will be invalid from that point of time forth!
The question is how to detect such date objects and how to treat them?

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:
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]
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.
Each browser has it's own way of implementing the string-to-date parsing. When you use either the constructor new Date(string) or the Date.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 use mm/dd/yyyy ordering and will treat this as January 2nd, while other regions use dd/mm/yyyy ordering and will treat this as February 1st. (Also, some parts of the world use yyyy/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.
Now with all of that said, you can certainly take a 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.

JavaScript dates are parsed in the local time of the browser, unless you use the ISO date format "2015-03-22", which will be parsed as UTC. In your case, since you control the server, and you know the dates are actually UTC, you could leave the format the way it is and parse the date, then subtract the timezone offset value from the date to convert it to UTC. The following function should return exactly the results you requested in your last use case. You could obviously modify this to format the string in any way you see fit.:
function date_decomposition(dateString) {
var local = new Date(dateString);
var d = new Date(local.valueOf() - (local.getTimezoneOffset() * 60000));
return [ d.getUTCFullYear(),
(d.getUTCMonth() +1 ),
d.getUTCDate(),
d.getUTCHours(),
d.getUTCMinutes(),
d.getUTCSeconds() ];
}
The only gotcha about this approach is that if you change your date format on the server to use the ISO 8601 date format, it will still subtract the timezone offset and you'll end up with the wrong output.
To be 100% safe, you should format the date string using the ISO 8601 date format, like "2015-03-22" or "2015-03-22T00:00:00Z". Then you could delete the line that subtracts the timezone offset from the parsed date and it'll work correctly with any ISO date string:
function date_decomposition(dateString) {
var d = new Date(dateString);
return [ d.getUTCFullYear(),
(d.getUTCMonth() +1 ),
d.getUTCDate(),
d.getUTCHours(),
d.getUTCMinutes(),
d.getUTCSeconds() ];
}

Date Parse (1st answer before modified question)
This would parse input and isolate each value.
function dateDecomposition(d) {
return [ d.getFullYear(),d.getMonth()+1,d.getDate(),
d.getHours(),d.getMinutes(),d.getSeconds() ];
};
function dateUTCDecomposition(d) {
return [ d.getUTCFullYear(),d.getUTCMonth()+1,d.getUTCDate(),
d.getUTCHours(),d.getUTCMinutes(),d.getUTCSeconds() ];
};
function pdte() {
var ndte=new Date(Date.parse(document.getElementById('datein').value));
var ar=dateDecomposition(ndte);
document.getElementById('yyyy').innerHTML=ar[0];
document.getElementById('mm').innerHTML=ar[1];
document.getElementById('dd').innerHTML=ar[2];
document.getElementById('HH').innerHTML=ar[3];
document.getElementById('MN').innerHTML=ar[4];
document.getElementById('SS').innerHTML=ar[5];
ar=dateUTCDecomposition(ndte);
document.getElementById('Uyyyy').innerHTML=ar[0];
document.getElementById('Umm').innerHTML=ar[1];
document.getElementById('Udd').innerHTML=ar[2];
document.getElementById('UHH').innerHTML=ar[3];
document.getElementById('UMN').innerHTML=ar[4];
document.getElementById('USS').innerHTML=ar[5];
document.getElementById('test').innerHTML='';
for (var i=1426896000000;i<1427068800000;i+=1800000) {
ar=dateUTCDecomposition(new Date(i));
var cmp=Date.parse(ar[0]+"/"+ar[1]+"/"+ar[2]+" "+ar[3]+":"+ar[4]+":"+ar[5]);
var re=dateDecomposition(new Date(cmp));
var fail=0;
for (var l=0;l<6;l++) { if (ar[l]!==re[l]) fail=1 };
document.getElementById('test').innerHTML+=fail+" -- "+ar.join(":")+'<br />';
};
};
document.getElementById('datein').addEventListener('change',pdte,true);
window.onload=pdte
<input id="datein" value="2015/03/14 12:34:56" />
<table border=1>
<tr><td>Locale</td>
<td id="yyyy"></td>
<td id="mm"></td>
<td id="dd"></td>
<td id="HH"></td>
<td id="MN"></td>
<td id="SS"></td>
</tr><tr><td>UTC</td>
<td id="Uyyyy"></td>
<td id="Umm"></td>
<td id="Udd"></td>
<td id="UHH"></td>
<td id="UMN"></td>
<td id="USS"></td>
</tr>
</table>
<div id="test"></div>
Testing rule #1
Searching for gaps from year 2000 to year 2015, by 1/2hour steps
function dateDecomposition(d) {
return [ d.getFullYear(),d.getMonth()+1,d.getDate(),
d.getHours(),d.getMinutes(),d.getSeconds() ];
};
function dateUTCDecomposition(d) {
return [ d.getUTCFullYear(),d.getUTCMonth()+1,
d.getUTCDate(), d.getUTCHours(),
d.getUTCMinutes(),d.getUTCSeconds() ];
};
for (var i=946684800000;i<1420070400000;i+=1800000) {
ar=dateUTCDecomposition(new Date(i));
var cmp=Date.parse(
ar[0]+"/"+ar[1]+"/"+ar[2]+" "+
ar[3]+":"+ar[4]+":"+ar[5]);
var re=dateDecomposition(new Date(cmp));
var fail=0;
for (var l=0;l<6;l++) { if (ar[l]!==re[l]) fail=1 };
if (fail!==0) {
document.getElementById('test').innerHTML+=fail+
" -- "+new Date(i)+" -- "+ar.join(":")+'<br />';
}
}
div#test {
font-family: mono, monospace, terminal;
font-size: .8em;
}
<div id="test"> </div>
At all, this seem not to be a javascript bug, but I found this about ExtJs:
Ext.Date.parse - problem when parsing dates near daylight time change.
Second testing rule
As this question was modified several times, there is a test rule working on last 10 years, by 15 minutes steps, for showing gaps AND overlaps:
function dateDecomposition(d) {
return [ d.getFullYear(),d.getMonth()+1,d.getDate(),
d.getHours(),d.getMinutes(),d.getSeconds() ];
};
function dateUTCDecomposition(d) {
return [ d.getUTCFullYear(),d.getUTCMonth()+1,
d.getUTCDate(), d.getUTCHours(),
d.getUTCMinutes(),d.getUTCSeconds() ];
};
var end=(Date.now()/900000).toFixed(0)*900000;
var start=end-365250*86400*10;
for (var i=start;i<end;i+=900000) {
ar=dateUTCDecomposition(new Date(i));
var cmp=Date.parse(
ar[0]+"/"+ar[1]+"/"+ar[2]+" "+
ar[3]+":"+ar[4]+":"+ar[5]);
var re=dateDecomposition(new Date(cmp));
var fail=0;
for (var l=0;l<6;l++) { if (ar[l]!==re[l]) fail++ };
if (fail!==0) {
document.getElementById('test').innerHTML+=
fail+" -- "+new Date(i)+" -- "+ar.join(":")+'<br />';
} else {
ar=dateDecomposition(new Date(i));
var cmp=Date.parse(
ar[0]+"/"+ar[1]+"/"+ar[2]+" "+
ar[3]+":"+ar[4]+":"+ar[5]);
if (cmp != i) {
document.getElementById('test').innerHTML+=
fail+" -- "+new Date(i)+" -- "+ar.join(":")+'<br />';
}
}
}
div#test {
font-family: mono, monospace, terminal;
font-size: .8em;
}
<div id="test"> </div>
You could test this by running your prefered browser under some differents timezones:
env TZ=Iran firefox http://stackoverflow.com/a/29048205/1765658
env TZ=Iran opera http://stackoverflow.com/a/29048205/1765658
env TZ=Iran chrome http://stackoverflow.com/a/29048205/1765658
env TZ=Iran chromium http://stackoverflow.com/a/29048205/1765658
env TZ=Europe/Berlin firefox http://stackoverflow.com/a/29048205/1765658
env TZ=Europe/Berlin opera http://stackoverflow.com/a/29048205/1765658
env TZ=Europe/Berlin chrome http://stackoverflow.com/a/29048205/1765658
env TZ=US/Central firefox http://stackoverflow.com/a/29048205/1765658
env TZ=US/Central opera http://stackoverflow.com/a/29048205/1765658
env TZ=US/Central chrome http://stackoverflow.com/a/29048205/1765658
env TZ=Asia/Gaza firefox http://stackoverflow.com/a/29048205/1765658
env TZ=Asia/Gaza opera http://stackoverflow.com/a/29048205/1765658
env TZ=Asia/Gaza chromium http://stackoverflow.com/a/29048205/1765658
I hope that you will be able to create your own testing rules for your ExtJS extensions, taking ideas from this last snippet.
Screenshoots
TZ=Iran Chromium
TZ=Iran Iceweasel (firefox)
TZ=US/Central Chromium
TZ=US/Central Iceweasel
TZ=Asia/Gaza Chromium
TZ=Asia/Gaza Iceweasel

You have to use the variable length argument constructor.
function convert(dateStr) {
var date = dateStr.split('/');
return new Date(date[2], (date[0] | 0) - 1, date[1]);
}
function formatDate(d) {
function pad(i) {
return i < 10 ? '0' + i : i;
}
return d.getFullYear()
+ '-' + pad(d.getMonth() + 1)
+ '-' + pad(d.getDate())
+ ' ' + pad(d.getHours())
+ ':' + pad(d.getMinutes())
+ ':' + pad(d.getSeconds())
}
var str = formatDate(convert('3/22/2015'));
document.write(str);

The problem is more on the approach. The browsers are behaving exactly as expected because they are both aware of the Upcoming Daylight Saving Time Clock Changes. So, your Chromium browser knows that at 12:00 Midnight local time in march 22, needs to move the clock forward to to 1:00 AM and your Firefox Browser knows that at Midnight Sat-Sun local time in march 22, needs to move the clock back to to Sat 11:00 PM.
There is a reason for the different time zones to be defined based on UTC. UTC is a standard, not a timezone. If you want to be cross browser you need to live by standards. Stick to UTC, store in UTC and do all date calculations in UTC. If you have a calendar component tell your component you are passing the dates in UTC. When you need to display the date to the user (thus browser dependent) you (if you are not using a component) or your calendar component are the ones responsible to check if daylight savings time is in effect, and adjust the date to be displayed accordingly.
¿How do you know if daylight savings time is in effect?
Check this article about adding methods to the date object to do exactly that. The logic behind the code is that the timezone difference between UTC and Local Time are different if daylight saving time is in effect or not. If you just want to check the code here are the two functions:
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.dst = function() {
return this.getTimezoneOffset() < this.stdTimezoneOffset();
}

I got the solution of your problem
It will surly help you
First of all I would like to explain daylight time.
Daylight time: During the summer days are longer; the sun rises earlier and sets later. Congress decided several decades ago that it would be better to have more daylight in the evening than in the morning, so they came up with daylight savings time. The clocks are shifted ahead one hour in the spring and back one hour in the fall. That way, you don't have sunrise at 4:00 a.m. and sunset at 8:00 p.m., but sunrise at 5:00 a.m. and sunset at 9:00 p.m., in the middle of summer. They figured people would use less electricity to light their houses in the evenings, and so forth.
So now, most of the country uses daylight savings time in the summer, and standard time in the winter. yahoo link
Firstly I would like to Give you a solution of your problem:
Set you computer's (Tested on win 7) time zone to GMT+3:30 and change your date to 23-march-2015.
and run your code on both browsers (chromium/mozila).
you will find the same result (i.e. +1hr to your input time)
yepppii the problem is solved.
you will get +1hr added in your input time, because after March,22 2015 at 12:00 AM (for GMT +3:30) day light Saving time begins. (you can check it here also from here here)
Now the reason for this:
please have a look on this screen-shot : http://prntscr.com/6j4gdn (on win 7)
here you can see "The day light Saving time begins on March,22 2015 at 12:00 AM.
Means time will be shifted 1 hr ahead after March,22 2015 at 12:00 AM.(only for those countries which uses daylight saving time system )
so if you changes the time to March,23 2015 the daylight saving period starts for (e.g. GMT+3:30)
and you will find correct out put on both browsers.
I guess chrome always uses daylight saving time system (only for those countries which uses daylight saving time system)
I have tested it by changing time of my system (but not 100% sure)
on the other hand mozila supports daylight saving time system. So after March,22 2015 at 12:00 AM for GMT+3:30 mozila will add 1hr to your input dateTime
I guess it would be helpful to all of you.

Related

Jquery countdown in UTC [duplicate]

Suppose a user of your website enters a date range.
2009-1-1 to 2009-1-3
You need to send this date to a server for some processing, but the server expects all dates and times to be in UTC.
Now suppose the user is in Alaska. Since they are in a timezone quite different from UTC, the date range needs to be converted to something like this:
2009-1-1T8:00:00 to 2009-1-4T7:59:59
Using the JavaScript Date object, how would you convert the first "localized" date range into something the server will understand?
Simple and stupid
var date = new Date();
var now_utc = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(),
date.getUTCDate(), date.getUTCHours(),
date.getUTCMinutes(), date.getUTCSeconds());
console.log(new Date(now_utc));
console.log(date.toISOString());
The toISOString() method returns a string in simplified extended ISO
format (ISO 8601), which is always 24 or 27 characters long
(YYYY-MM-DDTHH:mm:ss.sssZ or ±YYYYYY-MM-DDTHH:mm:ss.sssZ,
respectively). The timezone is always zero UTC offset, as denoted by
the suffix "Z".
Source: MDN web docs
The format you need is created with the .toISOString() method. For older browsers (ie8 and under), which don't natively support this method, the shim can be found here:
This will give you the ability to do what you need:
var isoDateString = new Date().toISOString();
console.log(isoDateString);
For Timezone work, moment.js and moment.js timezone are really invaluable tools...especially for navigating timezones between client and server javascript.
Here's my method:
var now = new Date();
var utc = new Date(now.getTime() + now.getTimezoneOffset() * 60000);
The resulting utc object isn't really a UTC date, but a local date shifted to match the UTC time (see comments). However, in practice it does the job.
Update: This answer is a quick-and-dirty way to get the UTC date when calling utc.toString(), utc.toLocaleString(), etc. Though, there are better solutions, in particular nowadays with modern browsers, and I should work on an improved answer. Basically, now.toISOString() (IE 9+) is what you want to use.
Convert to ISO without changing date/time
var now = new Date(); // Fri Feb 20 2015 19:29:31 GMT+0530 (India Standard Time)
var isoDate = new Date(now.getTime() - now.getTimezoneOffset() * 60000).toISOString();
//OUTPUT : 2015-02-20T19:29:31.238Z
Convert to ISO with change in date/time(date/time will be changed)
isoDate = new Date(now).toISOString();
//OUTPUT : 2015-02-20T13:59:31.238Z
Fiddle link
Date.prototype.toUTCArray= function(){
var D= this;
return [D.getUTCFullYear(), D.getUTCMonth(), D.getUTCDate(), D.getUTCHours(),
D.getUTCMinutes(), D.getUTCSeconds()];
}
Date.prototype.toISO= function(){
var tem, A= this.toUTCArray(), i= 0;
A[1]+= 1;
while(i++<7){
tem= A[i];
if(tem<10) A[i]= '0'+tem;
}
return A.splice(0, 3).join('-')+'T'+A.join(':');
}
Another solution to convert to UTC and keep it as a date object:
(It works by removing the ' GMT' part from the end of the formatted string, then putting it back into the Date constructor)
const now = new Date();
const now_utc = new Date(now.toUTCString().slice(0, -4));
console.log(now_utc.toString()); // ignore the timezone
I needed to do this to interface with a datetime picker library. But in general it's a bad idea to work with dates this way.
Users generally want to work with datetimes in their local time, so you either update the server side code to parse datetime strings with offsets correctly, then convert to UTC (best option) or you convert to a UTC string client-side before sending to the server (like in Will Stern's answer)
Browsers may differ, and you should also remember to not trust any info generated by the client, that being said, the below statement works for me (Google Chrome v24 on Mac OS X 10.8.2)
var utcDate = new Date(new Date().getTime());
edit: "How is this different than just new Date()?" see here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
If no arguments are provided, the constructor creates a JavaScript Date object for the current date and time according to system settings.
Note: Where Date is called as a constructor with more than one argument, the specifed arguments represent local time. If UTC is desired, use new Date(Date.UTC(...)) with the same arguments. (note: Date.UTC() returns the number of millisecond since 1970-01-01 00:00:00 UTC)
Adding the 60000 * Date.getTimezoneOffset() as previous answers have stated is incorrect. First, you must think of all Dates/Times as already being UTC with a timezone modifier for display purposes.
Again, browsers may differ, however, Date.getTime() returns the number of milliseconds since 1970-01-01 UTC/GMT. If you create a new Date using this number as I do above, it will be UTC/GMT. However, if you display it by calling .toString() it will appear to be in your local timezone because .toString() uses your local timezone, not the timezone of the Date object it is called on.
I have also found that if you call .getTimezoneOffset() on a date, it will return your local timezone, not the timezone of the date object you called it on (I can't verify this to be standard however).
In my browser, adding 60000 * Date.getTimezoneOffset() creates a DateTime that is not UTC. However when displayed within my browser (ex: .toString() ), it displays a DateTime in my local timezone that would be correct UTC time if timezone info is ignored.
My solution keeps the date the same no matter what timezone is set on the client-side. Maybe someone will find it useful.
My use case:
I'm creating a todo app, where you set date of your task. This date should remain constant no matter what timezone you're in.
Example. You want to call your friend at 8 am on June 25th.
You create this task 5 days before (June 20th) while you're in China.
Then, on the same day, you fly to New York for a few days.
Then on June 25th, while you're still in New York, you wake up at 7:30 am (which means you should receive task notification in 30 mins (even tho it's 1:30 pm already in China where you were when creating the task)
So the task is ignoring the timezone. It means 'I want to do it at 8 am in whatever timezone I'll be in'.
What I do is let's say 'I assume you're always in London Timezone - UTC'.
What it means is - when the user picks some date in her/his Timezone - I convert this date to the same date in UTC. ie. You pick 8 am in China, but I convert it to 8 am in UTC.
Then - next time you open the app - I read the date saved in UTC and convert it to the same date in your current timezone - eg. I convert 8 am in UTC to 8 am in the New York timezone.
This solution means that the date can mean something else depending on where you are when setting it and where you're reading it, but it remains constant in a way that it 'feels' like you're always in the same timezone.
Let's write some code:
First - we have 2 main functions for converting from/to UTC ignoring timezone:
export function convertLocalDateToUTCIgnoringTimezone(date: Date) {
const timestamp = Date.UTC(
date.getFullYear(),
date.getMonth(),
date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds(),
date.getMilliseconds(),
);
return new Date(timestamp);
}
export function convertUTCToLocalDateIgnoringTimezone(utcDate: Date) {
return new Date(
utcDate.getUTCFullYear(),
utcDate.getUTCMonth(),
utcDate.getUTCDate(),
utcDate.getUTCHours(),
utcDate.getUTCMinutes(),
utcDate.getUTCSeconds(),
utcDate.getUTCMilliseconds(),
);
}
Then, I save/read this date like:
function saveTaskDate(localDate: Date) {
// I convert your local calendar date so it looks like you've picked it being in UTC somewhere around London
const utcDate = convertLocalDateToUTCIgnoringTimezone(localDate);
api.saveTaskDate(utcDate);
}
function readTaskDate(taskUtcDate: Date) {
// I convert this UTC date to 'look in your local timezone' as if you were now in UTC somewhere around london
const localDateWithSameDayAsUTC = convertUTCToLocalDateIgnoringTimezone(taskUtcDate);
// this date will have the same calendar day as the one you've picked previously
// no matter where you were saving it and where you are now
}
var myDate = new Date(); // Set this to your date in whichever timezone.
var utcDate = myDate.toUTCString();
Are you trying to convert the date into a string like that?
I'd make a function to do that, and, though it's slightly controversial, add it to the Date prototype. If you're not comfortable with doing that, then you can put it as a standalone function, passing the date as a parameter.
Date.prototype.getISOString = function() {
var zone = '', temp = -this.getTimezoneOffset() / 60 * 100;
if (temp >= 0) zone += "+";
zone += (Math.abs(temp) < 100 ? "00" : (Math.abs(temp) < 1000 ? "0" : "")) + temp;
// "2009-6-4T14:7:32+10:00"
return this.getFullYear() // 2009
+ "-"
+ (this.getMonth() + 1) // 6
+ "-"
+ this.getDate() // 4
+ "T"
+ this.getHours() // 14
+ ":"
+ this.getMinutes() // 7
+ ":"
+ this.getSeconds() // 32
+ zone.substr(0, 3) // +10
+ ":"
+ String(temp).substr(-2) // 00
;
};
If you needed it in UTC time, just replace all the get* functions with getUTC*, eg: getUTCFullYear, getUTCMonth, getUTCHours... and then just add "+00:00" at the end instead of the user's timezone offset.
date = '2012-07-28'; stringdate = new Date(date).toISOString();
ought to work in most newer browsers. it returns 2012-07-28T00:00:00.000Z on Firefox 6.0
My recommendation when working with dates is to parse the date into individual fields from user input. You can use it as a full string, but you are playing with fire.
JavaScript can treat two equal dates in different formats differently.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse
Never do anything like:
new Date('date as text');
Once you have your date parsed into its individual fields from user input, create a date object. Once the date object is created convert it to UTC by adding the time zone offset. I can't stress how important it is to use the offset from the date object due to DST (that's another discussion however to show why).
var year = getFullYear('date as text');
var month = getMonth('date as text');
var dayOfMonth = getDate('date as text');
var date = new Date(year, month, dayOfMonth);
var offsetInMs = ((date.getTimezoneOffset() * 60) // Seconds
* 1000); // Milliseconds
var utcDate = new Date(date.getTime + offsetInMs);
Now you can pass the date to the server in UTC time. Again I would highly recommend against using any date strings. Either pass it to the server broken down to the lowest granularity you need e.g. year, month, day, minute or as a value like milliseconds from the unix epoch.
If you are dealing with dates a lot, it's worth using moment.js (http://momentjs.com). The method to convert to UTC would be:
moment(yourTime).utc()
You can use format to change your date to any format you want:
moment(yourTime).utc().format("YYYY-MM-DD")
There is offset options in moment as well but there is an additional complementary library for dealing with timezone (http://momentjs.com/timezone/). The time conversion would be as simple as this:
moment.tz(yourUTCTime, "America/New_York")
I've found the jQuery Globalization Plugin date parsing to work best. Other methods had cross-browser issues and stuff like date.js had not been updated in quite a while.
You also don't need a datePicker on the page. You can just call something similar to the example given in the docs:
$.parseDate('yy-mm-dd', '2007-01-26');
I just discovered that the 1.2.3 version of Steven Levithan's date.format.js does just what I want. It allows you to supply a format string for a JavaScript date and will convert from local time to UTC. Here's the code I'm using now:
// JavaScript dates don't like hyphens!
var rectifiedDateText = dateText.replace(/-/g, "/");
var d = new Date(rectifiedDateText);
// Using a predefined mask from date.format.js.
var convertedDate = dateFormat(d, 'isoUtcDateTime');
Using moment.js UTC method;
const moment = require('moment');
const utc = moment.utc(new Date(string));
This function works beautifully for me.
function ParseDateForSave(dateValue) {
// create a new date object
var newDate = new Date(parseInt(dateValue.substr(6)));
// return the UTC version of the date
return newDate.toISOString();
}
This method will give you : 2017-08-04T11:15:00.000+04:30 and you can ignore zone variable to simply get 2017-08-04T11:15:00.000.
function getLocalIsoDateTime(dtString) {
if(dtString == "")
return "";
var offset = new Date().getTimezoneOffset();
var localISOTime = (new Date(new Date(dtString) - offset * 60000 /*offset in milliseconds*/)).toISOString().slice(0,-1);
//Next two lines can be removed if zone isn't needed.
var absO = Math.abs(offset);
var zone = (offset < 0 ? "+" : "-") + ("00" + Math.floor(absO / 60)).slice(-2) + ":" + ("00" + (absO % 60)).slice(-2);
return localISOTime + zone;
}
If you need Date Object
Passing only date string Date assumes time to be 00:00 shifted by time zone:
new Date('2019-03-11')
Sun Mar 10 2019 18:00:00 GMT-0600 (Central Standard Time)
If you add current hours and minutes you get proper date:
new Date('2019-03-11 ' + new Date().getHours() + ':' + new Date().getMinutes())
Mon Mar 11 2019 04:36:00 GMT-0600 (Central Standard Time)
The getTimezoneOffset() method returns the time zone difference, in
minutes, from current locale (host system settings) to UTC.
Source: MDN web docs
This means that the offset is positive if the local timezone is behind UTC, and negative if it is ahead. For example, for time zone UTC+02:00, -120 will be returned.
let d = new Date();
console.log(d);
d.setTime(d.getTime() + (d.getTimezoneOffset() * 60000));
console.log(d);
NOTE: This will shift the date object time to UTC±00:00 and not convert its timezone so the date object timezone will still the same but the value will be in UTC±00:00.
This is what I have done in the past:
var utcDateString = new Date(new Date().toUTCString()).toISOString();
For other people whos goal is to get it as a "Date Object" and not as a string, and you only want to display the date/time without the TZ (probably hardcoded), what you can do is:
const now = new Date();
const year = now.getUTCFullYear();
const month = now.getUTCMonth();
const day = now.getUTCDate();
const hour = now.getUTCHours();
const tomorrowUTC= new Date();
tomorrowUTC.setDate(day + 1); // +1 because my logic is to get "tomorrow"
tomorrowUTC.setYear(year);
tomorrowUTC.setMonth(month);
tomorrowUTC.Hours(hour);
// then use the tomorrowUTC for to display/format it
// tomorrowUTC is a "Date" and not a string.
You can then do stuff like:
We will delete your account at ${format(tomorrowUTC, 'EEEE do MMMM hh:mmaaa')} UTC
(format is a date-fns function, you can use other lib if you want);
This is kinda "hacky" as this is still using your local timezone, but if you just wanna display the date and not the timezone, then this works.
If your date has the timezone on it you can use date-fns-tz:
import { zonedTimeToUtc } from 'date-fns-tz';
const dateBrazil = new Date() // I'm in Brazil, you should have or get the user timezone.
const dateUtc = zonedTimeToUtc(dateBrazil, 'America/Sao_Paulo')
Looking at your question its clear that you just want to send the date range to your backend for further post processing.
I am assuming you are conforming to the standard data guidelines which expect the data to be in a particular format. For example, I use ODATA which is a RESTfull API which expects date time objects to be in the format:-
YYYY-MM-DDT00:00:00.
That can be easily achieved via the snippet posted below(Please change the format as per your requirement).
var mydate;//assuming this is my date object which I want to expose
var UTCDateStr = mydate.getUTCFullYear() + "-" + mydate.getUTCMonth() + "-" + mydate.getUTCDate() + "T00:00:00";
If on the other hand, you are in my situation wherein you have received a date from your backend, and the browser converts that to your local date. You on the other hand are interested in the UTC date then you can perform the following:-
var mydate;//assuming this is my date object which I want to expose
var UTCDate = new Date(mydate);/*create a copy of your date object. Only needed if you for some reason need the original local date*/
UTCDate.setTime(UTCDate.getTime() + UTCDate.getTimezoneOffset() * 60 * 1000);
The code snippet above basically adds/subtracts the time added/subtracted by the browser based on the timezone.
For example if I am in EST(GMT-5) and my Service returns a date time object = Wed Aug 17 2016 00:00:00 GMT-0500
my browser automatically subtracts the timezone offset(5hrs) to get my local time. So if I try to fetch the time I get Wed Aug 16 2016 19:00:00 GMT-0500. This causes a lot of problems. There are a lot of libraries out there which will definitely make this easier but I wanted to share the pure JS approach.
For more info please have a look at: http://praveenlobo.com/blog/how-to-convert-javascript-local-date-to-utc-and-utc-to-local-date/ where in I got my inspiration.
Hope this helps!
var userdate = new Date("2009-1-1T8:00:00Z");
var timezone = userdate.getTimezoneOffset();
var serverdate = new Date(userdate.setMinutes(userdate.getMinutes()+parseInt(timezone)));
This will give you the proper UTC Date and Time.
It's because the getTimezoneOffset() will give you the timezone difference in minutes.
I recommend you that not to use toISOString() because the output will be in the string Hence in future you will not able to manipulate the date
Using moment package, you can easily convert a date string of UTC to a new Date object:
const moment = require('moment');
let b = new Date(moment.utc('2014-02-20 00:00:00.000000'));
let utc = b.toUTCString();
b.getTime();
This specially helps when your server do not support timezone and you want to store UTC date always in server and get it back as a new Date object. Above code worked for my requirement of similar issue that this thread is for. Sharing here so that it can help others. I do not see exactly above solution in any answer. Thanks.
I know this question is old, but was looking at this same issue, and one option would be to send date.valueOf() to the server instead. the valueOf() function of the javascript Date sends the number of milliseconds since midnight January 1, 1970 UTC.
valueOf()
You can use the following method to convert any js date to UTC:
let date = new Date(YOUR_DATE).toISOString()
// It would give the date in format "2020-06-16T12:30:00.000Z" where Part before T is date in YYYY-MM-DD format, part after T is time in format HH:MM:SS and Z stands for UTC - Zero hour offset
By far the best way I found to get the GMT time is first get your local date time. Then convert in to GMT String. Then use the string to build new time by removing the timezone.
let dtLocal = new Date()
let dt = new Date(dtLocal.toISOString().split('Z')[0])
Note: - it will create the new datetime in GMT. But it will be local date time as timezone will be attached to it.
Extension function:
if (!Date.prototype.toUTC){
Date.prototype.toUTC = function(){
var utcOffset = new Date().getTimezoneOffset();
var utcNow = new Date().addMinutes(utcOffset);
return utcNow;
};
}
Usage:
new Date().toUTC();

Is there any possible way to find the timezone using the ISOtime format in javascript

localDate: "2020-10-13"
localTime: "20:00:00"
dateTime: "2020-10-14T01:00:00Z"
I have these three datas, how to find the timezone using these data.
Assuming your inputs are exactly as given:
var localDate = "2020-10-13";
var localTime = "20:00:00";
var dateTime = "2020-10-14T01:00:00Z";
var offsetMillis = new Date(localDate + 'T' + localTime + 'Z') - new Date(dateTime);
var offsetHours = offsetMillis / 36e5;
console.log(offsetHours); // -5
The result is -5, which is 5 hours behind UTC. This works by creating two date objects, one that is UTC-based, and one that pretends to be UTC-based but is actually using the local values. Subtracting the two yields the offset.
Keep in mind - this is an offset from UTC, not a time zone. One cannot determine the time zone from the offset alone. See "Time Zone != Offset" in the timezone tag wiki for more details.
Also keep in mind that not all time zone offsets will be in whole hours. For example, India uses UTC-5:30, so expect -5.5 from this code in such a case. (There also exist time zones with 45-minute offsets.)

Convert past datetimes using JS with browser support

toLocaleString() is deprecated but seems to lack replacement
I have a website where I store member post dates in this format: YYYY-MM-DD HH:MM:SS
The problem is that these dates are all stored with the timezone UTC and I want to convert these dates using JS to the users OS timezone. I know I could just ask users to input their timezone but I would like to try something new which require as little user interaction as possible. The goal of the site is to make a forum which is incredibly easy and straightforward to use.
I have almost gotten it to work the way I want but it currently only supports Chrome it seems like. This is not good enough since I plan on having a lot of users.
Since the timezone offset varies it is not good enough to only retrieve the current time offset which a lot of topics here seem to suggest. I want to accurately display for e.g. registration dates.
This is my current solution (timezone test only supports chrome as far as I can tell) it is also implemented on the whole website which is in beta.
https://ingsan.net/timezone/
Source code below
var d = new Date();
var tzName = d.toLocaleString('en', {timeZoneName:'short'}).split('').pop();
var cusid_ele = document.getElementsByClassName('timestamp');
for (var i = 0; i < cusid_ele.length; ++i) {
var timestamp = cusid_ele[i];
timestamp.innerHTML += " "+tzName;
t = new Date(timestamp.innerHTML);
usertime = t.toLocaleString()
timestamp.innerHTML = usertime;
}
I have used this site for years without asking but this time I seem to be unable to find a topic similar enough to mine.
The method I use which might be questionably is simply replacing objects inside the assigned class name. I plan to do other classes for other format outputs.
<p>Register date: <span class="timestamp">2016-01-31 20:12:55</span></p>
If you got a solution to this issue I would very much appreciate it. I am no professional and never went to any course but I have managed to make ingsan.net on my own. Help a humble student out ;)
If all you're doing is converting from UTC to the browser's local time zone, you do not need to actually try to figure out what time zone that is. Just be sure your input is clearly identified as UTC, and the default behavior of the Date object is to display values in the browser's local time zone.
var input = "2016-01-31 20:12:55";
var d = new Date(input + " UTC");
var s = d.toString();
The resulting string will vary by implementation and time zone, such as (a few examples):
"Sun Jan 31 2016 12:12:55 GMT-0800 (Pacific Standard Time)"
"Sun Jan 31 2016 15:12:55 GMT-0500 (Eastern Standard Time)"
"Mon Feb 01 2016 01:42:55 GMT+0530 (India Standard Time)"
"Mon Feb 01 2016 09:12:55 GMT+1300 (New Zealand Daylight Time)"
This will work in all browsers. The only problem is that you don't have control over the format of the output string. That's where toLocaleString can help, though as you pointed out - it's not necessarily implemented in all browsers. However, you seemed to think it was deprecated, and it is not. It's actually part of a newer specification (ECMA-402), which just is not widely implemented everywhere (yet).
You have other options though. You could build the output string yourself, like this:
var z = function(x) {return x < 10 ? '0' + x : x};
var s = d.getFullYear() + '-' + z(d.getMonth() + 1) + '-' + z(d.getDate()) + ' '
+ z(d.getHours()) + ':' + z(d.getMinutes()) + ':' + z(d.getSeconds());
Of course you could figure out how to adjust to 12 hour format, use different separators, different date-field ordering (YMD vs DMY vs MDY), etc.
Or, you could just use a library such as moment.js which is already equipped for that.
var input = "2016-01-31 20:12:55";
var m = moment.utc(input).local(); // parse as UTC, then switch to local mode
var s = m.format(); // use any of the format parameters you wish

How to get hours and minutes in desired timezone without creating new moment object?

I have to display a string on the web page in this format: 16:00 HH:mm
I'm using a moment object to represent a date/time and timezone.
var day = moment().tz('GMT');
day.hours(16).minutes(0).seconds(0).milliseconds(0);
So this is 16:00 in GMT time.
On my web page I want to change the time zone and then collect the hours and minutes.
If I make a new moment object
var day2 = moment().tz('PST); //this is 8 AM since gmt was 16
console.log(day2.get('hours'));
it is 16 not 8!
and try to get the hours and minutes they are in GMT not in PST.
How can I get it in PST? Do I have to keep wrapping it?
// initialize a new moment object to midnight UTC of the current UTC day
var m1 = moment.utc().startOf('day');
// set the time you desire, in UTC
m1.hours(16).minutes(0);
// clone the existing moment object to create a new one
var m2 = moment(m1); // OR var m2 = m1.clone(); (both do the same thing)
// set the time zone of the new object
m2.tz('America/Los_Angeles');
// format the output for display
console.log(m2.format('HH:mm'));
Working jsFiddle here.
If you can't get it to work, then you haven't correctly loaded moment, moment-timezone, and the required time zone data. For the data, you either need to call moment.tz.add with the zone data for the zones you care about, or you need to use one of the moment-timezone-with-data files available on the site.
In the fiddle, you can see the moment-files I'm loading by expanding the External Resources section.
PST can mean different things in different regions. In the moment-timezone docs, I see nothing referring to "PST" or similar abbreviations.
Perhaps try:
var day2 = moment().tz('PST');
// 16 with Error: Moment Timezone has no data for PST. See http://momentjs.com/timezone/docs/#/data-loading/.
var day2 = moment().tz('America/Los_Angeles');
// 15
I don't know about using moment.js, but it's fairly simple using POJS and the same algorithm should work. Just subtract 8 hours from the UTC time of a date object and return a formatted string based on the adjusted UTC time.
Assuming PST is "Pacific Standard Time", also known as "Pacific Time" (PT), and is UTC -8:00:
/* #param {Date} date - input date object
** #returns {string} - time as hh:mm:ss
**
** Subtract 8 hours from date UTC time and return a formatted times string
*/
function getPSTTime(date) {
var d = new Date(+date);
d.setUTCHours(d.getUTCHours() - 8);
return ('0' + d.getUTCHours()).slice(-2) + ':' +
('0' + d.getUTCMinutes()).slice(-2) + ':' +
('0' + d.getUTCSeconds()).slice(-2);
}
document.write('Current PST time: ' + getPSTTime(new Date));
There is moment-timezone which adds functionality to moment.js for IANA time zones. For PST you can use America/Los_Angeles, however it might also automatically adjust for daylight saving so you'll get PDT when that applies. If you want ignore daylight saving, use the above or find a location with the offset you need and use that.

Javascript date parsing on Iphone

I'm working on an offline capabable Javascript site that targets mobile devices. One such mobile device is the IPhone.
I'm trying to parse a date from our REST API (a member of JSON object). I'm using
Date.parse("2010-03-15 10:30:00");
This works on Android devices, however on IPhone it just gives an invalid date.
How do I need to format my date string so it can be parsed by the IPhone?
Not all browsers support the same date formats. The best approach is to split the string on the separator characters (-, and :) instead, and pass each of the resulting array items to the Date constructor:
var arr = "2010-03-15 10:30:00".split(/[- :]/),
date = new Date(arr[0], arr[1]-1, arr[2], arr[3], arr[4], arr[5]);
console.log(date);
//-> Mon Mar 15 2010 10:30:00 GMT+0000 (GMT Standard Time)
This will work the same in all browsers.
For UTC/GMT time, you can try:
var arr = "2014-10-27T16:05:44+0000".split(/[\-\+ :T]/);
var date = new Date();
date.setUTCFullYear(arr[0]);
date.setUTCMonth(arr[1] - 1);
date.setUTCDate(arr[2]);
date.setUTCHours(arr[3]);
date.setUTCMinutes(arr[4]);
date.setUTCSeconds(arr[5]);
The date object will display in the proper local timezone when used.
You might have better luck if you stick to ISO 8601 format:
Date.parse("2010-03-15T10:30:00");
// e.g.
var d = new Date( Date.parse("2010-03-15T10:30:00") );
console.log( d.toString() ); //Mon Mar 15 2010 10:30:00 GMT+0000 (BST)
Update 2019-07-29: Not removing existing content below, but I highly suggest you don't use this method. I learned from my mistakes. Do not override JavaScript's existing methods. Becomes bad for your code's portability and performance. If you can't get ISO 8601 (which is standard practice in JS and most API's) - then change that system instead. Alternatively, write your own function, which always has to be called to generate a Date object.
If you can correct your REST API to send down proper ISO 8601 date times, the proper way to handle this is to use a regex shim that allows all browsers to process ISO 8601 dates. Sure it might be slower, but handling dates with Javascript is like trying to get a cat into a tub of water.
Keep in mind the following method overrides the original Date.parse method. This could be problematic in larger projects or with many developers with different expectations. Use only if you're aware of what you're doing.
/**
* Date.parse with progressive enhancement for ISO 8601 <https://github.com/csnover/js-iso8601>
* © 2011 Colin Snover <http://zetafleet.com>
* Released under MIT license.
*/
(function (Date, undefined) {
var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ];
Date.parse = function (date) {
var timestamp, struct, minutesOffset = 0;
// ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string
// before falling back to any implementation-specific date parsing, so that’s what we do, even if native
// implementations could be faster
// 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm
if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) {
// avoid NaN timestamps caused by “undefined” values being passed to Date.UTC
for (var i = 0, k; (k = numericKeys[i]); ++i) {
struct[k] = +struct[k] || 0;
}
// allow undefined days and months
struct[2] = (+struct[2] || 1) - 1;
struct[3] = +struct[3] || 1;
if (struct[8] !== 'Z' && struct[9] !== undefined) {
minutesOffset = struct[10] * 60 + struct[11];
if (struct[9] === '+') {
minutesOffset = 0 - minutesOffset;
}
}
timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]);
}
else {
timestamp = origParse ? origParse(date) : NaN;
}
return timestamp;
};
}(Date));
https://github.com/csnover/js-iso8601
There is bug in the
var date = new Date();
date.setUTCFullYear(arr[0]);
date.setUTCMonth(arr[1] - 1);
date.setUTCDate(arr[2]);
date.setUTCHours(arr[3]);
date.setUTCMinutes(arr[4]);
date.setUTCSeconds(arr[5]);
approach. If Date() is the end of month and the month set has less days then the result is one month out.
Wrapping Up with the string will do the magic. Since the safari has some unique way of parsing and formatting date
Date.parse(String("2010-03-15 10:30:00"));
Still facing issue use the Moment.js
Happy to help. :-)

Categories

Resources