Date objects in dictionaries change timezone in Javascript/Node - javascript

To avoid day light saving issues with date objects, I use UTC. For example:
new Date(2019,8,20, 9, 0) gives 2019-09-20T08:00:00.000Z
new Date(Date.UTC(2019,8,20, 9, 0)) gives 2019-09-20T09:00:00.000Z -- what I want
My issue now is that when I add that date to a dictionary, It uses local timezone somehow. For example:
const b = {}
b[Date(Date.UTC(2019,8,20, 9, 0))] = true
gives the following:
{ 'Fri Sep 20 2019 10:00:00 GMT+0100 (IST)': true }

you can do the following to get the UTC time -
var utcDate = new Date(Date.UTC(2019,8,20,9,0)).toUTCString()
b[utcDate] = true
EDIT
You should use ISOString() format to get format like 2019-09-20T09:00:00.000Z
var utcDate = new Date(Date.UTC(2019,8,20,9,0)).toISOString()
b[utcDate] = true

Related

How to compare two dates without hours in javascript?

I have two dates date and meeting.date coming from two APIs. I want to check, if the dates are equal.
// date = "28.02.2022";
// meeting.date = "2022-02-08 14:30:00";
const firstDate = new Date(`${date.split(".").reverse().join("-")}`);
const secondDate = new Date(`${meeting.date.split(" ")[0]}`)
firstDate.setHours(0,0,0,0);
secondDate.setHours(0,0,0,0);
console.log(firstDate, secondDate, firstDate === secondDate);
This logs me
[Log] Mon Feb 28 2022 00:00:00 GMT+0100 (CET) – Mon Feb 28 2022 00:00:00 GMT+0100 (CET) – false
I expect that to be true?
I found the solution, setting the hours to zero here on stackoverflow, but I'd say, that this is gratuitous, as I don't pass the time the Date. Anyhow, what am I doing wrong?
What you are doing here is comparing two different instances (two different references) of Date object, which lead to an unexpected result
You could try to compare the millisecond of these by using getTime
const date = "28.02.2022";
const meeting = { date: "2022-02-28 14:30:00" }
const firstDate = new Date(`${date.split(".").reverse().join("-")}`);
const secondDate = new Date(`${meeting.date.split(" ")[0]}`)
firstDate.setHours(0,0,0,0);
secondDate.setHours(0,0,0,0);
console.log(firstDate, secondDate, firstDate.getTime() === secondDate.getTime());
You are comparing the Date objects themselves - and they will never be equal unless it is the same "object" pointed to by 2 different variables, e.g.
const dateOne = dateTwo = new Date();
You must either compare the date parts as Strings, e.g.
console.log(
firstDate,
secondDate,
firstDate.toISOString().substr(0, 10) === secondDate.toISOString().substr(0, 10)
);
or compare the dates as Numbers (in milliseconds) which is the recommended way:
console.log(
firstDate,
secondDate,
firstDate.getTime() === secondDate.getTime()
);
You can use .getTime() method to convert both of the dates to the number of milliseconds since the ECMAScript epoch and then compare them.
firstDate.setHours(0,0,0,0).getTime();
secondDate.setHours(0,0,0,0).getTime();
console.log(firstDate, secondDate, firstDate === secondDate);

Convert a string in date (so formatted: month day, year hh:mm:ss)

