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.)
My backend sends me a timestamp in the following format: 2019-12-15T20:00:00.000Z which is UTC time. Now all I need is to pull out the time without modifying it for timezones. I am using date-fns but every time I try to get the time I either get an Invalid Date or it modifies the time to the local timezone. Right now my code is:
import { format, parse, parseISO } from 'date-fns'
format(parseISO('2019-12-15T20:00:00.000Z'), 'h:mm a')
What I want is: 8:00 PM but what I get is 3:00 PM because its converting from UTC to EST time (my local timezone). I know I can do things like to IsoString and then parse the string, but I am looking for a way to use date-fns to do it so I don't have to write custom string parsers. There has to be an easy way to just ignore timezones?
I am going to answer my own question with how I make it work in case it helps someone, but I am still looking to see if there is a better alternative of just using date-fns since mine seems a bit hackish:
What I do is remove the timezone from the ISO string and then use that time with date-fns:
let time = "2019-12-15T20:00:00.000Z".slice(0, -5)
The above is a time with no time zone, and because there is no timezone date-fns assumes the local timezone, so when you do:
format(parseISO(time), 'h:mm a')
you get: 8:00 PM, or whatever format you prefer. You just have to be careful with the string that you are slicing. If its always the same format then it should work.
Here is one solution that manually formats the string using the vanilla JS Date object:
const date = new Date('2019-12-15T20:00:00.000Z');
function format(date) {
const hours = date.getUTCHours();
const minutes = date.getMinutes();
return (1 + ((hours - 1) % 12)) + ":" + minutes.toString().padStart(2, "0") + " " + ((hours > 11) ? "PM" : "AM");
}
format(date);
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.
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.
I am experiencing some unexpected results with a GMT time zone offset calcultaion.
I am using the ExtJS date class to help calculate the time that a message arrives from the server at GMT 0 to the user's local machine which is currently GMT +8.
I thought that if i calculated the offset, then added that to the timestamp in seconds then i could use the following calculation to give me a string to format as i please.
var d = new Date((stamp + offset) * 1000);
stamp is the date/time in seconds as is the offset.
This returns the current date and time at my location but plus 8 hours. If i do not add the offset then the time is correct.
Does the method new Date() automatically give me the date/time at local time?
Just adding this in case it helps others looking latter. May be wrong but works for what I needed.
I have the user's offset stored in seconds.
offset assumed in seconds change the (offset * 1000) to make sure offset gets converted to milliseconds if your offset not in seconds.
function offsetDate(offset){
var d = new Date(new Date().getTime() + (offset * 1000));
var hrs = d.getUTCHours();
var mins = d.getUTCMinutes();
var secs = d.getUTCSeconds();
//simple output
document.write(hrs + ":" + mins + ":" + secs);
just insert a unix timestamp containing the thousandth of a second.
that unix timestamp should be UTC 0 and javascript will look up the local timezone of the clients computer and will calculate it.
as you can see behind this link, there are some UTC methods wich give you the time without local time offset. maybe this might be what you search for (in case you really want to set the timezone yourself).
edit:
new Date().getTimezoneOffset() will show you the difference between the local timezone and utc but you should consider using the utc methods.
From the looks of it, stamp is your local time including timezone offset. JavaScript has no knowledge of the timezone of the server, it's (mainly) a client-side language.