NAME

Time::Str::Util - Binary search and time zone database utilities

SYNOPSIS

use Time::Str::Util qw( find_tzdb_directory
                        valid_tzdb_timezone valid_posix_timezone
                        lower_bound upper_bound range_bounds );

# Locate system zoneinfo directory
my $tzdir = find_tzdb_directory();  # /usr/share/zoneinfo or undef

# Validate timezone names
valid_tzdb_timezone('America/New_York');           # true
valid_posix_timezone('EST5EDT,M3.2.0,M11.1.0');   # true

my @sorted = (10, 20, 30, 40, 50);

my $i = lower_bound(\@sorted, 25);  # 2 (first element >= 25)
my $j = upper_bound(\@sorted, 30);  # 3 (first element >  30)

# With optional bounds
my $k = lower_bound(\@sorted, 25, 1, 4);  # search within [1, 4)

# Range query: indices of elements in [15, 35]
my ($lo, $hi) = range_bounds(\@sorted, 15, 35);  # (1, 3)

DESCRIPTION

This module provides binary search functions for sorted integer arrays and utilities for locating the system's IANA Time Zone Database. All functions are exportable on request. Use :all to import everything.

FUNCTIONS

lower_bound

my $index = lower_bound($arrayref, $value);
my $index = lower_bound($arrayref, $value, $lo, $hi);

Returns the index of the first element in the sorted array that is greater than or equal to $value. If all elements are less than $value, returns the length of the array (one past the last index).

Optional $lo and $hi parameters restrict the search to the half-open range [$lo, $hi). Defaults to [0, length).

range_bounds

my ($lo, $hi) = range_bounds($arrayref, $min_value, $max_value);

Returns a pair of indices ($lo, $hi) defining the half-open range [$lo, $hi) of elements within [$min_value, $max_value]. $lo is the index of the first element >= $min_value (via binary search), and $hi is the index of the first element > $max_value (via linear scan from $lo).

This is optimized for cases where few elements fall within the range, as the linear scan avoids a second binary search.

Croaks if $min_value > $max_value.

upper_bound

my $index = upper_bound($arrayref, $value);
my $index = upper_bound($arrayref, $value, $lo, $hi);

Returns the index of the first element in the sorted array that is strictly greater than $value. If all elements are less than or equal to $value, returns the length of the array.

Optional $lo and $hi parameters restrict the search to the half-open range [$lo, $hi). Defaults to [0, length).

Time Zone Database

find_tzdb_directory

my $dir = find_tzdb_directory();

Returns the path to the system's IANA Time Zone Database directory (zoneinfo), or undef if no valid directory is found.

The search order is:

1. $ENV{TZDIR} if set and the directory exists.
2. Well-known paths: /usr/share/zoneinfo, /usr/lib/zoneinfo, /usr/share/lib/zoneinfo, /etc/zoneinfo, /usr/share/zoneinfo.default.

A candidate directory is accepted only if it exists and contains a UTC file.

Validation

valid_tzdb_timezone

my $bool = valid_tzdb_timezone($string);

Returns true if $string is structurally valid as an IANA Time Zone Database timezone name (e.g., America/New_York, Europe/Stockholm, Etc/GMT+5, UTC).

A valid name consists of one or more path components separated by /. Each component starts with a letter, followed by letters or digits, with optional _, +, or - separators introducing additional alphanumeric segments.

This is a structural check only; it does not verify that the timezone exists in the database.

valid_posix_timezone

my $bool = valid_posix_timezone($string);

Returns true if $string is structurally valid as a POSIX TZ string (IEEE Std 1003.1).

A POSIX TZ string has the form:

std offset [dst [offset] , start [/time] , end [/time]]

Examples:

EST5EDT,M3.2.0,M11.1.0          # US Eastern
CET-1CEST,M3.5.0/2,M10.5.0/3   # Central European
UTC0                             # Fixed UTC

Transition rules may use Mm.w.d (month/week/weekday), Jn (Julian day excluding Feb 29), or n (zero-based Julian day including Feb 29) forms.

This validates the syntactic structure of the string only, not the semantic validity of field values (e.g., month ranges, day ranges). Quoted timezone designations (<+05>) as used in TZif extensions are not part of the POSIX standard and are not accepted.

SEE ALSO

Time::Str

AUTHOR

Christian Hansen

COPYRIGHT AND LICENSE

Copyright (C) 2026 by Christian Hansen

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