Search code examples
javascriptdatetimetimestampgoogle-tag-manager

Convert UTC timestamp to other timezone in ISO format


I am trying to convert a UTC timestamp to an ISO-formatted timestamp in German time.

Input: 2022-04-30T17:30:00.000000Z

Expected Output: 2022-04-30T19:30:00

This is my current, working solution but it feels very... hacky... to say the least. For one, I am afraid that many browsers might not be able to correctly convert the date to the german timezone.

var inputDateString = "2022-04-30T17:30:00.000000Z";
// Convert to German timezone and formatted as iso date
var output = toIsoString(convertTZ(inputDateString, "Europe/Berlin")).split("+")[0];

console.log("inputDateString:", inputDateString)
console.log("output:", output)

// Ref.: https://stackoverflow.com/a/54127122
function convertTZ(date, tzString) {
  return new Date((typeof date === "string" ? new Date(date) : date).toLocaleString("en-US", { timeZone: tzString }));
}

// Ref.: https://stackoverflow.com/a/17415677
function toIsoString(date) {
  var tzo = -date.getTimezoneOffset(),
    dif = tzo >= 0 ? "+" : "-",
    pad = function (num) {
      return (num < 10 ? "0" : "") + num;
    };

  return date.getFullYear() + "-" + pad(date.getMonth() + 1) + "-" + pad(date.getDate()) + "T" + pad(date.getHours()) + ":" + pad(date.getMinutes()) + ":" + pad(date.getSeconds()) + dif + pad(Math.floor(Math.abs(tzo) / 60)) + ":" + pad(Math.abs(tzo) % 60);
}

Do you know a better/improved solution to this problem that is compatible with most browsers? I feel like JavaScript must offer a better solution for this task without having to rely on external libraries...

Edit: As an alternative to the toIsoString function, I have also found this: .toLocaleString("sv-SE") but I assume that this has even worse browser support.


Solution

  • If you don't need to guarantee the format of the resulting timestamp, you can use toLocaleDateString plus toLocaleTimeString with a suitable language tag, e.g.

    function getISOLocal(loc, date = new Date()) {
      return `${date.toLocaleDateString('en-CA', {timeZone: loc})}` +
             `T${date.toLocaleTimeString('en', {timeZone: loc, hour12:false})}`;
    }
    
    console.log('Current local time in:');
    ['Pacific/Midway',
     'America/New_York',
     'Europe/Berlin',
     'Pacific/Kiritimati'].forEach(
       loc => console.log(`${loc} : ${getISOLocal(loc)}`)
    );

    But be aware that the format of toLocale* methods is implementation dependent and may vary between implementations, particularly for less common languages and combinations of language, options and host default language.

    A better alternative is to use Intl.DateTimeFormat with formatToParts, e.g.

    function getISOLocal(loc, date = new Date()) {
      let {year, month, day, hour, minute, second} = 
        new Intl.DateTimeFormat('en', {
          year: 'numeric',
          month: '2-digit',
          day: '2-digit',
          hour: '2-digit',
          minute: '2-digit',
          second: '2-digit',
          hour12: false,
          timeZone: loc
        }).formatToParts(date).reduce((acc, part) => {
          acc[part.type] = part.value;
          return acc;
        }, Object.create(null));
        return `${year}-${month}-${day}T${hour}:${minute}:${second}`
    }
    
    console.log('Current local time in:');
    ['Pacific/Midway',
     'America/New_York',
     'Europe/Berlin',
     'Pacific/Kiritimati'].forEach( 
       loc => console.log(`${loc} : ${getISOLocal(loc)}`)
    );

    The timeZone option isn't supported in IE, but maybe that doesn't matter.