Modules · Time · 0.10
Time::Keeper
Dates and times with time zones, leap seconds, locales and every CLDR calendar.
What Time::Piece and DateTime do between them, in XS. A value is one instant and a zone, it never changes, and with no zone it is UTC. Zones come from the IANA database, names and formats from every CLDR locale, and the same instant can be read in any of CLDR's calendars.
use Time::Keeper;
binmode STDOUT, ':utf8';
my $t = Time::Keeper->new(
year => 2026, month => 10, day => 15, hour => 9, zone => 'Europe/London',
);
print "$t\n"; # 2026-10-15T09:00:00+01:00
print $t->in_zone('Asia/Tokyo'), "\n"; # the same instant: 17:00+09:00
print $t->zone_abbr, "\n"; # BST
# values never change: plus returns a new one, months before days
my $later = $t->plus(months => 1, days => 2);
print $later->format_date('long'), "\n"; # November 17, 2026
# the locale's names, formats and digits
print $t->in_locale('fr')->format_date('full'), "\n"; # jeudi 15 octobre 2026
print $t->in_locale('ar-EG')->format_date('long'), "\n"; # ١٥ أكتوبر ٢٠٢٦
print $t->format_relative(to => $later), "\n"; # last month
What you get
-
Values that never change
with,plus,in_zoneandtruncatereturn a new value. Arithmetic adds months before days, so$b->minus($a)adds back to exactly$b. -
Zones, gaps and overlaps
IANA zones by name, offsets, floating time and the machine's own. A time that never happened is refused unless you say
gap => 'forward', and of two that did, the earlier is taken. -
Leap seconds
23:59:60 on the 27 days that had one, in any zone, and refused on any other. Nanoseconds, and years from -999,999,999 to 999,999,999.
-
Every CLDR locale
Month and day names, formats by skeleton, relative times, intervals, zone names, weekends and the first day of the week, and the locale's own digits, from CLDR 48.2.3.
-
Every CLDR calendar
in_calendar('hebrew')reads the same instant in another calendar: Hebrew, Islamic, Chinese, Japanese, Persian, Ethiopic and the rest, with eras, leap months and month codes. -
Parse what people write
parsereads ISO 8601, RFC 3339 and 2822, HTTP dates and more.strptimeandparse_cldrread a pattern, and the parsers read any script's digits.
In use
use Time::Keeper;
# the same instant, read in other calendars
my $t = Time::Keeper->parse('2026-10-15T09:00:00Z');
for my $cal (qw(hebrew islamic-umalqura chinese japanese)) {
my $c = $t->in_calendar($cal);
printf "%-17s %s\n", $cal, $c->format_date('long');
}
# hebrew 4 Heshvan 5787
# islamic-umalqura Jumada I 4, 1448 AH
# chinese Ninth Month 6, 2026(bing-wu)
# japanese October 15, 8 Reiwa
# a leap second is a real second, and only where there was one
my $leap = Time::Keeper->new(year => 2016, month => 12, day => 31,
hour => 23, minute => 59, second => 60);
print "$leap\n"; # 2016-12-31T23:59:60Z
print eval { Time::Keeper->new(year => 2026, month => 1, day => 1, second => 60) }
? '' : "no leap second then\n";
# a wall-clock time that never happened is refused, unless you say what you meant
my %gap = (year => 2026, month => 3, day => 29, hour => 1, minute => 30, zone => 'Europe/London');
print eval { Time::Keeper->new(%gap) } ? '' : "01:30 did not happen in London\n";
print Time::Keeper->new(%gap, gap => 'forward'), "\n"; # 2026-03-29T02:30:00+01:00
The rest of the family
Each is built on the core's C ABI. Time::Keeper::Zone holds the IANA zones. Time::Keeper::Recurrence walks RRULE, cron and named rules in any zone. Time::Keeper::Business counts business days, hours and SLAs. Time::Keeper::Natural reads the words people use for a time, in every locale. Time::Keeper::Sky is the sky from a place: the sun and the moon, the planets, the bright stars and eclipses, where they are and when they rise.
For C
time_keeper_abi.h, found through ExtUtils::Depends and resolved at run time: another extension reads the clock, finds a zone, turns an instant into a wall clock and back, formats in any locale and parses, all with no Perl call, and a value made there is a value here. It is at version 3, each version appended to the last.