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
achempion
  • 794
  • 6
  • 17
  • Please check this `https://stackoverflow.com/questions/9756120/how-do-i-get-a-utc-timestamp-in-javascript` – Forest 1 Jul 30 '21 at 13:30
  • checked, can't see any hints how to do arbitrary timezone conversion – achempion Jul 30 '21 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 Jul 30 '21 at 13:41
  • x = new Date() var UTCseconds = (x.getTime() + x.getTimezoneOffset()*60*1000)/1000; console.log("UTCseconds", UTCseconds) – Forest 1 Jul 30 '21 at 13:44
  • @Forest1 That won't work for anything other than the local timezone. OP wants to do it for arbitrary timezones. – phuzi Jul 30 '21 at 13:47
  • Does this answer your question? [Get epoch for a specific date using Javascript](https://stackoverflow.com/questions/3367415/get-epoch-for-a-specific-date-using-javascript) – nbppp2 Jul 30 '21 at 13:58
  • @nbppp2 No, the `new Date()` does not accept timezone as an argument – achempion Jul 30 '21 at 14:13
  • Probably a duplicate of [*Calculate Timezone offset only for one particular timezone*](https://stackoverflow.com/questions/61361914/calculate-timezone-offset-only-for-one-particular-timezone). – RobG Jul 31 '21 at 01:33

2 Answers2

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
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • I think using 10**3 or even 1000 will make function simpler than using 1e3 – achempion Jul 30 '21 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)`. – Mr. Polywhirl Jul 30 '21 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 Jul 30 '21 at 16:03
  • @achempion The `getTimeZoneOffset` function is standalone from the logic within `convetToUTC`. If you look at [MDN: Date.prototype.getTimezoneOffset()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/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. – Mr. Polywhirl Jul 30 '21 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 Jul 30 '21 at 18:54
  • This approach fails when applied to a place that observes daylight saving as the time may either not exist in that place when going into DST, or exist twice when coming out. E.g. for New York, entering values for 3:30 am Sun 7 Nov 2021 returns a value of 1636270200, which when converted to a Date and using *toLocaleString* for New York returns a time of 02:30, so it's an hour out. See [*this answer*](https://stackoverflow.com/a/61364310/257182). – RobG Aug 01 '21 at 02:08
-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

achempion
  • 794
  • 6
  • 17
  • 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 Aug 02 '21 at 01:34