NAME
Time::Keeper::Duration - an amount of time: months, days, minutes and seconds
VERSION
Version 0.11
SYNOPSIS
use Time::Keeper;
my $d = Time::Keeper::Duration->new(months => 1, days => 2, hours => 3);
my $t = Time::Keeper->now->plus($d);
my $u = Time::Keeper->now + $d;
my $between = $b - $a; # a duration
print $between->days, "\n";
print "$between\n"; # P1M2DT3H, ISO 8601
my $ninety = Time::Keeper::Duration->from_seconds(90);
print $ninety->total_seconds, "\n"; # 90
DESCRIPTION
A duration is an amount of time in four units that do not turn into one another: months, because a month is not a fixed number of days; days, because a day is not always 24 hours where the clocks change; minutes, because a minute is not always 60 seconds when there is a leap second; and seconds, with nanoseconds. Years are kept as months, weeks as days and hours as minutes.
Each unit has its own sign, so a duration can be a month forward and a day back. Durations, like Time::Keeper values, never change: every method that would change one returns a new one.
How a duration is added to a time is described under "ARITHMETIC" in Time::Keeper.
CONSTRUCTORS
new
my $d = Time::Keeper::Duration->new(
years => 1, months => 2, weeks => 3, days => 4,
hours => 5, minutes => 6, seconds => 7, nanoseconds => 8,
end_of_month => 'limit',
);
Every unit is optional and a whole number; nanoseconds past a second are carried into the seconds exactly. end_of_month is wrap, limit or preserve, as described under "Months and the end of the month" in Time::Keeper.
from_seconds
my $d = Time::Keeper::Duration->from_seconds(90);
my $d = Time::Keeper::Duration->from_seconds('1.000000001');
A duration of seconds alone. A string of digits with up to nine decimal places is taken exactly; a number with a fraction is rounded to the microsecond.
parse
my $d = Time::Keeper::Duration->parse('P1Y2M3DT4H5M6.5S');
An ISO 8601 duration: P, then years, months, weeks and days, then T and hours, minutes and seconds, any of them left out, and - before it for a negative one. The last of the hours, minutes or seconds may have a fraction: PT1.5H is an hour and 30 minutes. A part may have a sign of its own, as "iso8601" writes a duration whose parts differ in sign.
THE UNITS
As DateTime::Duration gives them: each is the size of its part, never negative, so a duration of 90 minutes is 1 hour and 30 minutes.
years
months
The whole years in the months, and the months left over.
weeks
days
The whole weeks in the days, and the days left over.
hours
minutes
The whole hours in the minutes, and the minutes left over.
seconds
nanoseconds
in_units
my ($h, $m) = $d->in_units('hours', 'minutes');
The duration in the units asked for, with signs. Each unit takes what it can from its own part, the larger units first: years and months from the months, weeks and days from the days, hours and minutes from the minutes, seconds and nanoseconds from the seconds. A unit never borrows from another part, since months, days and minutes do not convert. In scalar context, the first unit asked for.
deltas
The five parts as a list of pairs: months, days, minutes, seconds and nanoseconds.
end_of_month
wrap, limit or preserve.
is_positive
is_negative
is_zero
True when every part is zero or more and one is more than zero; when every part is zero or less and one is less; or when every part is zero. A duration with parts of both signs is neither positive nor negative.
total_seconds
The duration as a number of seconds, for one with no months or days: those are not a fixed number of seconds, and a duration with them is an error here.
total
$d->total('hours'); # 49.5
$d->total('days', relative_to => $t); # months and days need a start
The duration as a number of the unit, with a fraction. Hours, minutes, seconds and below are the clock time; days and weeks are 24-hour days for a duration of clock time alone. A duration with months or days, or a total in months or years, is measured from relative_to, a value: the whole units from it, then the fraction of the next, so a month from 31 January is 31 days and 45 days from 1 January 2026 is 1.5 months.
NEW DURATIONS FROM OLD
inverse
The duration with every part's sign turned over. Its end_of_month is worked out afresh for its new months unless given: $d->inverse(end_of_month => 'limit').
calendar_duration
clock_duration
The months and days alone, and the minutes and seconds alone.
plus
minus
$d->plus($e) and $d->minus($e): part by part. + and - are the same, and -$d is "inverse".
multiply
$d->multiply(3): every part three times, as $d * 3. The factor is a whole number.
divide
$d->divide(2): every part divided by a whole number. The months, days and minutes must divide exactly, since a remainder of one is not a whole number of the next; the seconds carry into the nanoseconds, and what is left below a nanosecond is dropped.
abs
The duration with every part made positive. A duration with parts of both signs has no absolute value, and is an error here.
balance
$d->balance(largest => 'days', relative_to => $t); # P1M2D is P33D from 31 January
$d->balance(largest => 'hours'); # PT1H30M from PT90M
The duration with largest as its largest unit (months if not given), as "until" in Time::Keeper gives it from relative_to. Without relative_to a duration of clock time is balanced from a floating moment, so a day is 24 hours; one with months or days needs it.
round
my $d = $d->round(to => 'minutes');
my $d = $d->round(to => 'minutes', increment => 5, mode => 'ceil');
The duration rounded to a whole number of hours, minutes, seconds, milliseconds, microseconds or nanoseconds, with increment and mode as "round" in Time::Keeper takes them; floor of a negative duration goes further from zero, and half_even counts buckets from zero. The result is in seconds, or in minutes when the duration was minutes alone and the unit is a minute or more.
$d->round(to => 'months', relative_to => $t); # P1M15D from 1 January 2026 is P2M
With relative_to, a value, months and days are rounded too: the duration is added to the value and the result rounded as "until" in Time::Keeper rounds, so a month and fifteen days from 1 January 2026 is two months, since 16 February is past the middle of February. A duration with months or days needs it, and so does rounding to days, weeks, months or years.
COMPARING AND PRINTING
compare
Time::Keeper::Duration->compare($d, $e);
Time::Keeper::Duration->compare($d, $e, $start);
-1, 0 or 1 as $d is shorter than, as long as or longer than $e, measured from $start, or from now. A month is longer from 1 January than from 1 February, so durations with months or days compare only from a starting point: <=> compares durations without them, and is an error for those with.
format
$d->format; # 1 hour, 30 minutes
$d->format(locale => 'de', style => 'narrow'); # 1 Std., 30 Min.
$d->format(style => 'digital'); # 1:30:00
$d->format(units => ['hours', 'minutes'], largest => 1); # 1 hour
The duration in the words of locale (English if not given), as Intl.DurationFormat writes one: each part that is not zero in its unit's words for its plural category, joined as the locale joins a list. style is long, short or narrow, or digital for h:mm:ss (a duration with months or days has no fixed length and cannot be written as digits). units is a list of the units that may appear, from years to nanoseconds; a unit left out folds into the next smaller one given (90 minutes is 1 hour 30 minutes, or 90 minutes without hours), and what is left below the smallest unit given is dropped; months need years or months, and days weeks or days. largest keeps that many of the largest parts. A duration of nothing is 0 seconds, in the smallest unit given. Parts are written without their sign.
iso8601
The ISO 8601 form, such as P1Y2M3DT4H5M6.5S, as "$d" gives it. A duration whose parts have both signs has no ISO 8601 form; each part is then given with its own sign, as P1M-2D.
SERIALISATION
A duration freezes as text, its five parts and its end-of-month rule, with Storable and with the serialisers that use Types::Serialiser's protocol (Sereal, CBOR::XS).
STORABLE_freeze
STORABLE_thaw
FREEZE
THAW
The hooks.
AUTHOR
LNATION <email@lnation.org>
LICENSE AND COPYRIGHT
This software is Copyright (c) 2026 by LNATION.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)