2

I have a date with a time, like this

const date = {year: 2020, month: 12, day: 31};
const time = {hours: 16, minutes: 2};

How do I get UTC representation of that time depending on a timezone? (without using any libraries)

convetToUTC(date, time, "Europe/Moscow") // => <UTC timestamp>
convetToUTC(date, time, "America/New_York") // => <UTC timestamp>

Examples

convetToUTC(
  {year: 2021, month: 7, day: 30}, 
  {hours: 16, minutes: 15}, 
  "Europe/Moscow"
) // => 1627650900

convetToUTC(
  {year: 2021, month: 7, day: 30}, 
  {hours: 16, minutes: 15}, 
  "America/New_York"
) // => 1627676100
8
  • Please check this https://stackoverflow.com/questions/9756120/how-do-i-get-a-utc-timestamp-in-javascript
    – Forest 1
    Commented Jul 30, 2021 at 13:30
  • checked, can't see any hints how to do arbitrary timezone conversion
    – achempion
    Commented Jul 30, 2021 at 13:40
  • "How do I get UTC representation of that time depending on a timezone." Does that mean you want to take the specified date and time, treat it as a date & time in the specified timezone and then get the UTC timestamp of that moment?
    – phuzi
    Commented Jul 30, 2021 at 13:41
  • x = new Date() var UTCseconds = (x.getTime() + x.getTimezoneOffset()*60*1000)/1000; console.log("UTCseconds", UTCseconds)
    – Forest 1
    Commented Jul 30, 2021 at 13:44
  • @Forest1 That won't work for anything other than the local timezone. OP wants to do it for arbitrary timezones.
    – phuzi
    Commented Jul 30, 2021 at 13:47

2 Answers 2

1

Piggy-backing on Achempion's response, I fixed the timezone offset calculation. The timezone date should be subtracted from the UTC date. The result of this difference should be in minutes.

You will need to then convert the minute offset back into milliseconds and subtract this from the date.

/**
* Calculates the timezone offset of a particular time zone.
* @param {String} timeZone - a database time zone name
* @param {Date} date - a date for determining if DST is accounted for
* @return {Number} returns an offset in minutes
* @see https://stackoverflow.com/a/68593283/1762224
*/
const getTimeZoneOffset = (timeZone = 'UTC', date = new Date()) => {
  const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
  const tzDate = new Date(date.toLocaleString('en-US', { timeZone }));
  return (tzDate.getTime() - utcDate.getTime()) / 6e4;
}

const defaultDateConfig = { year: 0, month: 0, date: 0 };
const defaultTimeConfig = { hours: 0, minutes: 0, seconds: 0 };

const convetToUTC = (dateConfig, timeConfig, timeZone) => {
  const { year, month, date } = { ...defaultDateConfig, ...dateConfig };
  const { hours, minutes, seconds } = { ...defaultTimeConfig, ...timeConfig };
  const d = new Date(Date.UTC(year, month - 1, date, hours, minutes, seconds));
  const offsetMs = getTimeZoneOffset(timeZone, d) * 6e4;
  return (d.getTime() - offsetMs) / 1e3;
};

// Main
const date = { year: 2021, month: 7, date: 30 };
const time = { hours: 16, minutes: 15 };

console.log(convetToUTC(date, time, 'America/New_York')); // 1627676100
console.log(convetToUTC(date, time, 'Europe/Moscow'));    // 1627650900

6
  • I think using 10**3 or even 1000 will make function simpler than using 1e3
    – achempion
    Commented Jul 30, 2021 at 15:46
  • @achempion 1e3 is actually the same as 1000, it is just scientific notation; whereas 10**3 is a power calculation. The latter is effectively syntactic sugar for Math.pow(10, 3). Commented Jul 30, 2021 at 15:51
  • Actually there is not point of dividing on 6e4 in getTimeZoneOffset and then multiplying it back as you still keep seconds in offset because devision produces float number and you don't round it
    – achempion
    Commented Jul 30, 2021 at 16:03
  • @achempion The getTimeZoneOffset function is standalone from the logic within convetToUTC. If you look at MDN: Date.prototype.getTimezoneOffset(). It states that the result is in minutes. I am sticking with this as a rule of thumb. I updated my code to include some JS Doc for additional info. Commented Jul 30, 2021 at 16:05
  • I agree that you're following the right approach here and that we should return minutes in getTimeZoneOffset function. The issue is that it returns values like this 1627661700.24, where ".24" part at the end meant that it still has seconds part present in here. By "It states that the result is in minutes." I think you meant whole minutes but your function can return 1min 30m seconds as an example.
    – achempion
    Commented Jul 30, 2021 at 18:54
-1
const dateWithTimeZone = (timeZone, year, month, day, hour, minute, second) => {
  let date = new Date(Date.UTC(year, month, day, hour, minute, second));

  let utcDate = new Date(date.toLocaleString('en-US', { timeZone: "UTC" }));
  let tzDate = new Date(date.toLocaleString('en-US', { timeZone: timeZone }));
  let offset = utcDate.getTime() - tzDate.getTime();

  date.setTime( date.getTime() + offset );

  return date;
};

dateWithTimeZone("America/New_York", 2021, 7 - 1, 30, 16, 15, 0).getTime() / 1000)
// => 1627676100

dateWithTimeZone("Europe/Moscow", 2021, 7 - 1, 30, 16, 15, 0).getTime() / 1000)
// => 1627650900

7 - 1 used to illustrate that function accepts month's index, not month's number

1
  • Test with 3:30 am Sun 7 Nov 2021 for New York, then take the result and create a timestamp for New York: new Date(value*1e3).toLocaleString('default',{timeZone:'America/New_York'}). It will be out by an hour.
    – RobG
    Commented Aug 2, 2021 at 1:34

Not the answer you're looking for? Browse other questions tagged or ask your own question.