Home > Enterprise >  Why does javascript subtract an entire month rather than one day if the time isn't zeroed out?
Why does javascript subtract an entire month rather than one day if the time isn't zeroed out?

Time:02-02

Not looking for solutions, just want to know why I get those results and get a better understanding of javascript.

I'm calculating dates and date strings for today through 3 days ago. If I zero out time for today, everything works just fine. If I don't (and maybe also it's close to midnight UTC) yesterday is a month ago, other previous day calculations work as expected.

Screenshot: screenshot of code run in the console with results

Code run in the console at about 5:30p eastern which is 11:30p UTC

Here's the code so you can run it yourself.

Zero out time and yesterday is yesterday:

let today = new Date();
today.setUTCHours(0,0,0); // remove time, otherwise yesterday if based off of today could be a month ago.
let todays_date_string = today.toISOString().slice(0, 10);

let yesterday = new Date(todays_date_string);
yesterday.setDate(today.getDate() - 1);
let yesterdays_date_string = yesterday.toISOString().slice(0, 10);

let two_days_ago = new Date(yesterdays_date_string);
two_days_ago.setDate(two_days_ago.getDate() - 1);
let two_days_ago_date_string = two_days_ago.toISOString().slice(0, 10);

let three_days_ago = new Date(two_days_ago_date_string);
three_days_ago.setDate(three_days_ago.getDate() - 1);
let three_days_ago_date_string = three_days_ago.toISOString().slice(0, 10);
console.log({today, today_s: today.toISOString(), todays_date_string, yesterdays_date_string, two_days_ago_date_string, three_days_ago_date_string});

Don't zero out time and yesterday is a month ago:

let today = new Date();
//today.setUTCHours(0,0,0); // remove time, otherwise yesterday if based off of today could be a month ago.
let todays_date_string = today.toISOString().slice(0, 10);

let yesterday = new Date(todays_date_string);
yesterday.setDate(today.getDate() - 1);
let yesterdays_date_string = yesterday.toISOString().slice(0, 10);

let two_days_ago = new Date(yesterdays_date_string);
two_days_ago.setDate(two_days_ago.getDate() - 1);
let two_days_ago_date_string = two_days_ago.toISOString().slice(0, 10);

let three_days_ago = new Date(two_days_ago_date_string);
three_days_ago.setDate(three_days_ago.getDate() - 1);
let three_days_ago_date_string = three_days_ago.toISOString().slice(0, 10);
console.log({today, today_s: today.toISOString(), todays_date_string, yesterdays_date_string, two_days_ago_date_string, three_days_ago_date_string});

Also note that I'm using today to calculate yesterday's date rather than the newly created yesterday based off today's date string which could be a contributing factor. But I wouldn't think that would affect calculating yesterday and certainly not to that degree.

Is there an explanation for this?

CodePudding user response:

The key point is this: The setDate() method changes the day of the month of a given Date instance, based on local time.

Let's walk through some code.

const date = new Date('2023-02-01T22:34:47.458Z');
const todayDateString = date.toISOString().slice(0, 10); // 2023-02-01
const todayDateDate = new Date(todayDateString);

The value of todayDateDate.toISOString() is 2023-02-01T00:00:00.000Z

The value of date.getDate() is 1.

The value of todayDateDate.toString() is Tue Jan 31 2023 19:00:00 GMT-0500 (Eastern Standard Time) (this will vary depending on your time zone).

The local date is January 31!

If you setDate(0), it gives you the last day of the previous month, based on local time. Since the local time is in January, this means December 31 in local time, and will be January 1 UTC.

The exact results depend on your local time zone.

CodePudding user response:

When you construct the new date that doesn't explicitly specify a time zone, e.g. new Date('2023-02-01'), the date is interpreted in your local time zone.

But when you stringify it via toISOString() you get the date in the UTC time zone, which could be a different day (the other side of midnight).

Consider:

// February 1 in UTC 05:00
const d1 = new Date('2023-02-01T01:00:00 05:00')

// is still January 31 in UTC
console.log(d1.toISOString()); // 2023-01-31T20:00:00.000Z

So if

  1. your timezone offset means it's still yesterday in UTC, and
  2. yesterday was still January 31 instead of February 1, then
  3. setDate(1) makes it January 1: a month ago.
  • Related