Date - extremely fast Date framework with timezones, microseconds, relative dates and intervals support.

SYNOPSIS

use Date qw/date date_ymd rdate rdate_ymd tzset/;

my $date = Date->new($epoch); # using server's local timezone
$date = Date->new("2013-03-05 23:45:56.345");
$date = Date->new_ymd($y,$m,$d,$h,$m,$s);
$date = Date->new_ymd(year => $y, month => $m, day => $d, hour => $h, min => $m, sec => $s, mksec => $mks);
$date = Date::now(); # same as Date->new(time()) but faster
$date = Date::today(); # same as Date->new(time())->truncate but faster
$date = Date::now_hires(); # current time with microseconds

# create using function 'date'
tzset('Europe/Moscow'); # using 'Europe/Moscow' as server's local timezone
$date = date(123456789);
$date = date("2001/11/12 07:13:12", 'America/New_York'); # $date operates in custom time zone

# creating relative date object
# (normally you don't need to create this object explicitly)
$reldate = Date::Rel->new("3Y 1M 3D 6h 2m 4s");
$reldate = Date::Rel->new("6Y");
$reldate = Date::Rel->new($secs);
$reldate = Date::Rel->new_ymd($year,$month,$day,$hour,$min,$sec);
$reldate = Date::Rel->new_ymd(year => $year, month => $month, day => $day, hour => $hour, min => $min, sec => $sec);
$reldate = rdate "-1M -3D 6h";
$reldate = 3*MONTH; # "3M"
$reldate = 2*YEAR + MONTH - 30*DAY; # "2Y 1M -30D"
print $reldate/2; # 1Y 5h 14m 32s
$reldate = YEAR/2 + HOUR/2; # 6M 30m

$date;              # prints the date in default output format (ISO/SQL format)
$date->epoch;       # unix timestamp
$date->year;        # year, e.g: 2001
$date->c_year;      # year - 1900, e.g. 101
$date->yr;          # 2-digit year 0-99, e.g 1
$date->month;       # month 1..12
$date->mon;         # same as prev.
$date->c_month;     # month 0..11
$date->day;         # day of month
$date->mday;        # day of month
$date->day_of_month;# same as prev.
$date->hour;
$date->min;
$date->minute;      # same as prev.
$date->sec;
$date->second;      # same as prev.
$date->mksec;
$date->wday;        # 1-7, Sunday-Saturday
$date->day_of_week; # same as prev.
$date->c_wday;      # 0-6, Sunday-Saturday
$date->ewday;       # 1-7, Monday-Sunday
$date->yday;        # day of year [1-366]
$date->day_of_year; # same as prev.
$date->c_yday;      # [0-365]
$date->isdst;       # DST?
$date->daylight_savings; # same as prev.
$date->strftime($format);
$date->month_name;  # March
$date->month_sname; # Mar
$date->wday_name;   # Thursday
$date->wday_sname;  # Thu
$date->to_string    # 2000-02-29 12:21:11
"$date"             # same as prev.
$date->to_string(Date::FORMAT_ISO8601); # 2000-02-29T12:21:11+03:00
$date->gmtoff       # current offset from UTC (in seconds)
$date->tzname       # returns the timezone name (EST, EET, etc)
$date->tzlocal      # true if $date is in local time zone
$date->timezone     # returns timezone object
$date->timezone('GMT+5')  # changes $date's timezone (saving YMDhms)
$date->to_timezone('UTC') # changes $date's timezone (saving epoch)

