NAME

DateTime::Lite::TimeZone - Lightweight timezone support for DateTime::Lite

SYNOPSIS

use DateTime::Lite::TimeZone;

my $tz = DateTime::Lite::TimeZone->new( name => 'Asia/Tokyo' ) ||
    die( DateTime::Lite::TimeZone->error );

my $dt = DateTime::Lite->now( time_zone => $tz );

# Alias
my $tz2 = DateTime::Lite::TimeZone->new( name => 'US/Eastern' );

# Fixed offset
my $tz3 = DateTime::Lite::TimeZone->new( name => '+09:00' );

# Special zones
my $utc  = DateTime::Lite::TimeZone->new( name => 'UTC' );
my $flt  = DateTime::Lite::TimeZone->new( name => 'floating' );

# Single-argument shorthand
my $tz4 = DateTime::Lite::TimeZone->new( 'Europe/Paris' );

# Memory cache (three-layer: object + span + POSIX footer)
# Enable once at application start-up for best performance:
DateTime::Lite::TimeZone->enable_mem_cache;

# Or per-call:
my $tz5 = DateTime::Lite::TimeZone->new(
    name          => 'America/New_York',
    use_cache_mem => 1,
);

DateTime::Lite::TimeZone->disable_mem_cache;  # disables and clears
DateTime::Lite::TimeZone->clear_mem_cache;    # clears without disabling

# Offset and DST queries
use DateTime::Lite;
my $dt = DateTime::Lite->now( time_zone => $tz );

my $offset_secs = $tz->offset_for_datetime( $dt );        # e.g. -18000
my $local_off   = $tz->offset_for_local_datetime( $dt );  # from wall-clock time
my $is_dst      = $tz->is_dst_for_datetime( $dt );        # 1 or 0
my $abbr        = $tz->short_name_for_datetime( $dt );    # e.g. "EDT"

printf "%s", $tz->offset_as_string( $offset_secs );       # "-0500"
printf "%s", $tz->offset_as_string( $offset_secs, ':' );  # "-05:00"

# Parse an offset string to seconds:
my $secs = DateTime::Lite::TimeZone->offset_as_seconds( '-05:00' );  # -18000

# Zone metadata
$tz->name;             # canonical name, e.g. "America/New_York"
$tz->is_olson;         # 1 if an IANA named zone
$tz->is_utc;           # 1 if UTC
$tz->is_floating;      # 1 if floating
$tz->has_dst;          # 1 if zone ever observes DST
$tz->country_codes;    # arrayref of ISO 3166-1 alpha-2 codes, e.g. ['US']
$tz->countries;        # arrayref of hashrefs with full country data
$tz->coordinates;      # e.g. "+404251-0740023"
$tz->comment;          # free-text annotation from IANA data
$tz->latitude;
$tz->longitude;
$tz->tz_version;       # IANA release string, e.g. "2026a"
$tz->tzif_version;     # TZif binary format version (1, 2, 3, or 4)
$tz->footer_tz_string; # POSIX TZ string for recurring DST rules
$tz->transition_count;
$tz->type_count;
$tz->leap_count;

# Zone discovery
my $all      = DateTime::Lite::TimeZone->all_names;  # array reference
my @all      = DateTime::Lite::TimeZone->all_names;
my $cats     = DateTime::Lite::TimeZone->categories; # array reference
my @cats     = DateTime::Lite::TimeZone->categories;
my $in_cat   = DateTime::Lite::TimeZone->names_in_category('America');  # array reference
my @in_cat   = DateTime::Lite::TimeZone->names_in_category('America');
my $in_cc    = DateTime::Lite::TimeZone->names_in_country('JP');        # array reference
my @in_cc    = DateTime::Lite::TimeZone->names_in_country('JP');
my $is_valid = DateTime::Lite::TimeZone->is_valid_name('Asia/Tokyo');  # 1

my $aliases  = DateTime::Lite::TimeZone->aliases;   # hashref alias => canonical
my %aliases  = DateTime::Lite::TimeZone->aliases;   # hash    alias => canonical
my $links    = $tz->links;                          # arrayref of alias names

