Search code examples
perldatetimetimezoneiso8601

Normalize ISO 8601 datetime to any time zone with support of non-standard time zone abbreviations


Having time in ISO 8601 format (e.g. 2019-09-17T16:15:20Z), how can I convert/normalize this time from one time zone to another time zone (such as ET = US Eastern Time, CT = US Central Time, PT = US Pacific Time) ?

Desired solution should accept any time zone abbreviation, standard and non-standard abbreviations.


Perl subroutine

sub normalizeDateTime
{
  ... # ???
}

print normalizeDateTime('2019-09-17T16:15:20Z', 'ET');

Solution

  • We can use DateTime based libraries

    use DateTime::TimeZone;
    use DateTime::Format::ISO8601;
    use DateTime::TimeZone::Alias;
    

    and set desired non-standard abbreviations as aliases,

    DateTime::TimeZone::Alias->set('ET' => 'America/New_York');
    DateTime::TimeZone::Alias->set('CT' => 'America/Chicago');
    DateTime::TimeZone::Alias->set('PT' => 'America/Los_Angeles');
    

    sub normalizeDateTime
    {
      my $dt = DateTime::Format::ISO8601
                 ->new()
                 ->parse_datetime($_[0])
                 ->set_time_zone($_ = DateTime::TimeZone->new(name => $_[1]));
    
      $dt . DateTime::TimeZone::offset_as_string($_->offset_for_datetime($dt))
              =~ s/^[+-]00:?00$/Z/r
              =~ s/^([+-]\d{2})(\d{2})$/$1:$2/r;
    }
    

    so then we can use such time zone names directly as valid time zones:

    print normalizeDateTime('2019-09-17T16:15:20Z', 'ET');
    

    2019-09-17T12:15:20-04:00