($year,$month,$day,$hour,$min,$sec) = $date->array;
($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = $date->struct;

# constructing new date based on an existing one:
$new_date = $date->clone;
$new_date = $date->clone(year => 1977, sec => 14, tz => 'Australia/Melbourne');
# valid keys: year, month, day, hour, min, sec, tz

$date->month_begin   # First day of the month (date object)
$date->month_end     # Last day of the month
$date->days_in_month # 28..31

# error handling
$a = date($date_string);
if ($a) { # valid date
  ...
} else { # invalid date
  if ($a->error == Date::Error::parser_error) { ... }
  print $a->error->message;
}

# date range check
Date::range_check(0); # this is the default
print date("2001-02-31"); # will print 2001-03-03
Date::range_check(1);
print date("2001-02-31"); # will print nothing

# getting values of a relative date object
$reldate->year;
$reldate->month;
$reldate->day;
$reldate->hour;
$reldate->min;
$reldate->sec;
"$reldate";             # reldate in "1Y 2M 3D 4h 5m 6s" format
$reldate->duration;     # duration in seconds
int $reldate;           # same as prev
$reldate->to_secs;      # same as prev
$reldate->to_mins;      # relative date in minutes
$reldate->to_hours;     # relative date in hours
$reldate->to_days;      # relative date in days
$reldate->to_months;    # relative date in months
$reldate->to_years;     # relative date in years
 
# arithmetic with dates:
print date_ymd(2001,12,11,4,5,6)->truncate; # "2001-12-11"

$new_date = $date + $reldate;
$date2    = $date - '3Y 2D';   # 3 Years and 2 days
$date3    = $date + rdate_ymd(1,2,3);   # $date plus 1 year, 2 months, 3 days

# accurate relative dates
$rel = $date1 - $date2;
$rel->duration;              # accurate number of seconds in interval
$rel->to_months;             # accurate number of months in interval
$rel->includes("2013-01-01") # returns -1, 0, or 1

$days_between = ($date1 - $date2)->to_days;

# comparison between absolute dates
print $date1 > $date2 ? "I am older" : "I am younger";

# comparison between relative dates
print $reldate1 > $reldate2 ? "I am faster" : "I am slower";

# Adding / Subtracting months and years are sometimes tricky:
print date("2001-01-29") + '1M' - '1M'; # gives "2001-02-01"
print date("2000-02-29") + '1Y' - '1Y'; # gives "2000-03-01"

#strict mode
{
    use Date::strict;
    my $d = Date->new("invalid date"); # throws
    my $rel = Date::Rel->new("1X"); # throws
    $d += "1X"; # throws
}

# like strptime(3)
$date4 = Date::strptime('2019-02-03 04:05:06 Europe/Moscow', '%Y-%m-%d %H:%M:%S %Z');

DESCRIPTION

Date is a port of C++ panda::date framework which is complete feature-rich date framework, supporting timezones (not just offsets), microseconds, relative dates and intervals. Timezones with leap seconds (right/*) are also supported.

Date supports dates between -2147483648/01/01 00:00:00 and 2147483647/12/31 23:59:59 (on 64bit perls).

With Date you can perform some operations even faster than in plain C program using stdlibc functions because it has its own efficient low-level time functions implementation.

CLASS METHODS

new([$epoch = 0], [$timezone = local])

Creates date from UNIX timestamp (number of seconds since 1970). $epoch may be non-integer value in which case microseconds is set.

new($string, [$timezone = local], [$format = INPUT_FORMAT_ALL])

Creates date from string in one of the following formats:

Default iso date format
YYYY-MM-DD[ HH:MM[:SS[.ss]][±hh[:mm]|Z]]
YYYY/MM/DD[ HH:MM[:SS[.ss]][±hh[:mm]|Z]]

2019-12-11
2019-12-11 14:56:23
2019-12-11 14:56:23.123
2019-12-11 14:56:23Z
2019/12/11 14:56+03:00
2019/12/11 14:56:23.123+03

.ss is a microseconds part 1-6 digits long.

YYYY-MM[-DD[THH[:MM[:SS[.ss]]][±hh[:mm]|Z]]]]
YYYYMMDD[THH[MM[SS[.ss]]][±hh[mm]|Z]]]
YYYY-Wnn[-n]

2019-12
2019-12-11T14:56:23
20191211T145623.123Z
20191211T145623.123+0300
2019-12-11T14:56:23.123-03:30
2019-W35
2019-W35-2
RFC 822/1123
Tue, 10 Dec 2019 00:00:00 GMT
10 Dec 2019 00:00 Z

Only C locale supported.

RFC 850
Tuesday, 01-Jan-19 03:04:00 GMT

Only C locale supported.

ANSI C
Sun Sep  1 03:04:00 2019

Only C locale supported.

Dotted format
DD.MM.YYYY
Comman Log Format
dd/MMM/yyyy:hh:mm:ss +-hhmm
[dd/MMM/yyyy:hh:mm:ss +-hhmm]

Only C locale supported.

If timezone offset designator is present inside string, the date is created in unnamed timezone with constant offset from GMT (no transition rules). $timezone parameter is ignored in this case.

You can limit the number of allowed formats by passing $format argument, which is the bitmask of the following constants: INPUT_FORMAT_ALL(the default), INPUT_FORMAT_ISO, INPUT_FORMAT_ISO8601, INPUT_FORMAT_RFC1123, INPUT_FORMAT_RFC850, INPUT_FORMAT_ANSI_C, INPUT_FORMAT_DOT, INPUT_FORMAT_CLF.

If $string is an invalid date or a valid date but in format other than specified, then error() is set to Date::Error::parser_error.

If you want it to throw an exception on invalid inputs, see Date::strict.

new_ymd(%ymdhms)

Creates date from named YMDHMS arguments.

Date->new(
    year  => 2019
    month => 12,
    day   => 31,
    hour  => 23,
    min   => 59,
    sec   => 59,
    mksec => 123456,
    tz    => 'Europe/Moscow',
);

the default values for year is 1970, month&day - 1, others - 0, timezone - local.

If the date represented by YMDHMS is ambigious (falls into DST->STD timezone transition, i.e. when clock goes 1 hour back and certain HMS appears twice), the greater epoch is preferred. This behaviour can be altered with additional isdst hash parameter. The default value is -1 (auto). 0 means 'prefer standart time', 1 means 'prefer daylight savings time'. If specified date is not ambigious, isdst parameter is completely ignored.

new_ymd($year, [$month = 1], [$day = 1], [$hour = 0], [$min = 0], [$sec = 0], [$mksec = 0], [$timezone = local])

Created date from unnamed YMDHMS arguments.

new($date_object)

If $date_object is a Date object, then returns copy of the date as clone(). Otherwise stringifies $date_object and create date as new($string). If this object does not have stringification overload, the result is date with parsing error.

new* [all variants]

Common behaviour for any of constructor variants.

If $timezone is present, created object will operate as if tzset($timezone) was called, but without calling tzset().

If $timezone is absent (or undef or ""), local timezone will be used. Further changes of local timezone via tzset() won't affect constructed object.

If there is any errors while creating an object, error() will be set and the date is set to epoch=0. The object itself will return false in boolean context, empty string in string context and so on.

OBJECT METHODS

set(...)

set_ymd(%ymdhms | @ymdhms)

Set another date for object. This is faster than creating new object. See new() and new_ymd() for arguments details.

epoch([$epoch])

Get or set UNIX timestamp (integer or double with mksecs)

epoch_sec ()

Get UNIX timestamp as integer (without microseconds even if they are present).

year([$year])

Get or set year [-2**31, 2**31-1]

c_year([$year])

Get or set year counted from 1900 (C-style)

yr([$yr])

Get or set 2-digits-style-year [0-99]

month([$mon]), mon

Get or set month [1-12]

c_month([$mon])

Get or set month [0-11] (C-style)

day([$day]), mday, day_of_month

Get or set day of month [1-31]

hour([$hour])

Get or set hours [0-23]

min([$min]), minute

Get or set minutes [0-59]

sec([$sec]), second

Get or set seconds [0-60]

mksec([$mksec])

Get or set microseconds [0-999999]

wday([$wday]), day_of_week

Day of week. 1 = Sunday, ... , 7 = Saturday.

If you pass an argument then another day of the same week will be set.

c_wday([$wday])

C-style day of week. 0 = Sunday, ... , 6 = Saturday.

If you pass an argument then another day of the same week will be set.

ewday([$wday])

Europe-friendly day of week. 1 = Monday, ..., 7 = Sunday.

If you pass an argument then another day of the same week will be set.

yday([$yday]), day_of_year

Day of the year [1-366].

If you pass an argument then another day of the same year will be set.

c_yday([$yday])

C-style day of the year [0-365].

If you pass an argument then another day of the same year will be set.

isdst(), daylight_savings()

Is daylight savings time in effect now (true/false).

month_name()

Full name of the month

month_sname()

Short name of the month

wday_name()

Full name of the day

wday_sname()

Short name of the day

""

Same as to_string().

to_string($format = Date::FORMAT_ISO)

Stringifies date using $format.

Available formats:

Date::FORMAT_ISO

Default format

2019-12-31 23:59:59.123

Microseconds part will be absent if mksec == 0.

Date::FORMAT_ISO_TZ

iso with timezone offset

2019-12-31 23:59:59.123+03:00
Date::FORMAT_ISO_DATE

Only date

2019-12-31
Date::FORMAT_ISO8601
2019-12-31T23:59:59.123+03:00
Date::FORMAT_ISO8601_NOTZ

Same as iso8601 but omits printing timezone offset

2019-12-31T23:59:59.123
Date::FORMAT_RFC1123
Tue, 10 Dec 2019 00:00:00 GMT

same as rfc1123

Date::FORMAT_RFC850
Tuesday, 01-Jan-19 03:04:00 GMT
Date::FORMAT_ANSI_C
Sun Sep  1 03:04:00 2019
Date::FORMAT_YMD
2019/12/31
Date::FORMAT_DOT
31.12.2019
Date::FORMAT_HMS
23:59:59
Date::FORMAT_CLF
10/Oct/1999:21:15:05 +0500
Date::FORMAT_CLF_BRACKETS
[10/Oct/1999:21:15:05 +0500]

strftime($format)

Works like Date::strftime() with current date's fields passed as arguments.

'bool', to_bool()

Called implicitly in boolean context

if ($date)
$date ? EXPR1 : EXPR2
$date && $something

Returns TRUE if date has no errors (i.e. has no parsing or out of range errors, etc), otherwise FALSE

'0+', to_number()

Returns epoch() in numeric context

gmtoff()

Returns current timezone offset from UTC in seconds - may change when the date changes isdst/nodst.

tzname()

Returns the name of the object's timezone (Europe/Moscow, America/New_York, etc).

tzabbr()

Returns timezone abbreviation (EST, EET, etc) - may change when the date changes isdst/nodst.

tzlocal()

Returns TRUE if this object's timezone is set as local.

tz([$newzone])

With no arguments returns Date::Timezone object associated with the date.

With argument changes the timezone of current object to $newzone preserving YMDhms information (epoch may change) and returns nothing. $newzone may be timezone object, name or rule.

to_tz($newzone)

Changes the timezone of current object to $newzone in a way that changed date still points to the same time moment (same epoch). YMDhms info may change. Returns nothing. $newzone may be timezone object, name or rule.

array()

Returns elements list - $year,$month,$day,$hour,$min,$sec,[$mksec].

$year is year() [2013=2013]

$month is month() [1-12]

If $mksec == 0 it will not be on the list

struct()

Returns elements list - $sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst

$year is c_year() [113 = 2013]

$month is c_month() [0-11]

$wday is c_wday() [0-6]

$yday is c_yday() [0-365]

clone(%diff | @diff)

Returns copy of the date.

If you pass a hash-list or array then date is cloned with changes described in the hash/array.

Hash keys: year (YYYY), month [1-12], day, hour, min, sec, mksec, tz

Array: [$year (YYYY), $month [1-12], $day, $hour, $min, $sec, $mksec, $tz]

If any values in hash or array are absent (or = undef or = -1) the appropriate field of source date is not changed.

If tz parameter is absent (or undef or ""), newly created date will use current date's timezone. Otherwise constructed date is converted to specified timezone preserving YMDhms information.

month_begin_new()

Returns the beggining of month. Only day of month is changed, HMS are preserved.

month_begin()

Same as month_begin_new() but changes current object instead of cloning.

month_end_new()

Returns the end of month. Only day of month is changed, HMS are preserved.

month_end()

Same as month_end_new() but changes current object instead of cloning.

days_in_month()

Returns the number of days in month

week_of_month()

Returns the week of the month [0..5]. The first week of the month is the first week that contains a Thursday. A day in the week before the week with the first Thursday will be week 0.

weeks_in_year()

Returns total number of weeks in the current year (52 or 53).

week_of_year()

my $week_number = $date->week_of_year();
my ($week_year, $week_number) = $date->week_of_year();

Returns information about the calendar week which contains this date object [1..53].

The first week of the year is defined by ISO as the one which contains the fourth day of January, which is equivalent to saying that it's the first week to overlap the new year by at least four days.

Typically the week year will be the same as the year that the object is in, but dates at the very beginning of a calendar year often end up in the last week of the prior year, and similarly, the final few days of the year may be placed in the first week of the next year.

error()

Returns error occured during creating or cloning object (if any) as XS::STL::ErrorCode object.

truncated()

Return copy of the current date with HMS set to 0. Same as clone(hour = 0, min => 0, sec => 0)>, but runs faster.

truncate()

Same as truncated() but changes current object instead of cloning.

'<=>', 'cmp', compare($arg)

Converts $arg to date, compares 2 dates and returns -1, 0 or 1.

$date <=> $arg
$arg  <=> $date

$date <=> "2001-01-01 01:01:01"
$date <=> 123456789; # epoch
"01.02.2019" <=> $date
$date1 <=> $date2

'+', sum($arg)

Converts $arg to relative date, adds it to the date and returns new date as a result.

$date + $arg
$arg + $date

$date + "1Y 2M";
86400 + $date;
$date + DAY;

'+=', add($arg)

Converts $arg to relative date and adds it to existing date object.

$date += $arg

$date += 10;
$date += YEAR;
$date += "1h 2m 3s";

'-', difference($arg)

If second operand is a date object, then returns Date::Rel object representing exact interval from right operand till left operand.

$date_till - $date_from; # same as Date::Rel->new($date_from, $date_till)

(date("2019-02-01") - date("2019-01-01"))->to_days == 31;

see Date::Rel.

Otherwise converts right operand to relative date, subtracts it from the date object and returns new date as a result. Reverse is not allowed.

$date - "1Y";
$date - 86400;
86400 - $date; # !not allowed!

'-=', subtract($arg)

Converts $arg to relative date and subtracts it from existing date object. Reverse is not allowed.

FUNCTIONS

now()

Same as Date->new(time()) but runs faster.

now_hires()

Same as Date->new(Time::HiRes::time()) but runs faster.

today()

Same as Date->new(time())->truncate but runs faster.

today_epoch()

Same as today()->epoch but runs faster.

range_check([$true_false])

If parts of the date are invalid or the whole date is not valid, e.g. 2001-02-31 then:

when range_check is not set (the default), then these date values are automatically converted to a valid date (normalized): 2001-03-03

when range_check is set, then a date "2001-02-31" became invalid date and error() is set to Date::Error::out_of_range.

One exception from this rule is when format has a weekday name (rfc1123, etc) and it doesn't match represented date, Date::Error::out_of_range will be always set.

date(...)

Same as Date->new(...). See new() for arguments details.

date_ymd(...)

Same as Date->new_ymd(...). See new_ymd() for arguments details.

rdate(...)

Same as Date::Rel->new(...), see Date::Rel

rdate_ymd(...)

Same as Date::Rel->new_ymd(...), see Date::Rel

tzget([$zone])

Returns timezone object for timezone name $zone (or local zone if $zone is not provided). The object can later be used for creating dates in this zone without setting this zone as local via tzset().

tzset([$zone])

Sets $zone as localzone. If you don't provide $zone, default timezone will be set ($ENV{TZ}, /etc/localtime, or whatever your OS considers to be localzone).

Does NOT affect POSIX:tzset(). Only this module's functions and objects will follow this timezone.

# change local zone to 'America/New_York'
tzset('America/New_York');
# or
my $tz = tzget('America/New_York');
tzset($tz);

# the same (doesnt work in Windows)
local $ENV{TZ} = 'America/New_York';
tzset();

# change localzone back to the default localzone (in case you didn't change $ENV{TZ})
tzset(); # or tzset(undef) or tzset('')

If you don't want to change localzone, you don't have to call this function directly as it's called implicitly on-demand.

If you provide $zone and no such zone found in zones directory (or timezone file is corrupted), 'UTC0' is used.

$zone may be Date::Timezone object.

tzname()

The name of localzone. Note that in some cases the real name of localzone is not known (for example when localzone is retrieved from /etc/localtime file, tzname() will return ':/etc/localtime')

use_system_timezones()

Use your OS's timezones dir. This is default behaviour if your OS has /usr/share/zoneinfo DB. Otherwise embedded zones are used by default (for example, on MS Windows).

If your OS doesn't have /usr/share/zoneinfo DB, this function warns and does nothing.

use_embed_timezones()

Use timezone files which come with this module (they may be newer or older than system ones).

tzdir([$newdir])

Sets or returns current timezones directory. If there was an error (!exists, !readable, etc) returns false and leaves tzdir unchanged.

say tzdir(); # prints /usr/share/zoneinfo (on UNIX)
tzdir('/home/frank/myzones'); # use /home/frank/myzones as timezones DB
say tzdir(); # prints /home/frank/myzones
tzset('Europe/Moscow'); # set /home/frank/myzones/Europe/Moscow as localzone

available_timezones()

Returns list of all available timezones in tzdir().

strftime($format, $sec, $min, $hour, $mday, $mon, $year, [$isdst], [$timezone])

Works like POSIX's strftime(), but runs much faster. Additionaly, supports timezone argument in which case it will interpret passed YMDHSD as date in timezone $timezone instead of local zone.

NOTE: $year must be in natural form (2019, not 119). Month as in original strftime(), 0-11.

strftime($format, $epoch, [$timezone])

Same as above, but receives UNIX timestamp instead of YMDHMS arguments.

gmtime($epoch)

The returned year is in human-readable form (not year-1900). Month is [0-11]. The same applies for all further time functions.

localtime($epoch)

anytime($epoch, $timezone)

Like localtime() but runs calculations in timezone $timezone instead of local zone

timegm($sec, $min, $hour, $day, $mon, $year, [$isdst])

timegmn($sec, $min, $hour, $day, $mon, $year)

Same as timegm() except for the arguments which have to be non-constant values because they are normalized during calculations.

timelocal($sec, $min, $hour, $day, $mon, $year, [$isdst])

timelocaln($sec, $min, $hour, $day, $mon, $year, [$isdst])

Same as timelocal() except for the arguments which have to be non-constant values because they are normalized during calculations.

timeany($sec, $min, $hour, $day, $mon, $year, [$isdst], [$timezone])

Like timelocal() but runs calculations in timezone $timezone instead of local zone

timeanyn($sec, $min, $hour, $day, $mon, $year, [$isdst], [$timezone])

strptime($string, $format)

Parses date in $string by the pattern defined in $format. Works similar to strptime(3).

CONSTANTS

Date::Error::parser_error

Wrong date or relative date string format

Date::Error::out_of_range

Invalid date (or date part) supplied when range_check() is in effect

YEAR

Constant for rdate("1Y"). These (YEAR...SEC) objects are constants (read-only).

If you try to change these objects they'll croak.

MONTH

Constant for rdate("1M")

DAY

Constant for rdate("1D")

WEEK

Constant for rdate("1W")

HOUR

Constant for rdate("1h")

MIN

Constant for rdate("1m")

SEC

Constant for rdate("1s")

CLONING/SERIALIZATION

Date supports:

Storable serialization/deserealization

You can freeze Date::* objects and thaw serialized data back without losing any date information.

If you serialize a date object which was created with personal timezone (second arg to constructor), then it will be deserialized exactly in the same timezone.

If a date object is in local timezone, then it will be deserialized in local timezone too (which may differ on differrent servers), but it's guaranteed that those two dates will point to the same time moment (epoch is preserved).

For example:

tzset('Europe/Moscow');
my $date = date("2014-01-01");
my $frozen = freeze $date;
tzset('America/New_York');
my $date2 = thaw $frozen;
$date == $date2; # true, because $date->epoch == $date2->epoch
say $date;  # 2014-01-01 00:00:00
say $date2; # 2013-12-31 15:00:00
Data::Recursive's clone
JSON serialization with convert_blessed

Serializes to epoch()

PACKAGES

Date (this package)

Date::Rel - relative date object

Date::strict - enable/disable strict mode

DATE RANGES

64bit OS + perl-64bit-int

from -2147483648/01/01 00:00:00 till 2147483647/12/31 23:59:59

32bit OS + perl-64bit-int

from -2147483648/01/01 00:00:00 till 2147483647/12/31 23:59:59

32bit OS + perl-32bit-int

from -285424812/02/20 18:53:48 till 285428751/11/12 11:36:32

Please note, that these ranges means that Date object can make calculations and its API works correctly while date is in these ranges.

However, it won't be able to parse and to stringify such dates because no ISO/RFC standarts exist for those ranges.

The maximum range for parsing and stringification is 0000/01/01 00:00:00 - 9999/12/31 23:59:59

SUPPORTED OS

Tested on FreeBSD, Linux, MacOSX, Windows 2003-10.

I believe all of UNIX-like and Windows-like systems are supported.

Timezones are supported in Olson DB format (V1,2,3).

CAVEATS

Setting $ENV{TZ} doesn't work in Windows. To set $zone as localzone, you should write

Date::tzset($zone);

to produce platform-independent code.

Also please note that in some extremely rare cases the result of date calculations of this module and POSIX functions may not match (this module's results are correct in this case, POSIX's - are not).

That's because POSIX's timegm/timelocal/etc functions have bugs on some certain time moments. For detailed info see Date::TimeImpl.

PERFORMANCE

Date runs 1.5 - 50x faster than Time::Moment. Tests were performed on Xeon-v3 3.0Ghz, Debian 10, perl 5.30.0

new empty
                  Rate Time::Moment         Date
Time::Moment 3619344/s           --         -34%
Date         5450519/s          51%           --

new with current time
                  Rate Time::Moment   Date_hires         Date
Time::Moment  420102/s           --         -91%         -92%
Date_hires   4812084/s        1045%           --         -13%
Date         5519512/s        1214%          15%           --

new from epoch
                  Rate Time::Moment         Date
Time::Moment 3849666/s           --         -25%
Date         5164206/s          34%           --

new from string
                  Rate Time::Moment         Date
Time::Moment 2682760/s           --         -29%
Date         3780922/s          41%           --

new from named YMDHMS
                  Rate Time::Moment         Date
Time::Moment 2372854/s           --         -13%
Date         2737056/s          15%           --

today (now+truncate)
                  Rate Time::Moment         Date
Time::Moment  430970/s           --         -91%
Date         4721290/s         996%           --

adding 1 relative
                  Rate Time::Moment         Date
Time::Moment 3780922/s           --         -53%
Date         7989875/s         111%           --

adding many relatives
                  Rate Time::Moment         Date
Time::Moment 1341380/s           --         -72%
Date         4733156/s         253%           --    

converting epoch<->YMDHMS with date operations
                  Rate Time::Moment         Date
Time::Moment 1102769/s           --         -49%
Date         2143658/s          94%           --

using timezones
                  Rate Time::Moment         Date
Time::Moment   71087/s           --         -98%
Date         3674915/s        5070%           --
    

Benchmark code:

my $time = -1;

say "new empty";
cmpthese($time, {
    Date           => sub { Date::date() },
    "Time::Moment" => sub { Time::Moment->new },
});
say;

say "new with current time";
cmpthese($time, {
    Date           => sub { Date::now() },
    Date_hires     => sub { Date::now_hires() },
    "Time::Moment" => sub { Time::Moment->now },
});
say;

say "new from epoch";
cmpthese($time, {
    Date           => sub { Date::date(1000000000) },
    "Time::Moment" => sub { Time::Moment->from_epoch(1000000000) },
});
say;

say "new from string";
cmpthese($time, {
    Date           => sub { Date::date("2019-01-01T23:59:59Z") },
    "Time::Moment" => sub { Time::Moment->from_epoch("2019-01-01T23:59:59Z") },
});
say;

say "new from named YMDHMS";
cmpthese($time, {
    Date           => sub { Date::date_ymd(year => 2019, month => 1, day => 1, hour => 23, min => 59, sec => 59, mksec => 123456) },
    "Time::Moment" => sub { Time::Moment->new(year => 2019, month => 1, day => 1, hour => 23, minute => 59, second => 59, nanosecond => 123456000) },
});
say;

say "today (now+truncate)";
{
    my $d = Date::now();
    my $tm = Time::Moment->now();
    cmpthese($time, {
        Date           => sub { Date::today() },
        "Time::Moment" => sub { Time::Moment->now->at_midnight },
    });
}
say;

say "adding 1 relative";
{
    my $d = Date::now();
    my $tm = Time::Moment->now();
    cmpthese($time, {
        Date           => sub { $d += HOUR },
        "Time::Moment" => sub { $tm = $tm->plus_hours(1) },
    });
}
say;

say "adding many relatives";
{
    my $d = Date::now();
    my $tm = Time::Moment->now();
    cmpthese($time, {
        Date           => sub { $d += "1D 1h 1m 1s" },
        "Time::Moment" => sub { $tm = $tm->plus_days(1)->plus_hours(1)->plus_minutes(1)->plus_seconds(1) },
    });
}
say;

say "converting epoch<->YMDHMS with date operations";
{
    my $d = Date::now();
    my $tm = Time::Moment->now();
    cmpthese($time, {
        Date           => sub { $d += 1; $d->year; $d += DAY; $d->epoch },
        "Time::Moment" => sub { $tm = $tm->plus_seconds(1); $tm->year; $tm = $tm->plus_days(1); $tm->epoch },
    });
}
say;

say "using timezones";
cmpthese($time, {
    Date           => sub { Date::date_ymd(2012, 12, 24, 15, 0, 0, 0, 'America/New_York') },
    "Time::Moment" => sub {
        my $tm = Time::Moment->new(
            year   => 2012,
            month  => 12,
            day    => 24,
            hour   => 15
        );
        my $zone   = DateTime::TimeZone->new(name => 'America/New_York');
        my $offset = $zone->offset_for_datetime($tm) / 60;
        $tm->with_offset_same_instant($offset);
    },
});
say;

AUTHOR

Pronin Oleg <syber@cpan.org>, Ivan Baidakou <i.baydakov@crazypanda.ru>, Crazy Panda LTD

LICENSE

You may distribute this code under the same terms as Perl itself.