# Database access (low-level)
my $path = DateTime::Lite::TimeZone->datafile;  # path to tz.sqlite3
# Raw SQLite queries via public view methods (return DBI statement handles):
#   $tz->zones, $tz->spans, $tz->transition, $tz->types,
#   $tz->aliases, $tz->countries, $tz->leap_second, $tz->metadata

# Error handling
my $bad = DateTime::Lite::TimeZone->new( name => 'Mars/Olympus' );
if( !defined( $bad ) )
{
    warn DateTime::Lite::TimeZone->error;  # "Unknown time zone 'Mars/Olympus'"
}
$tz->fatal(1);  # make errors die instead of warn+return undef

# Object context is detected even in errors, but allows the chain to unfold until the end to avoid the typical: "Can't call method "%s" on an undefined value"
# See https://perldoc.perl.org/perldiag#Can't-call-method-%22%25s%22-on-an-undefined-value
my $bad = DateTime::Lite::TimeZone->new( name => 'Mars/Olympus' )->name;

DESCRIPTION

DateTime::Lite::TimeZone is a drop-in replacement for DateTime::TimeZone designed to eliminate its heavy dependency and memory footprint.

DateTime::TimeZone loads 85 modules at startup, including the entire Specio, Params::ValidationCompiler, and Exception::Class stacks, simply to validate constructor arguments. DateTime::Lite::TimeZone replaces all of that with a single DBD::SQLite query against a compact bundled database (tz.sqlite3).

You may also be interested in the Unicode CLDR (Common Locale Data Repository) with the module Locale::Unicode::Data, which provides richer timezone information, such as metazones, regions, and historical timezone data.

For example:

my $cldr = Locale::Unicode::Data->new;
my $ref  = $cldr->timezone( timezone => 'Asia/Tokyo' );

This would return an hash reference with the following information:

{
   timezone_id => 281,
   timezone    => 'Asia/Tokyo',
   territory   => 'JP',
   region      => 'Asia',
   tzid        => 'japa',
   metazone    => 'Japan',
   tz_bcpid    => 'jptyo',
   is_golden   => 1,
   is_primary  => 0,
   is_preferred => 0,
   is_canonical => 0,
}

You can also returns all the timezones for a country code:

my $array_ref = $cldr->timezones( territory => 'US' );

Would return 55 results, such as:

{
    alias => [qw( America/Atka US/Aleutian )],
    is_canonical => 1,
    is_golden => 1,
    is_preferred => 0,
    is_primary => 0,
    metazone => "Hawaii_Aleutian",
    region => "America",
    territory => "US",
    timezone => "America/Adak",
    timezone_id => 55,
    tz_bcpid => "usadk",
    tzid => "haal",
}

You can also get the localised city name for a time zone:

my $ref = $cldr->timezone_city(
    locale   => 'de',
    timezone => 'Asia/Tokyo',
);

which would return:

{
   tz_city_id  => 7486,
   locale      => 'de',
   timezone    => 'Asia/Tokyo',
   city        => 'Tokio',
   alt         => undef,
}

And if you want to access historical information:

my $ref = $cldr->timezone_info(
    timezone    => 'Europe/Simferopol',
    start       => '1994-04-30T21:00:00',
);

which would return:

{
   tzinfo_id   => 594,
   timezone    => 'Europe/Simferopol',
   metazone    => 'Moscow',
   start       => '1994-04-30T21:00:00',
   until       => '1997-03-30T01:00:00',
}

or, maybe:

my $ref = $cldr->timezone_info(
    timezone    => 'Europe/Simferopol',
    start       => ['>1992-01-01', '<1995-01-01'],
);

This is handy if you do not know the exact date, and want to provide a range instead.

Database schema

The bundled tz.sqlite3 uses the following main tables:

aliases

Alias-to-zone_id FK mappings (such as US/Eastern to America/New_York)

metadata

Key/value pairs including the tzdata version

spans

Pre-computed time spans derived from transitions and types, indexed for fast range lookup

types

Local time type records from the TZif files

zones

Canonical IANA zone names with country codes and coordinates

Fallback mode

If DBD::SQLite is not available, or the bundled tz.sqlite3 cannot be found, DateTime::Lite::TimeZone falls back transparently to DateTime::TimeZone and emits a one-time warning, if warning is permitted.

If DateTime::TimeZone is not available, then it dies.