I know there are tons of questions about date formatting, but I'm stuck with a conversion.
I have a string so formatted: mag 11, 2021 2:31:00 pm ("mag" is the abbreviation of May in italian).
I want to convert it in date so I can change it to the format DD/MM/YYYY HH:MM:ss (in this case "11/05/2021 14:31").
I tried to use the new Date or Date.parse functions, but in console it returns me the error 'Invalid date'.
Here's what I tried:
let a = "mag 11, 2021 2:31:00 pm";
let b = new Date(a);
console.log(b);
console output -----> Invalid Date
let a = "mag 11, 2021 2:31:00 pm";
let b = Date.parse(a);
console.log(b);
console output -----> NaN
Any idea? Thx
This question has been answered many times before, the following is for this specific case.
A Date object isn't required, the timestamp can be split into its parts, the month name converted to a number then the parts reformatted, e.g.
/*
mag 11, 2021 2:31:00 pm => DD/MM/YYYY HH:MM:ss
e.g. 11/05/2021 14:31
*/
function reformatDate(date) {
let z = n => ('0'+n).slice(-2);
let months = [,'gen','feb','mar','apr','mag','giu',
'lug','ago','set','ott','nov','dic'];
let [M,D,Y,h,m,s,ap] = date.toLowerCase().split(/\W+/);
h = h%12 + (ap == 'am'? 0 : 12);
M = months.indexOf(M);
return `${z(D)}/${z(M)}/${Y} ${z(h)}:${m}`;
}
console.log(reformatDate('mag 11, 2021 2:31:00 pm'));
In the OP, the format tokens include seconds but the example doesn't. Adding seconds to the above output if required should be easy.
The above can be modified to build the month names array based on a specific language, but then language to use would need to be passed to the function too.
If a library is used to parse the string, the language and format must be specified for the parser (e.g. date-fns allows setting the parse and format language), then the language and format of the output. So unless other date manipulation is required, a library may be more trouble than it's worth.
let now = new Date();
var dateString = moment(now).format('YYYY-MM-DD');
console.log(dateString) // Output: 2020-07-21
var dateStringWithTime = moment(now).format('YYYY-MM-DD HH:MM:SS');
console.log(dateStringWithTime) // Output: 2020-07-21 07:24:06
You can check here for all details of dateTime for Javascript

Comparing two dates in different timezones

