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).
Related
I am using Node.js with pg to query a Postgres database. I am trying to execute the following query:
SELECT COUNT(*) FROM table_name WHERE date_time_added::date = some_date;
some_date is the date I pass from Node.js.
I have no problems with this most of the time, as both date/time strings are in the same format. However, issues arise when Timezones are introduced. As I am located in the UK, we use British Summer Time (GMT+1) for some parts of the year.
When I cast date_time_added from it's stored type (timestamp with timezone) to date, the time defaults to midnight. The issue with this is that during BST, the timestamp is cast to one day earlier, and the time set to 23:00 instead of 00:00, as shown below:
{ date_time_added: 2020-09-28T23:00:00.000Z }
This wouldn't be a problem if JavaScript had the same behaviour, however if I try the following:
const date1 = new Date(2020, 8, 29, 10, 47, 54);
console.log(date1.toString());
date1.setHours(0);
date1.setMinutes(0);
date1.setSeconds(0);
date1.setMilliseconds(0);
console.log(date1.toString());
The following output is produced:
"Tue Sep 29 2020 10:47:54 GMT+0100 (British Summer Time)"
"Tue Sep 29 2020 00:00:00 GMT+0100 (British Summer Time)"
The time is set to midnight rather than 23:00 the day before. This is a problem for me as this means the dates don't match, and therefore the row is not returned in the database query.
Does anyone know how I can get the same behaviour for both dates (JS and Postgres)? It would be good to be able to initialise a new Date Object with the time defaulting in the same way as Postgres does when I cast to date, however I am not sure if this is possible.
Posting in case anyone else comes across this problem in the future.
This issue was caused because casting a timestamp to a date in Postgres loses the timezone, therefore it was subtracting an hour from midnight (as the time is set to midnight when casting a timezone to a date), causing it to become 23:00 one day before the date (as BST is +1 hour).
I decided to use epoch time as this made comparison easier for my needs.
I solved this by using the following query:
SELECT COUNT(*) FROM table_name WHERE extract(epoch from date_trunc('day', date_time_added)) = some_date_epoch;
This keeps the timezone and returns the epoch time (at midnight) for the date of the selected timestamp. 'some_date_epoch' is the epoch time of a JavaScript Date Object, obtained using the Date.getTime() method. This is the epoch time of the date I'd like to search for in the database.
For example, this is how I'd get the epoch time for the current date in JavaScript, to then use in the database query:
let currentDate = new Date();
currentDate.setHours(0);
currentDate.setMinutes(0);
currentDate.setSeconds(0);
currentDate.setMilliseconds(0);
let currentDateEpoch = (currentDate.getTime() / 1000); //Convert to seconds to match Postgres
currentDateEpoch would then be used to query the database.
I am trying to convert milliseconds into UTC date object as below -
var tempDate = new Date(1465171200000);
// --> tempDate = Mon Jun 06 2016 05:30:00 **GMT+0530 (India Standard Time)** {}
var _utcDate = new Date(tempDate.getUTCFullYear(), tempDate.getUTCMonth(), tempDate.getUTCDate(), tempDate.getUTCHours(), tempDate.getUTCMinutes(), tempDate.getUTCSeconds());
//--> _utcDate = Mon Jun 06 2016 00:00:00 **GMT+0530 (India Standard Time)** {}
Time is resetting to UTC time but Time Zone is still coming as GMT+0530 (India Standard Time).
Is there any sure shot approach to convert milliseconds into UTC date object with UTC Time Zone?
Quoting from this answer (that I suggest you to read completely):
There is no time zone or string format stored in the Date object itself. When various functions of the Date object are used, the computer's local time zone is applied to the internal representation.
As time zone is not stored in the Date object there is no way to set it.
I see two options:
the first is to make use of a library (as suggested in the answer above). Quite popular now is Moment.js
the second (pure JavaScript - if it's a viable solution in your context):
Do the "time math" in your local timezone.
When you're ready to switch to UTC use toUTCString() method.
Of course you'll end up with a string as this let you store the time zone as long as the date time value.
As you won't be able to manipulate the date time as a Date object from now on this must be the last step.
var tempDate = new Date(1465171200000);
// Mon Jun 06 2016 05:30:00 GMT+0530
// Do your date time math here
// using the Date object's methods
var utcDateAsString = tempDate.toUTCString();
// Mon Jun 06 2016 00:00:00 GMT
You say:
Time is resetting to UTC time but Time Zone is still coming as GMT+0530 (India Standard Time). Is there any sure shot approach to convert milliseconds into UTC date object with UTC Time Zone?
But I think you misunderstand what is occurring. When you pass a number to the Date constructor as in:
new Date(1465171200000)
is it assumed to be milliseconds since the ECMAScript epoch (1970-01-01T00:00:00Z), so a Date object is created with that value as its internal time value. So Date objects are inherently UTC.
When you write that to a string, internally a human readable date string is generated based on the host timezone setting, which is why you see a date for GMT+0530 (that is your host system timezone setting). The Date object itself does not have a timezone, it's always UTC.
When you then use UTC values to create a "local" Date using:
new Date(tempDate.getUTCFullYear(), tempDate.getUTCMonth(), ...)
then the host timezone is used to generate a UTC time value equivalent to a "local" date for the associated values. You've effectively subtracted your timezone offset from the original time value so it now represents a different moment in time. You can get exactly the same result doing:
var d = new Date(1465171200000);
d.setMinutes(d.getMintues() + d.getTimezoneOffset());
which just shows a bit more clearly what's going on. Note that ECMAScript timezone offsets are in minutes and have the opposite sense to UTC, that is, they are negative (-) for east and positive (+) for west. So an offset of UTC+05:30 it is represented as -330 and you need to add it to "shift" a Date rather than subtract it.
var tempDate = new Date(1465171200000);
var _utcDate = new Date(tempDate.getUTCFullYear(), tempDate.getUTCMonth(), tempDate.getUTCDate(), tempDate.getUTCHours(), tempDate.getUTCMinutes(), tempDate.getUTCSeconds());
console.log('Direct conversion to Date\ntempDate: ' + tempDate.toString());
console.log('Adjusted using UTC methods\n_utcDate: ' + _utcDate.toString());
tempDate.setMinutes(tempDate.getMinutes() + tempDate.getTimezoneOffset());
console.log('Adjusted using timezoneOffset\ntempDate: ' + tempDate.toString());
However, I can't understand why you want to do the above. 1465171200000 represents a specific moment in time (2016-06-06T00:00:00Z), adjusting it for every client timezone means it represents a different moment in time for each client with a different timezone offset.
If you create a Date from a Number, the local timezone is the one considered. But if you want to see what a timestamp would mean with the hours corrected for UTC, you could use a helper as such:
Number.prototype.toUTCDate = function () {
var value = new Date(this);
value.setHours(value.getHours() - (value.getTimezoneOffset() / 60));
return value;
};
The usage would be:
var date = (1465171200000).toUTCDate();
my system uses timezone UTC+03:00 ,
im trying to get a date in string format, represented by NY timezone,
and convert it to a Date object in utc
const dateInNY = moment.tz(xlsxDate, "M/D/YYYY h:mm a", "America/New_York")
.tz("Z").toDate();
doesnt work correctly
how am i even suppose to convert to utc time?
-----------edit---------------
i got it to work, using the timezone "Africa/Accra" , where UTC offset is 0, and ther is no daylight savings time:
moment.tz(xlsxDate, "M/D/YYYY h:mm a", "America/New_York").tz("Africa/Accra")
but this solution is a bad workaround, and if the government of Accra decide to change the time laws, will stop working!
is there a way to set the utc offset to 0 in momentjs-timezones?
As Álvaro González mentioned, that Date object does not contain Time zone information.
I do the following:
new Date(moment.tz(date, currentTimezone).tz(newTimezone).format('YYYY/MM/DD HH:mm:ss'))
where date is a date object or a string (e.g. '2017-10-30 16:30:00.0000')
so, I change date from currentTimezone to newTimezone and after that new Date object will be returned
Let's change '2017-10-30 16:30:00.0000' from UTC to America/Toronto (UTC-4)
new Date(moment.tz(date, 'UTC').tz('America/Toronto').format('YYYY/MM/DD HH:mm:ss'))
And I got
Mon Oct 30 2017 12:30:00 GMT+0400
GMT+0400 is my timezone and console.log() just shows it with any
date object and it can mislead you. Please, don't look at the this
timezone.
Let's change '2017-10-30 16:30:00.0000' from Europe/Samara (UTC+4) to America/Toronto (UTC-4)
new Date(moment.tz('2017-10-30 16:30:00.0000', 'Europe/Samara').tz('America/Toronto').format('YYYY/MM/DD HH:mm:ss'))
Firstly, moment.tz undertands that date has no timezone information and associate with Europe/Samara (UTC+4)
timezone. After that computes difference between new and old
timezone (it's -8 hours in this case)
And returns result
Mon Oct 30 2017 08:30:00 GMT+0400
And answer on your question
If xsltDate is a date object or string which do not contain timezone information
dateUTC = new Date(moment.tz(xlsxDate, "America/New_York").tz("UTC").format('YYYY/MM/DD HH:mm:ss'));
If xsltDate contain timezone information (e.g.'2013-06-01T00:00:00-04:00'), then no need to tell moment.tz which timezone xlsxDate has, just mention a new timezone
dateUTC = new Date(moment.tz(xlsxDate, "UTC").format('YYYY/MM/DD HH:mm:ss'));
Short answer is that you cannot.
The .toDate() method of the Moment library returns a native Date object. Such objects do not keep memory of any specific time zone (that's one of the reasons to use Moment in the first place), they just keep track of the exact time moment represented and merely pick a time zone when formatting to string, which is either UTC or the browser's time zone (not an arbitrary one).
The long answer is that you're probably getting correct results but are printing them with a method that uses the browser's time zone.
i found a function that does what i was trying to do, it belongs to the momentjs library itself: utcOffset(n) sets the offset to n.
(i also had to explicitly write the date string format correctly, thanks VincenzoC)
this is the code i was trying to write:
const dateInNY = moment.tz(xlsxDate, "M/D/YYYY h:mm a", "America/New_York");
const dateUTC = dateInNY.utcOffset(0).toDate();
however, the toDate function changes the timezone to my local timezone anyway, so .utcOffset(0) is redundat, and i can just use moment this way:
const dateInNY = moment.tz(xlsxDate, "M/D/YYYY h:mm a", "America/New_York");
const dateUTC = dateInNY.toDate();
and change the Date objects date to utc time later (in my case, the JSON.stringify stuff i use later does that for me)
I store two dates. First date is the current day and the second one is a future date. To convert those dates into format year-month-day I use toISOSstring function. However usually (but not always) the current date is changed one day backwards.
I also tried to use toJSON function instead. But nothing has changed.
season.from = "Sun Apr 02 2017 18:29:52 GMT+0200 (CEST)"
season.to = "Fri Apr 21 2017 18:29:52 GMT+0200 (CEST)"
var date1 = new Date(season.from);
var date2 = new Date(season.to);
season.from = date1.toISOString().slice(0,10);
season.to = date2.toISOString().slice(0,10);
console.log(season.from); // one day backwards (e.g. 2017-04-01 not 2017-04-02)
console.log(season.to); // proper date somewhere in the future
Your original time strings are in local time, or at least they have a time zone specification. But toISOString returns the UTC time:
The timezone is always zero UTC offset, as denoted by the suffix "Z"
In the case of your GMT+02 time zone, this means the date/time returned by toISOString is two hours "earlier" than the local time. In some cases this can be a time before midnight, and this can render a different date as well.
The implementation of the toJSON method depends on toISOString, so it has the same behaviour.
Work-around
You could use toLocaleDateString('se'), which uses your local time, and formats according to Swedish standards, i.e. YYYY-MM-DD, so you don't even need to slice it. There are some other country codes you could specify to have the same.
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)