CONSTRUCTOR

new

my $zone = DateTime::Lite::TimeZone->new( 'Asia/Tokyo' );
my $zone = DateTime::Lite::TimeZone->new(
    name  => 'Asia/Tokyo',
    fatal => 1, # Makes all error fatal
);

A new DateTime::Lite::TimeZone object can be instantiated by either passing the timezone as a single argument, or as an hash, such as name => 'Asia/Tokyo'

Recognised forms:

Named IANA timezones such as America/New_York, Europe/Paris.
Aliases such as US/Eastern, Japan.
Fixed-offset strings such as +09:00, -0500.
The special names UTC and floating.

An optional use_cache_mem => 1 argument activates the process-level memory cache for this call. When set, subsequent calls with the same zone name (or its alias) return the cached object without a database query. See "MEMORY CACHE" for details and for the class-level "enable_mem_cache" alternative.

# Each of these hits the cache after the first construction:
my $tz = DateTime::Lite::TimeZone->new(
    name          => 'America/New_York',
    use_cache_mem => 1,
);

Returns the new object on success. On error, sets the exception object with error() and returns undef in scalar context, or an empty list in list context. In method-chaining (object) context, returns a DateTime::Lite::NullObject to avoid the error Can't call method '%s' on an undefined value. At the end of the chain, undef or an empty list will still be returned though.

MEMORY CACHE

By default, each call to "new" constructs a fresh object with a SQLite query. For applications that construct DateTime::Lite::TimeZone objects repeatedly with the same zone name, a three-layer cache is available.

Layer 1 - Object cache: When enabled, the second and subsequent calls for the same zone name return the original object directly from a hash, bypassing the database entirely.

Layer 2 - Span cache: Each cached TimeZone object stores the last matched UTC and local time span. Calls to offset_for_datetime and offset_for_local_datetime skip the SQLite query when the timestamp falls within the cached span's [utc_start, utc_end) or [local_start, local_end) range.

Layer 3 - POSIX footer cache: For zones where current dates are governed by a recurring DST rule (POSIX TZ footer string), the result of the footer calculation is cached by calendar day. DST transitions happen twice a year; on all other days the cached result is returned without re-evaluating the rule.

Together these three layers reduce the per-call cost of DateTime::Lite->new( time_zone => 'America/New_York' ) from ~430 µs to ~25 µs, putting it on par with DateTime.

Cache entries are keyed by the name passed to "new", plus the canonical name (after alias resolution). Both US/Eastern and America/New_York therefore map to the same cached object.

Cached objects are immutable in normal use. All public accessors are read-only, so sharing an object across callers is safe.

enable_mem_cache

Class method. Activates the memory cache for all subsequent "new" calls.

DateTime::Lite::TimeZone->enable_mem_cache;

# Every new() call now hits the cache after the first construction:
my $tz = DateTime::Lite::TimeZone->new( name => 'America/New_York' );
my $tz2 = DateTime::Lite::TimeZone->new( name => 'America/New_York' );
# $tz and $tz2 are the same object

Equivalent to passing use_cache_mem => 1 on every "new" call, but more convenient when you want the cache active for the lifetime of the process. Returns the class name to allow chaining.

disable_mem_cache

Class method. Disables the memory cache and clears all cached entries. Subsequent "new" calls will construct fresh objects.

DateTime::Lite::TimeZone->disable_mem_cache;

Returns the class name.

clear_mem_cache

Class method. Empties the cache without disabling it. The next "new" call for any zone name will re-query the database and re-populate the cache.

Useful if the tz.sqlite3 database has been replaced at runtime (an unusual operation):

DateTime::Lite::TimeZone->clear_mem_cache;

Returns the class name.

METHODS

aliases

# Checking for errors too
my $aliases  = DateTime::Lite::TimeZone->aliases ||
    die( DateTime::Lite::TimeZone->error );
my( %aliases ) = DateTime::Lite::TimeZone->aliases ||
    die( DateTime::Lite::TimeZone->error );
my $aliases    = $zone->aliases ||
    die( $zone->error );
my( %aliases ) = $zone->aliases ||
    die( $zone->error );

This can be called as an instance method, or as a class function.

This returns a hash of all the zones aliases (the old, deprecated names) to their corresponding canonical names.