I'm comparing two dates; one returned as a UTC String (as part of an Ajax response) and the second in local browser time:
Basically, I want to see if the date returned (endTime) happened before right now. My code is below and I thought I had it right but it's not working.
var isActive = true;
var buffer = 30000; // 30 seconds
var endTime = new Date(Date.parse(response.endTime)); // Fri Oct 23 2015 12:01:14 GMT-0400 (EDT)
var now = new Date(); // Thu Oct 22 2015 20:01:31 GMT-0400 (EDT)
var nowUtc = new Date(now).toUTCString(); // "Fri, 23 Oct 2015 00:01:31 GMT"
var nowTimeMs = new Date(nowUtc).getTime(); // 1445558491000
var endTimeMs = endTime.getTime() + buffer; // 1445616104000
if( nowTimeMs > endTimeMs ){
isActive = false;
}
isActive should remain as true but instead it's false. I feel like I've been looking at this too long and am missing something very simple. Am I?
Thanks for any helpful tips.
Update:
Based on the responses I thought I'd update my question. What is the best way to compare two dates where one is this:
new Date(); // Thu Oct 22 2015 21:51:53 GMT-0400 (EDT)
...and the other is a String representation of date:
"2015-10-23 01:49:27"
I figure the best way to create a valid Date object out of the String is using this code.
isThisActive:function(p){
var isActive = true;
var buffer = 30000;
var pEndTime = myObj.parseStringAsDate(p.callEndTime);
var now = new Date();
var offset = now.getTimezoneOffset() * 60000;
now.setTime( now.getTime() + offset );
var nowTimeMs = now.getTime();
var endTimeMs = pEndTime.getTime() + buffer;
if( nowTimeMs > endTimeMs ){
isActive = false;
}
return isActive;
},
parseStringAsDate:function(str){
var dateTimeStr = str.split(" ");
var dateStr = dateTimeStr[0].split("-");
var year = dateStr[0];
var month = dateStr[1];
var day = dateStr[2];
var timeStr = dateTimeStr[1].split(":");
var hours = timeStr[0];
var minutes = timeStr[1];
var seconds = timeStr[2];
return new Date( year,month,day,hours,minutes,seconds);
}
Because "pEndTime" is in UTC I applied the offset to the "now" Date object but even this is not working. Where's the problem here? I thought this would solve it.
SOLVED:
The latest code I posted did work. I was just getting incorrect values for the response.endTime (It wasn't converted to correct military time). Thank you everyone for your input. I've tried to upgrade as many helpful responses as I could.
You should not use the Date constructor or Date.parse (which do the same thing) to parse date strings. Either write your own parse function (below) or use a well maintained library.
To parse the format in the OP, you can use:
// Parse Thu Oct 22 2015 20:01:31 GMT-0400 (EDT)
function parseMMMDY(s) {
var b = s.split(/\W/);
var months = {jan:0,feb:1,mar:2,apr:3,may:4,jun:5,jul:6,aug:7,sep:8,oct:9,nov:10,dec:11};
var sign = /GMT-\d{4}/i.test(s)? 1 : -1;
var min = +b[5] + (sign * b[8].slice(0,2) * 60 ) + (sign * b[8].slice(-2));
return new Date(Date.UTC(b[3], months[b[1].toLowerCase().slice(0,3)], b[2], b[4], min, b[6]));
}
document.write(parseMMMDY('Thu Oct 22 2015 20:01:31 GMT-0400 (EDT)'));
I think the problem is here:
var endTime = new Date(Date.parse(response.endTime));
respnonse.endTime is UTC, right? But when you parse it to Date value, Date.parse assumes it is in local timezone (GMT-0400 as in your example code). It means that the endDate gets the wrong value
I usually use moment.js in my projects which related to formatting date time, especially in the reports (I'm working in the field of finance). You must have one more library in your project but it provides many other functionalities
Sorry, this is for your new update. I haven't got enough 'population' to leave a comment :P
var endTime = new Date(Date.parse(response.endTime)); // Fri Oct 23 2015 12:01:14 GMT-0400 (EDT)
var now = new Date(); // Thu Oct 22 2015 20:01:31 GMT-0400 (EDT)
Your endTime doesn't seem to return a UTC date as you mentioned. It looks to be using (EDT) so maybe you didn't have to convert it to UTC.

Need clarity on javascript date logic

I am getting false for both conditions
localStorage.getitem("dl-visited-date") // "Mon Oct 07 2013 13:58:18 GMT-0400 (EDT)";
currentDate // Tue Oct 08 2013 14:18:26 GMT-0400 (EDT)
currentDate > localStorage.getItem("dl-visited-date") //false
currentDate < localStorage.getItem("dl-visited-date") //false
localStorage.getitem does return a string (your Date object was implicitly stringified when you stored it in the localstorage). If you compare this with a Date object, both will be casted to numbers, but while this works for the Date object the string will become NaN. And that compares false to anything.
You will need to parse it before (using the Date constructor):
var date = new Date(localStorage.getitem("dl-visited-date")),
currentDate = new Date();
If you want to test them for equality, you will need to use plain numbers instead. Use Date.parse then:
var dateStamp = Date.parse(localStorage.getitem("dl-visited-date")),
currentDateStamp = Date.now();
$(function () {
var dateformate = localStorage.getItem("selectedFormat");
alert(dateformate);
});

How to the get the beginning of day of a date in javascript -- factoring in timezone

I am struggling to find out the beginning of day factoring in timezones in javascript. Consider the following:
var raw_time = new Date(this.created_at);
var offset_time = new Date(raw_hour.getTime() + time_zone_offset_in_ms);
// This resets timezone to server timezone
var offset_day = new Date(offset_time.setHours(0,0,0,0))
// always returns 2011-12-08 05:00:00 UTC, no matter what the offset was!
// This has the same issue:
var another_approach_offset_day = new Date(offset_time.getFullYear(),offset_time.getMonth(),offset_time.getHours())
I expect when i pass a Pacific Timezone offset, to get: 2011-12-08 08:00:00 UTC and so on.
What is the correct way to achieve this?
I think that part of the issue is that setHours method sets the hour (from 0 to 23), according to local time.
Also note that I am using javascript embedded in mongo, so I am unable to use any additional libraries.
Thanks!
Jeez, so this was really hard for me, but here is the final solution that I came up with the following solution. The trick was I need to use setHours or SetUTCHours to get the beginning of a day -- the only choices I have are system time and UTC. So I get the beginning of a UTC day, then add back the offset!
// Goal is given a time and a timezone, find the beginning of day
function(timestamp,selected_timezone_offset) {
var raw_time = new Date(timestamp)
var offset_time = new Date(raw_time.getTime() + selected_timezone_offset);
offset_time.setUTCHours(0,0,0,0);
var beginning_of_day = new Date(offset_time.getTime() - selected_timezone_offset);
return beginning_of_day;
}
In JavaScript all dates are stored as UTC. That is, the serial number returned by date.valueOf() is the number of milliseconds since 1970-01-01 00:00:00 UTC. But, when you examine a date via .toString() or .getHours(), etc., you get the value in local time. That is, the local time of the system running the script. You can get the value in UTC with methods like .toUTCString() or .getUTCHours(), etc.
So, you can't get a date in an arbitrary timezone, it's all UTC (or local). But, of course, you can get a string representation of a date in whatever timezone you like if you know the UTC offset. The easiest way would be to subtract the UTC offset from the date and call .getUTCHours() or .toUTCString() or whatever you need:
var d = new Date();
d.setMinutes(d.getMinutes() - 480); // get pacific standard time
d.toUTCString(); // returns "Fri, 9 Dec 2011 12:56:53 UTC"
Of course, you'll need to ignore that "UTC" at the end if you use .toUTCString(). You could just go:
d.toUTCString().replace(/UTC$/, "PST");
Edit: Don't worry about when timezones overlap date boundaries. If you pass setHours() a negative number, it will subtract those hours from midnight yesterday. Eg:
var d = new Date(2011, 11, 10, 15); // d represents Dec 10, 2011 at 3pm local time
d.setHours(-1); // d represents Dec 9, 2011 at 11pm local time
d.setHours(-24); // d represents Dec 8, 2011 at 12am local time
d.setHours(52); // d represents Dec 10, 2011 at 4am local time
Where does the time_zone_offset_in_ms variable you use come from? Perhaps it is unreliable, and you should be using Date's getTimezoneOffset() method. There is an example at the following URL:
http://www.w3schools.com/jsref/jsref_getTimezoneOffset.asp
If you know the date from a different date string you can do the following:
var currentDate = new Date(this.$picker.data('date'));
var today = new Date();
today.setHours(0, -currentDate.getTimezoneOffset(), 0, 0);
(based on the codebase for a project I did)
var aDate = new Date();
var startOfTheDay = new Date(aDate.getTime() - aDate.getTime() % 86400000)
Will create the beginning of the day, of the day in question
You can make use of Intl.DateTimeFormat. This is also how luxon handles timezones.
The code below can convert any date with any timezone to its beginging/end of the time.
const beginingOfDay = (options = {}) => {
const { date = new Date(), timeZone } = options;
const parts = Intl.DateTimeFormat("en-US", {
timeZone,
hourCycle: "h23",
hour: "numeric",
minute: "numeric",
second: "numeric",
}).formatToParts(date);
const hour = parseInt(parts.find((i) => i.type === "hour").value);
const minute = parseInt(parts.find((i) => i.type === "minute").value);
const second = parseInt(parts.find((i) => i.type === "second").value);
return new Date(
1000 *
Math.floor(
(date - hour * 3600000 - minute * 60000 - second * 1000) / 1000
)
);
};
const endOfDay = (...args) =>
new Date(beginingOfDay(...args).getTime() + 86399999);
const beginingOfYear = () => {};
console.log(beginingOfDay({ timeZone: "GMT" }));
console.log(endOfDay({ timeZone: "GMT" }));
console.log(beginingOfDay({ timeZone: "Asia/Tokyo" }));
console.log(endOfDay({ timeZone: "Asia/Tokyo" }));

Categories