Search code examples
angulardate-pipe

Dates timezones in Angular


In an Angular app, I have a form and I want the user to be able to create a Date in any timezone. The user inputs:

  • date: 20/09/2021
  • hour: 10:30
  • timezone (select): Europe/Madrid

What I did was saving in a local file the relationship between timezone and its offset (Europe/Madrid -> +0200) and then create the Date using a string, like this: new Date("2021-09-20T10:30:00+0200").

My problem is calculating the summer hours, right now I use static timezones but that is not realistic because some countries change its timezones twice a year. I need an automatic solution.

I've been reading about moment and moment-timezone but both are legacy projects and its use is discouraged, so I am looking for something different. Thank you.


Solution

  • Finally I've found a solution.

    Instead of static timezone offsets I used the IANA code (e.g. Asia/Kabul) so I can calculate its value live (GMT+4:30 -> +0430).

    1 - Get timezone offset from IANA zone identifier

    I used Intl, a native browser class to format dates. Example:

    new Intl.DateTimeFormat("en", {
      timeZone: "Pacific/Palau",
      day: "2-digit",
      month: "2-digit",
      year: "numeric",
      weekday: "long",
      hour: "2-digit",
      minute: "2-digit",
      hour12: false,
      second: "2-digit",
      timeZoneName: "short",
    }).format(new Date());
    
    ("Thursday, 07/15/2021, 17:19:51 PM GMT+9");
    

    With this I could create the Date, but Date constructor will use the local timezone offset and calculate the difference, so the final time in the selected timezone will not be the specified one.

    2 - Get timezone from string

    From Intl returned date string ("Thursday, 07/15/2021, 17:19:51 PM GMT+9") I get the last part (GMT+9) like this:

    const dateSplit = dateStr.split(" ");
    const timezoneAbbr = dateSplit[dateSplit.length - ONE];
    

    3 - Map non standard timezone abbreviations to its offset

    Now, because some countries have not-standard timezone abbreviations (like AST), we need to map time zone abbreviations to UTC before creating the Date object (because Date constructor returns error for some time zone abbreviations). I created this relationship using an enum like this:

    export enum TimezoneAbbreviation {
      ACDT = '+1030',
      ACST = '+0930',
      ACT = '-0500',
      ADT = '-0300',
    

    4 - Create Date

    Finally create date string with format yyyy/MM/dd hh:mm ZZ (e.g.: 2021/05/10 10:30 UTC-1) and call Date constructor with it.

    Warn

    There's no consensus on timezone abbreviations and may vary (they use CLDR format). Also there is confusing data like duplicated abbreviations:

    Abbr Name UTC offset
    AST Arabia Standard Time UTC+03
    AST Atlantic Standard Time UTC−04
    SST Samoa Standard Time UTC−11
    SST Singapore Standard Time UTC+08