Home > Back-end >  Given a date, how can I get the previous Monday in UTC format regardless of time zone?
Given a date, how can I get the previous Monday in UTC format regardless of time zone?

Time:06-24

I am given a unix timestamp like this: 1655402413 and am needing to find find the midnight of the Monday (in UTC/GMT format) of the same week, regardless of what day it is or what time zone. I then need to represent that Monday as a unix timestamp and return it. The function I have is as follows:

function findMonday(unixTimeStamp) {
  let startDate = new Date(unixTimeStamp);
  let startDay = startDate.getDay();
  let diff = startDate.getDate() - startDay   (startDay === 0 ? -6 : 1);
  let monday = new Date(startDate.setDate(diff));
  monday.setHours(0, 0, 0, 0);
monday = new Date(monday).valueOf();
  return monday;
}

That function almost works, but there are two problems, both related to the fact that the Date seems to always work with the user's current timezone:

  1. If given a timestamp that evaluates to midnight on a Monday in UTC/GMT format, depending on the time zone of the user, it returns the Monday of the previous week (because startDate evaluates to the Sunday before the Monday), which is not good.
  2. The monday that is returned is in local time, not UTC/GMT time.

This is driving me absolutely insane. Working with dates in JavaScript is a nightmare, and I would appreciate any direction you can give me.

CodePudding user response:

Multiply the unix timestamp by 1000, and use the UTC methods like getUTCDate instead of getDate, setUTCHours instead of setHours etc..

Of course to return as unix time, just divide by 1000.

eg.

function findMonday(unixTimeStamp) {
  let startDate = new Date(unixTimeStamp * 1000);
  let startDay = startDate.getUTCDay();
  let diff = startDate.getUTCDate() - startDay   (startDay === 0 ? -6 : 1);
  let monday = new Date(startDate.setUTCDate(diff));
  monday.setUTCHours(0, 0, 0, 0);
monday = new Date(monday).valueOf();
  return monday;
}

const monday = findMonday(1655402413);
const unixMonday = Math.trunc(monday / 1000);

console.log('The Date: '   new Date(monday).toISOString());

console.log('Unix time: '   unixMonday);

CodePudding user response:

As for Keith's answer but a little more concise. It returns seconds, not milliseconds. ;-)

// Given UNIX timestamp, return similar timestamp for
// previous UTC Monday at 00:00:00
let getLastUTCMonday = ts => {
  let d = new Date(ts * 1e3);
  d.setUTCDate(d.getUTCDate() - (d.getUTCDay() || 7)   1);
  return d.setUTCHours(0,0,0,0) / 1e3 | 0;
};

let ts = 1655402413;
let tsPriorMonday = getLastUTCMonday(ts)

console.log(
  `Start date  : ${new Date(ts*1e3).toUTCString()}\n`  
  `Prior Monday: ${new Date(tsPriorMonday * 1e3).toUTCString()}`
);

In ECMA-262, offsets from the epoch (in milliseconds) are called "time values". A timestamp is anything that represents a time or date, so a time value is a timestamp. ;-)

Given that ECMAScript UTC days are always exactly 8.64e7 milliseconds long, you can work out the previous UTC Monday from today by some simple arithmetic.

The ECMAScript epoch was Thursday, 1 Jan 1970 00:00:00, so you can:

  1. Subtract 4 UTC days worth of milliseconds (34.56e7) from the date to align with Monday instead of Thursday
  2. Get the remainder of dividing that value by the number of milliseconds in 7 UTC days (7 * 8.64e7 or 60.48e7)
  3. Subtract the remainder from the current date, which will return the previous Monday and also remove the time component

The above algorithm only works for dates after the epoch. Dates before then have time values are negative so add 3 days before getting the remainder, then subtract the remainder 7 days (i.e. date - remainder - 7 days).

The following just does the post–epoch calculation:

let getPreviousUTCMonday = date => {
  let weekRem = (date - 34.56e7) % 60.48e7;
  return date - weekRem;
}

let d = new Date();
for (let i=0; i<12; i  ) {
  console.log(`${d.toUTCString()}\n`   
  `${new Date(getPreviousUTCMonday(d)).toUTCString()}`);
  d.setDate(d.getDate()   1);
}

  • Related