For example:

Japan -> Asia/Tokyo

In scalar context, it returns an hash reference, and in list context, it returns an hash.

If an error occurred, this sets an exception object, and returns undef in scalar context, and an empty list in list context. The exception object can then be retrieved with "error"

all_names

# Checking for errors too
my $names    = DateTime::Lite::TimeZone->all_names ||
    die( DateTime::Lite::TimeZone->error );
my( @names ) = DateTime::Lite::TimeZone->all_names ||
    die( DateTime::Lite::TimeZone->error );
my $names    = $zone->all_names ||
    die( $zone->error );
my( @names ) = $zone->all_names ||
    die( $zone->error );

This can be called as an instance method, or as a class function.

This returns a list of all the time zone names sorted alphabetically. This list does not include zone alias (a.k.a. "links").

In scalar context, it returns an array reference, and in list context, it returns an array.

If an error occurred, this sets an exception object, and returns undef in scalar context, and an empty list in list context. The exception object can then be retrieved with "error"

categories

# Checking for errors too
my $categories    = DateTime::Lite::TimeZone->categories ||
    die( DateTime::Lite::TimeZone->error );
my( @categories ) = DateTime::Lite::TimeZone->categories ||
    die( DateTime::Lite::TimeZone->error );
my $categories    = $zone->categories ||
    die( $zone->error );
my( @categories ) = $zone->categories ||
    die( $zone->error );

This can be called as an instance method, or as a class function.

This returns a list of all time zone categories. A category is the part, if any, that precedes the forward slash of a zone name. For example, in Asia/Tokyo, the category would be Asia. However, with the special zone Factory, there would not be any category.

In scalar context, it returns an array reference, and in list context, it returns an array.

If an error occurred, this sets an exception object, and returns undef in scalar context, and an empty list in list context. The exception object can then be retrieved with "error"

category

my $zone = DateTime::Lite::TimeZone->new( name => "Asia/Tokyo" );
say $zone->category; # Asia
my $zone = DateTime::Lite::TimeZone->new( name => "UTC" );
say $zone->category; # undef

Returns the part of the time zone name before the first slash, such as Asia in Asia/Tokyo

comment

Returns the optional zone comment from zone1970.tab, such as "Mountain Time - south Idaho and east Oregon".

Returns undef in scalar context, or an empty list in list context if no comment is recorded.

coordinates

Returns the compact coordinate string from zone1970.tab, such as +3518+13942 for Tokyo. Returns undef in scalar context, or an empty list in list context when there are no coordinates, such as UTC, floating, and fixed-offset zones.

country_codes

Returns an arrayref of ISO 3166-1 alpha-2 country codes associated with this timezone, such as ["JP"] or ["US","CA"].

Returns undef in scalar context, or an empty list in list context when the timezone has no countries associated, such as UTC, floating, and fixed-offset zones.

countries

# Checking for errors too
my $countries    = DateTime::Lite::TimeZone->countries ||
    die( DateTime::Lite::TimeZone->error );
my( @countries ) = DateTime::Lite::TimeZone->countries ||
    die( DateTime::Lite::TimeZone->error );
my $countries    = $zone->countries ||
    die( $zone->error );
my( @countries ) = $zone->countries ||
    die( $zone->error );

This can be called as an instance method, or as a class function.

This returns a list of all the ISO 3166 2-letters country codes sorted alphabetically, and in lower-case. Those codes can be used to call "names_in_country".

In scalar context, it returns an array reference, and in list context, it returns an array.

If an error occurred, this sets an exception object, and returns undef in scalar context, and an empty list in list context. The exception object can then be retrieved with "error"

If you want to convert a country to its locale name, you can use the Unicode CLDR database designed specifically for this.

For example, using the locale en:

use Locale::Unicode::Data;
my $cldr = Locale::Unicode::Data->new;
my $ref  = $cldr->territory_l10n( locale => 'en', territory => 'JP', alt => undef );

# Returns an hash reference like this:

{
   terr_l10n_id    => 13385,
   locale          => 'en',
   territory       => 'JP',
   locale_name     => 'Japan',
   alt             => undef,
}

And, if you want to look up the ISO3166 code based on the locale country name, you could do something like this. Here we search for the country code matching アメリカ, which is America in Japanese:

use strict;
use warnings;
use utf8;
use open ':std' => ':utf8';
use Data::Pretty qw( dump );
use Locale::Unicode::Data;
my $cldr = Locale::Unicode::Data->new;
my $all = $cldr->territories_l10n( locale => 'ja' );
foreach my $ref ( @$all )
{
    if( $ref->{locale_name} =~ /アメリカ/ &&
        $ref->{territory} =~ /^[A-Z]{2}$/ ) # Because a territory, in Unicode CLDR, can also be a 3-digits code
    {
        say dump( $ref );
    }
}

which would produce something like this:

{
    alt => undef,
    locale => "ja",
    locale_name => "アメリカ合衆国",
    terr_l10n_id => 26334,
    territory => "US",
}

datafile

Returns the absolute path to the bundled tz.sqlite3 database file.

designation_charcount

Returns the total size of abbreviation string table (in bytes).

This is equivalent to TZif header field charcnt, including trailing NUL bytes.

See rfc9636, section 3.1

error

my $ex = $zone->error;

Returns the last exception object, if any.

fatal

Sets or gets the fatal property for this object.

When enabled, any error will trigger a fatal exception and call "die" in perlfunc

Returns the footer portion of the timezone.

See rfc9636, section 3.3

has_dst

This is an alias for "has_dst_changes"

has_dst_changes

Returns true if the timezone observes daylight saving time transitions.

is_canonical

my $zone = DateTime::Lite::TimeZone->new( 'Japan' );
say $zone->is_canonical; # false
my $zone = DateTime::Lite::TimeZone->new( 'Asia/Tokyo' );
say $zone->is_canonical; # true

Returns true if the timezone name provided is a canonical one, false otherwise.

is_dst_for_datetime( $dt )

Returns true if $dt falls within a DST period for this timezone.

is_floating

Returns true for the special floating timezone.

is_olson

Returns true for IANA/Olson-sourced timezones.

is_utc

Returns true for the UTC timezone and for fixed-offset +0000.

is_valid_name

say DateTime::Lite::TimeZone->is_valid_name( 'Singapore' );  # true
say $zone->is_valid_name( 'Singapore' );                     # true
say DateTime::Lite::TimeZone->is_valid_name( 'Paris' );      # false
say $zone->is_valid_name( 'Paris' );                         # false
say DateTime::Lite::TimeZone->is_valid_name( 'Asia/Seoul' ); # true
say $zone->is_valid_name( 'Asia/Seoul' );                    # true

This takes a canonical timezone or a timezone alias, and returns true if the value provided is valid, or false otherwise.

This sets an exception object, an returns an error only if no value was provided, so you may want to check if the value returned is defined.

Contrary to DateTime::TimeZone, passin a DateTime::TimeZone::Alias does not make that zone valid. This class, adhere strictly to the IANA time zones.

isstd_count

Returns the number of standard time (a.k.a "standard/wall") indicators.

This "must either be zero or equal to "typecnt".

See rfc9636, section 3.1

isut_count

Returns the number of UT/local time indicators.

This "must either be zero or equal to "typecnt".

See rfc9636, section 3.1

latitude

Returns the latitude for this zone, as a real number, if any.

This is an alias for "aliases"

longitude

Returns the longitude for this zone, as a real number, if any.

name

my $zone = DateTime::Lite::TimeZone->new( name => 'Japan' );
say $zone->name; # Asia/Tokyo

Returns the canonical timezone name, such as Asia/Tokyo.

This means that if you provide an alias upon instantiation, it will be resolved, and accessible with this method.

names_in_category

# Checking for errors too
my $names    = DateTime::Lite::TimeZone->names_in_category( 'Asia' ) ||
    die( DateTime::Lite::TimeZone->error );
my( @names ) = DateTime::Lite::TimeZone->names_in_category( 'Asia' ) ||
    die( DateTime::Lite::TimeZone->error );
my $names    = $zone->names_in_category( 'America' ) ||
    die( $zone->error );
my( @names ) = $zone->names_in_category( 'America' ) ||
    die( $zone->error );

This takes a category, which under this class means the left-hand side of the zone name, separated by a forward slash. So, with the example of Asia/Seoul, the category would be Asia.

