My server is sending my Javascript client a timestamp in UTC. I'm currently in Mountain Time which is currently in daylight savings (GMT-7), but any timezone adjustment I do is only applying -6 offset.
To confirm that javascript is even aware of my timezone, I did the following:
console.log(Date().toString()); which outputs the following: Mon Nov 19 2018 12:13:28 GMT-0700 (Mountain Standard Time). It's clear that JS knows I am currently in GMT-7.
Now, my server is sending 2018-08-24T17:00:00. So I parse it with moment.js, convert to local timezone and then format the result.
moment.utc(this.props.value).local().format('h:mm A')
The resulting value is 11:00 AM. 17:00:00 - 7 offset is 10:00 which is 10:00 AM. Why is javascript converting into 11:00? I get the same result if I try the spacetime library:
const s = spacetime(this.props.value,'UTC')
s.goto(spacetime().timezone().name);
console.log(s.format('h:mm a')); // Also spits out 11:00 AM
If I manually adjust the moment object with the offset, it works correctly:
var m = moment.utc(this.props.value);
m.add(-(new Date()).getTimezoneOffset(), 'minutes');
console.log(m.format('h:mm A')) // Outputs the correct time: 10:00 AM
Why is moment and spacetime both not adjusting my timezone correctly when Javascript is clearly aware of what timezone I'm in? The same problem occurs in both Chrome and Microsoft Edge. For now I will use the hacky workaround above, but I'd prefer to use the native methods so I'm curious as to why this isn't working. Any ideas?
Mountain Standard Time is UTC-7
Mountain Daylight Time is UTC-6
It is currently November 19th, and DST is not in effect, so you currently get UTC-7
For the date you gave, 2018-08-24T17:00:00, DST is in effect, so you get UTC-6
Everything is working correctly.
In other words, it doesn't matter whether it is currently in effect or not, only whether it is/was/will be in effect for the date in question.
Regarding this part:
m.add(-(new Date()).getTimezoneOffset(), 'minutes');
Don't do that. You aren't adjusting the time zone, you're picking a different moment in time.
Related
This question already has answers here:
When is it ok to store datetimes as local time rathen than UTC?
(1 answer)
How to store repeating dates keeping in mind Daylight Savings Time
(1 answer)
Managing timezone & DST issue for javascript application
(1 answer)
java Calendar, Date, and Time management for a multi-timezone application
(1 answer)
Closed 3 years ago.
I have a scheduling application, with a calendar that I build from scratch.
As a lawyer, you should be able to configure your available times for booking, like below:
A lawyer's availabilities in Australia
:
1- 10/01/2020, from 07:00am to 08:am
...
Here's what I do :
1- Get the epoch number of the entered date in Javascript :
const dateFrom = new Date(firstOfJanSevenAm).getTime() // 1578600000000
// Fri Jan 10 2020 07:00:00 GMT+1100 (Australian Eastern Daylight Time)
const dateTo = new Date(firstOfJanEightAm).getTime() // 1578603600000
// Fri Jan 10 2020 08:00:00 GMT+1100 (Australian Eastern Daylight Time)
2- Send this to NodeJS server and save it MongoDB
Mongoose.save({
from:dateFrom, //1578600000000
from:dateTo //1578603600000
})
3- Represent it inside the Calendar :
<div>{format(from, 'HH:mm')}</div>
Everything is working as expected.
Now, this lawyer is traveling to the US and he's in a coffee shop using the US local time ( any city), he opens the Calendar, he wants to add some availability, but in Sydney time. I need to provide him with a timezone dropdown so he can tell me that he wants the new date to be based on his home, Syndey.
Question :
1- Do I save the date as I'm doing ( as a number ), and save the timeZone separately next to it, and when representing it, just apply the timeZone?
Mongoose.save({
from:dateFrom, //1578600000000
from:dateFrom //1578603600000
currentTimeZone : 'America/Costa_Rica',
desiredTimeZone: 'Australia/Sydney'
})
<div>{formatWithTimeZone(from, 'HH:mm',desiredTimeZone)}</div>
Is this all I have to do? Or am I naively missing something that is going to trip me down the road?
And back to my original questions, where do I do the whole "always store time as UTC" thing?
All I've realized is, when I use the library that I'm using date-fns-tz and try to convert the user entered date to UTC, I get exactly the same output :
const dateFrom = new Date(firstOfJanSevenAm).getTime() // 1578600000000
const dateFromUTC = zonedTimeToUtc(dateFrom,currentTimeZone) // 1578600000000
// currentTimeZone is America/Costa_Rica, when he is in Costa Rica's caffee shop.
enter code here
1578600000000 === 1578600000000 = true
So why do I get the same output when converting the entered date, to it's UTC date?
I am going to give you an answer decoupled from the technical implementation.
Let's think by contradiction, you have a lawyer living in Australia and another living in Switzerland, what happens if you decide to store time in their preferred location?
You then need to save two information: the time of course (11 am) but it's relative so you also need to store the timezone (11 am in Australia) or (1 pm in Switzerland)
Now what happens if your lawyer travels to France? Do you want to update all his calendar information? 11 am is not 11 am anymore.
UTC solves this problem, you just need to store 11 am or 1 pm. UTC is arbitrary absolute, universal by convention.
So your backend/database should store this kind of information in UTC, always. And you need a way to know your user's timezone, maybe it's possible to update it using a web interface and it could be stored in a user database or just cookies. Maybe you want to derive it yourself by using your user's location, IP, browser's language, whatever.
Now that you know his timezone, your server can send UTC (absolute) time to the client and the client can seamlessly display the correct (relative) time.
The client deals with relative time, the server always makes it absolute.
So why do I get the same output when converting the entered date, to it's utc date?
The value returned by getTime is the number of milliseconds since 1 January 1970 00:00:00 according to universal time.
Therefore, UTC and getTime both represent a moment in time using the same universal timezone.
Do I save the date as I'm doing, and save the timeZone separately next to it, and when representing it, just apply the timeZone?
Don't save the timezone with the date. Just save the date in universal time. If you need to save timezone. That should go in user settings. However, you can guess the timezone; therefore, you don't need to save this information.
Saving the date
When you do save the date to the database, it should be represented by a UTC time string or by a UNIX timestamp, both options demonstrated below.
UNIX timestamp
UNIX timestamp should be in seconds. getTime returns milliseconds. You can just divide by 1000 to get the UNIX timestamp.
const unixTimestamp = new Date('December 17, 1995 03:24:00').getTime() / 1000;
UTC Date string
const utcDate = new Date('December 17, 1995 03:24:00').toUTCString();
Displaying the date
When you get the date from the back-end, then, converted it to the correct timezone.
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone
const fromUnix = utcToZonedTime(1578423483, timezone)
const fromUtc = utcToZonedTime('Fri, 02 Feb 1996 03:04:05 GMT', timezone)
Closing thoughts
Dealing with timezones can be confusing. I am not to familiar with date-fns-tz. If you have the option, I would suggest migrating to Moment.js. Moment.js is the de facto standard JavaScript library these days - I highly recommend it.
I use momentjs to parse a Date String and convert it to a native JavaScript Date:
let dateString = '1980-04-06';
console.log(moment().utcOffset());
console.log(moment(dateString, 'YYYY-MM-DD').toDate());
<script src="https://cdn.jsdelivr.net/npm/moment#2.22.2/moment.min.js"></script>
The output on client 1(Firefox 62) is
120
Date 1980 - 04 - 05 T23: 00: 00.000 Z
and the output on client 2(Firefox 52 ESR) is
120
Date 1980 - 04 - 05 T22: 00: 00.000 Z
Can somebody explain me, why the utcOffset is the same (new Date().getTimezoneOffset() prints also -120 on both clients), but the Date (hour) is different?
You're checking the current UTC offset, not the offset of your 1980 moment instance. My guess is that if you took moment(dateString, 'YYYY-MM-DD') and called utcOffset on that, you'd get different offsets on the different browsers.
I bet what's happening is that the rules for your zone have changed since 1980 (for example, perhaps the timing of the DST has changed, or DST has been added or eliminated, or perhaps the standard offset has even changed). Browsers vary in the degree to which they get historical zone data right, which leads to errors interpreting date strings. I suspect that Firefox fixed their historical zone database for your zone, leading to different behavior in newer versions of the browser.
The offsets you're showing are for the current date and time, not for the date provided. If you change the middle line to log moment(dateString, 'YYYY-MM-DD').utcOffset(), you should see that the result in the older Firefox 52 is 60 instead of 120.
Contributing factors explaining this difference are:
The daylight saving time rules for your time zone are not the same in 1980 as they are today. Assuming Vienna (from your user profile), in 1980 the start of DST was at 00:00 on April 6th (reference here) which is the first Sunday in April. The current (2018) rule for Vienna is the last Sunday in March, which would be March 25th 2018 (reference here).
ECMAScript 5.1 (section 15.9.1.8) and earlier required browsers to always assume the current DST rule was in effect for all time - even if this was not actually what happened. This was corrected in ECMAScript 6 / 2015 (section 20.3.1.8) .
ECMAScript 2015 was implemented in Firefox starting with version 54. Since you are testing version 52, you are seeing the old behavior.
Since this particular DST change is right at the stroke of midnight, and it's a spring-forward transition, then, 1980-04-06T00:00 is not valid. The first moment of that day in that time zone is 1980-04-06T01:00. Moment takes care of this for you when you pass a date-only value. In the current browser (62, not 52), If you call .format() on the moment, you should see 1980-04-06T01:00:00+02:00. Note that this is time is already in DST, with a UTC+02:00 offset. Converted to UTC is 1980-04-05T23:00:00Z, thus aligning with the correct data as seen in your examples.
Long story short, there are many reasons to use up-to-date browsers. This is one of them.
I am using moment-timezone library to build a UI that needs to be relative to a variety of timezones.
I am taking an input of a timezone, i.e "America/Chicago" and need to get the start of day in GMT.
For instance, if today is March 27th at 9am Chicago time (2pm GMT), I need to get the date in epoch seconds for March 27th, 00:00 AM.
I'm using the moment.tz("America/Chicago").startOf('day') but I keep getting Tue Mar 27 2018 01:00:00 GMT-0400 (EDT) . Any idea how to do this?
Thanks
// This part you are already doing correctly.
// You get back a Moment object representing the start of the current day in Chicago:
var m = moment.tz("America/Chicago").startOf('day');
I need to get the date in epoch seconds
// Ok, so simply now get the associated Unix Time
var timestamp = m.unix();
Also note that the correct terminology is "Unix Time", not "epoch seconds".
See my blog post: "Please don't call it Epoch Time".
... but I keep getting Tue Mar 27 2018 01:00:00 GMT-0400 (EDT)
You are probably either looking at _d or a Date object, or rather the string representation of one. Don't. See this Moment.js documentation for details.
Per your comment:
I need to take the current time in a specific timezone. I then need to convert that time to the corresponding day in GMT. Finally I need to get the midnight epoch timestamp of that GMT day.
That's a little different then you originally asked, but it would be like this:
var timestamp = moment.utc().startOf('day').unix();
Note that there's no purpose in involving another time zone for this operation. Logically, when asking for "Now in time zone A converted to time zone B", it's the same as asking for "Now in time zone B". In other words, you would get the same value even when the time zone was present:
var timestamp = moment.tz('America/Chicago').utc().startOf('day').unix();
So you're better off just leaving the time zone out.
I have been using momentjs and moment-timezone for a while. While using date comparison using isAfter method today, I discovered strange behaviour.
Suppose one moment date is configured with timezone value and other isn't then what should be the behaviour of date comparison?
In my case, we are converting an epoch time to specific timezone value for one date. Other date is in format yyyy-mm-dd without any timezone value. When I am comparing these two days, it is failing for same day value.
//Wed Sep 27 2017 01:13:04 GMT-0700
var localtime = moment(1506499984924).tz("America/Los_Angeles");
//Wed Sep 27 2017 00:00:00
var date = moment('2017-09-27');
//returns true
var value = localtime.isAfter(date, 'day');
Ideally since both dates are Sept 27 2017, it should return false.
Using diff method instead isAfter returns 0 which is true.
Any help is appreciated to solve this issue.
I have created a pen with this code: Moment
In your code:
var date = moment('2017-09-27');
This create a value at midnight local time. How that relates to a specific instant in time is highly dependent on which local time zone your computer is set to.
Moment objects are always compared as moments - that is, instantaneous values based on UTC.
I see false when I run your code pen because I am in a time zone that is behind UTC. If you see true, it's because you are in a local time zone that is ahead of UTC (by over an hour, given the time of the other value).
This question already has an answer here:
HTML Input type datetime-local setting the wrong time-zone
(1 answer)
Closed 7 years ago.
I'm writing a MVC 5 web application. Into this, I have multiple lists that I propagate and manage through javascript and jquery (one dataset, dependent select controls, and adding ajax callbacks would complicate it unnecessarily.)
The issue I have is: I have a hidden for field formatted to ISO 8601. I run into issues when I display the date in the user's local time, I get a shifted date.
So if the date were stated as: 01-01-2009 (in iso 8601 format: 2009-01-01), the user sees: 12-31-2008.
When I parse the date I'm using:
this.date = new Date(Date.parse(originalString));
/* ^- Date.parse is giving me a number. */
To display the text of the date I am using:
admin.date.toLocaleDateString().Concat(...
Do I need to do any sort of patch-up to adjust things to the proper time-zone? The date, when using console.log(admin.date); shows the original 2009-01-01
I'm thinking there's some parameter I'm not specifying correctly in the toLocaleDateString, but my familiarity level with it is low.
Edit: The goal is to prevent the date shift. All we store is the date, the time aspect is dropped. We have multiple time-zones posting to this database, and the goal is: We use the date of the person who posted it, time dropped. Were the date May 01, 2015, I want anyone who sees that date to see May 01, 2015, the 'toLocaleDateString' is merely a means to get it to appear format correct for their region. So someone who views dates as yyyy-mm-dd will see it properly.
Based on the documentation for Date.parse:
The Date.parse() method parses a string representation of a date, and returns the number of milliseconds since January 1, 1970, 00:00:00 UTC.
You are getting back the epoch time in UTC for your parsed date string hence why the date is off for local times. So, you would need to know the time difference between the local time and UTC which Date.getTimezoneOffset provides (in minutes) in order to set the correct date for the local time:
> var date = new Date(Date.parse('2009-01-01'));
undefined
> date;
Wed Dec 31 2008 19:00:00 GMT-0500 (EST)
> date.getTimezoneOffset();
300
> date.setMinutes(date.getTimezoneOffset());
1230786000000
> date;
Thu Jan 01 2009 00:00:00 GMT-0500 (EST)
One thing to note though is:
...the offset is positive if the local timezone is behind UTC and negative if it is ahead.
So you might need to take care for locales where the value is negative if this applies to your application. If so maybe just omitting negative values would be enough since the date should be the same if a locale's timezone is ahead of midnight UTC.
EDIT: To compensate for possible issues with daylight savings time:
> var dateVals = String.prototype.split.call('2009-01-01', '-');
undefined
> var date = new Date(dateVals[0], dateVals[1] - 1, dateVals[2]);
undefined
> date
Thu Jan 01 2009 00:00:00 GMT-0500 (EST)