Search code examples
perldatetimegeolocationtimezone

Take Longitude/Latitude and get UTC Offset in Perl


I'm trying to localize times that are in UTC when the only thing I know about the destination time is the longitude and latitude. I've come up with something that works, but feels kludgy:

# We need to get localized time for display purposes.
state $moduleGeoLocation = require Geo::Location::TimeZone;
my $gltzobj = Geo::Location::TimeZone->new();
my $tzName = $gltzobj->lookup( lon => $params->{'longitude'}, lat => $params->{'latitude'} );
say "TimeZone: " . $tzName;

So far so good. Here's where the kludge comes in. I'm parsing a time using Time::Piece's strptime but I don't have a GMT offset for the timezone, so after I parse the UTC time in Time::Piece, I'm sending it over to DateTime to do the time zone calculation. It seems rather clunky to be using both DateTime and Time::Piece:

# Get TimeZoneOffset.
state $moduleDateTime = require DateTime;
state $moduleDateTimeTimeZone = require DateTime::TimeZone;
my $timeZone = DateTime::TimeZone->new( 'name' => $tzName );

my $timePiece = Time::Piece->strptime($hour->{'time'}, '%Y-%m-%dT%H:%M:%SZ');
my $time = DateTime->from_epoch( 'epoch' => $timePiece->epoch, 'time_zone' => $timeZone );

Is there a better way to accomplish what I'm doing? I'm aiming for the fastest possible way to get to the result of a localized time.


Solution

  • Your question boils down to the following:

    How do I create a DateTime object from a timestamp using the %Y-%m-%dT%H:%M:%S format. I have the appropriate time zone as a DateTime::TimeZone object.

    To parse a date-time into a DateTime, one should first look for an appropriate DateTime::Format:: module.

    DateTime::Format::Strptime would be the most similar to your current attempt.

    use DateTime::Format::Strptime qw( );
     
    my $format = DateTime::Format::Strptime->new(
       pattern   => '%Y-%m-%dT%H:%M:%S',
       strict    => 1,
       time_zone => $tz,
       on_error  => 'croak',
    );
    
    my $dt = $format->parse_datetime($ts);
    

    You could also use DateTime::Format::ISO8601, although you couldn't use it as a validator since it doesn't accept only the stated format.

    use DateTime::Format::ISO8601 qw( );
     
    ( my $dt = DateTime::Format::ISO8601->parse_datetime($ts) )
       ->set_time_zone('floating')
       ->set_time_zone($tz);
    

    Given that the latter solution overrides any explicitly-provided time zone, I'd use the first solution for clarity.