With this category provided, this returns a list of the name on the left-hand side of the first forward slash.

For example:

For Asia/Taipei, the category would be Asia, and the list would return among the 74 results, the name Taipei.

For the category America, there would be 121 results, and of which Indiana/Vincennes whose full timezone is America/Indiana/Vincennes, would also be returned.

In scalar context, it returns an array reference, and in list context, it returns an array.

If an error occurred, this sets an exception object, and returns undef in scalar context, and an empty list in list context. The exception object can then be retrieved with "error"

names_in_country

# Checking for errors too
my $names    = DateTime::Lite::TimeZone->names_in_country( 'US' ) ||
    die( DateTime::Lite::TimeZone->error );
my( @names ) = DateTime::Lite::TimeZone->names_in_country( 'US' ) ||
    die( DateTime::Lite::TimeZone->error );
my $names    = $zone->names_in_country( 'US' ) ||
    die( $zone->error );
my( @names ) = $zone->names_in_country( 'US' ) ||
    die( $zone->error );

This takes a 2-letter ISO3166 country code, and returns a list of all the time zones associated with it.

This is case insensitive, so a country code provided, such as US or us would be treated equally.

In scalar context, it returns an array reference, and in list context, it returns an array.

If an error occurred, this sets an exception object, and returns undef in scalar context, and an empty list in list context. The exception object can then be retrieved with "error"

The order of the time zones returned is the same ones as set by IANA database.

offset_as_seconds

This takes an offset as a string, such as +09:00, and this returns the number of seconds represented by that offset either as a signed integer.

If no value was provided, or if that value is not comprised in the range -99:59:59 to +99:59:59, or, if the offset string provided does not match any of the following 2 patterns, then this sets an error object, and returns undef in scalar context or an empty list in list context.

The supported offset patterns are (sign defaults to + if absent):

Colon form: [+-]H:MM, [+-]HH:MM, [+-]HH:MM:SS

The regular expression is: \A([+-])?(\d{1,2}):(\d{2})(?::(\d{2}))?\z

Examples: +09:00, -02:00, 9:0:0

Compact form: [+-]HHMM, [+-]HHMMSS

The regular expression is: /\A([+-])?(\d{2})(\d{2})(\d{2})?\z/

Examples: +0900, -0200, 0900, +090000, 090000

The special string "0" (returns 0).

offset_as_string

say DateTime::Lite::TimeZone->offset_as_string(32400);       # +0900
say DateTime::Lite::TimeZone->offset_as_string(32400, ':' ); # +09:00
say $zone->offset_as_string(32400);                          # +0900
say $zone->offset_as_string(32400, ':');                     # +09:00

Class or instance method. This converts a numeric UTC offset in seconds to a formatted string such as +0900 (default) or +09:00 (with ':' as separator).

Drop-in compatible with "offset_as_string" in DateTime::TimeZone.

offset_for_datetime

my $offset = $zone->offset_for_datetime( $dt );

This takes a DateTime::Lite object, and returns the UTC offset in seconds applicable to that object.

Upon error, then this sets an error object, and returns undef in scalar context or an empty list in list context.

offset_for_local_datetime

my $offset = $zone->offset_for_local_datetime( $dt );

This takes a DateTime::Lite object, and returns the UTC offset in seconds given a local (wall-clock) time.

Used internally during timezone conversion.

Upon error, then this sets an error object, and returns undef in scalar context or an empty list in list context.

short_name_for_datetime

say $zone->short_name_for_datetime( $dt );

This takes a DateTime::Lite object, and returns the abbreviated timezone name applicable, such as JST or EDT.

transition_count

Returns the number of transitions record for this timezone.

Equivalent to TZif header field timecnt

See rfc9636, section 3.1

type_count

Returns the number of types for this timezone.

See rfc9636, section 3.1

tz_version

Returns the IANA tzdata version string from the database metadata table, such as 2026a.

tzif_version

Returns the timezone version string from the timezone data.

The possible values are 1, 2, 3 or 4

See rfc9636, section 3.1

ERROR HANDLING

Upon error, this class methods sets an exception object, and return undef in scalar context, and an empty list in list context. The exception is accessible via:

my $err = DateTime::Lite::TimeZone->error;   # class method
my $err = $tz->error;                        # instance method

The exception stringifies to a human-readable message including the source file and line number.

If the instance option fatal has been enabled, then any error triggered will be fatal.

EPOCH CONVENTION

The tz.sqlite3 database stores span boundaries as Unix seconds (seconds since 1970-01-01T00:00:00 UTC), matching the raw values from the TZif binary files. DateTime::Lite uses Rata Die seconds (seconds since 0001-01-01T00:00:00).

The conversion constant is:

UNIX_TO_RD = 62_135_683_200

All lookup methods subtract UNIX_TO_RD from $dt->utc_rd_as_seconds before querying the database. NULL span boundaries represent ±infinity (before the first recorded transition, and after the last).

BUILDING THE DATABASE

The bundled tz.sqlite3 is generated by running:

perl scripts/build_tz_database.pl [--verbose, --debug 3]

This fetches the latest tzcode and tzdata release from IANA, verifies the GPG signature, compiles it with zic(1), and populates the database. Run this script once per tzdata release, then commit the updated lib/DateTime/Lite/tz.sqlite3.

SQL SCHEMA

The SQLite SQL schema is available in the file scripts/cldr-schema.sql

The data are populated into the SQLite database using the script located in scripts/build_tz_database.pl and the data accessible from https://ftp.iana.org/tz/releases

The SQL schema used to create the SQLite database is available in the scripts directory of this distribution in the file tz_schema.sql

The tables used are as follows, in alphabetical order:

aliases

  • alias

    A string field, case insensitive.

  • zone_id

    An integer field.

countries

  • code

    A string field, case insensitive.

  • name

    A string field, case insensitive.

leap_second

  • leap_sec_id

    An integer field.

  • zone_id

    An integer field.

  • leap_index

    An integer field.

  • occurrence_time

    An integer field.

  • correction

    An integer field.

  • is_expiration

    A boolean field.

    Defaults to false

metadata

  • key

    A string field.

  • value

    A string field.

spans

  • span_id

    An integer field.

  • zone_id

    An integer field.

  • type_id

    An integer field.

  • span_index

    An integer field.

  • utc_start

    An integer field.

  • utc_end

    An integer field.

  • local_start

    An integer field.

  • local_end

    An integer field.

  • offset

    An integer field.

  • is_dst

    A boolean field.

    Defaults to false

  • short_name

    A string field, case insensitive.

transition

  • trans_id

    An integer field.

  • zone_id

    An integer field.

  • trans_index

    An integer field.

  • trans_time

    An integer field.

  • type_id

    An integer field.

types

  • type_id

    An integer field.

  • zone_id

    An integer field.

  • type_index

    An integer field.

  • utc_offset

    An integer field.

  • is_dst

    A boolean field.

  • abbreviation

    A string field, case insensitive.

  • designation_index

    An integer field.

  • is_standard_time

    A boolean field.

  • is_ut_time

    A boolean field.

  • is_placeholder

    A boolean field.

    Defaults to false

zones

  • zone_id

    An integer field.

  • name

    A string field, case insensitive.

  • canonical

    A boolean field.

    Defaults to true

  • has_dst

    A boolean field.

    Defaults to false

  • countries

    A string array field.

  • coordinates

    A string field.

  • latitude

    A real field.

  • longitude

    A real field.

  • comment

    A string field.

  • tzif_version

    An integer field.

  • footer_tz_string

    A string field.

  • transition_count

    An integer field.

  • type_count

    An integer field.

  • leap_count

    An integer field.

  • isstd_count

    An integer field.

  • isut_count

    An integer field.

  • designation_charcount

    An integer field.

  • category

    A string field, case insensitive.

  • subregion

    A string field, case insensitive.

  • location

    A string field, case insensitive.

SEE ALSO

DateTime::Lite, DateTime::TimeZone, Locale::Unicode::Data

RFC 9636 (The Time Zone Information Format (TZif)) https://www.rfc-editor.org/rfc/rfc9636

Locale::Unicode::Data for historical data of time zones, metazones, and BCP47 time zones data.

AUTHOR

Jacques Deguest <jack@deguest.jp>

COPYRIGHT & LICENSE

Copyright(c) 2026 DEGUEST Pte. Ltd.

All rights reserved

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.