DateTime
causes equally traditional serious complaints about the speed of work and memory consumption, so it gradually began to be ousted from our system by alternative modules. TIMTOWDI is great, but I still want to have some order in the project. Therefore, we decided to test several of the most popular modules in terms of speed, functionality and usability and choose the one that will become our main tool.DateTime
functionality is very wide, but the module is often criticized for poor performance.DateTime::Format::*
. They are also used to form a string with time in the required format if they implement the format_datetime
method. For tests, I will use DateTime::Format::ISO8601
(more often used in different APIs and services) and DateTime::Format::Strptime
(allows you to use your own template, similar to strptime). You can also create your own parser using DateTime::Format::Builder
.DateTime::TimeZone
objects to work with time zones, but this is optional. For example, you can create a time object in a specific zone simply by specifying time_zone => 'Asia/Taipei'
. It is important to understand that the description of the zones is located in the module itself, and their relevance should be monitored separately. You can also send a previously prepared DateTime::TimeZone
object instead of a string, which can be useful when we use a local time zone. Determining it may be long and efficient to prepare an object in advance, for example, like this: state $tz = DateTime::TimeZone->new( name => 'local' );
DateTime::Duration
objects are used, the same objects are returned when dates are subtracted.DateTime->compare( $dt1, $dt2 )
and DateTime->compare_ignore_floating( $dt1, $dt2 )
. # , new . my $dt = DateTime->new( year => 1964, month => 10, day => 16, hour => 16, minute => 12, second => 47, nanosecond => 500_000_000, time_zone => 'Asia/Taipei', ); # $dt = DateTime->now(); # UTC $dt = DateTime->now( time_zone => 'UTC' ); # $dt = DateTime->now( time_zone => '+1000' ); # (ISO8601) $dt = DateTime::Format::ISO8601->parse_datetime('2015-02-18T10:50:31.521345123+10:00'); # (, ISO8601) my $dt_format = DateTime::Format::Strptime->new( pattern => '%Y-%m-%dT%H:%M:%S.%9N%z', on_error => 'croak', ); $dt = $dt_format->parse_datetime('2015-02-18T10:50:31.521345123+1000'); # my $str = $dt_format->format_datetime($dt); # 1 2 3 4 5 6 my $dt_duration = DateTime::Duration->new( years => 1, months => 2, days => 3, hours => 4, minutes => 5, seconds => 6, ); my $dt2 = $dt + $dt_duration; # my $result = DateTime->compare( $dt, $dt2 ); # : -1 . . $dt < $dt2 # my $interval = $dt2->subtract_datetime( $dt1 ); # / my $week_begin = $dt->clone->truncate( to => 'week' ); my $week_end = $week_begin->clone->add( days => 6 ); my $month_begin = $dt->clone->truncate( to => 'month' ); my $month_end = $month_begin->clone->add( months => 1 )->subtract( days => 1 );
DateTime
, the functionality is divided into many modules, but the logic of their separation is not obvious. In order to create a new object, you have to use the documentation for three modules at once - Date::Manip
, Date::Manip::Date
, Date::Manip::Obj
.Date::Manip::Delta
objects, they are also returned as the difference between dates. $dm_date1->cmp( $dm_date2 );
# . , , # . my $dm = Date::Manip::Date->new; my $dt = $dm->new_date; # . . $dt->parse('now'); # UTC $dt->parse('now gmt'); # $date->parse('now gtm+10'); # (ISO8601) $date->parse('2015-02-18T10:50:31.521345123+10:00'); # my $str = $dm_date->printf("%Y.%m.%d %H-%M-%S %z"); # 1 2 3 4 5 6 my $dm_delta = Date::Manip::Delta->new; $dm_delta->parse('1:2:0:3:4:5:6'); # 0 — my $dm_date2 = $dm_date->calc( $dm_delta ); # my $result = $dm_date1->cmp( $dm_date2 ); # -1 # my $interval = $dm_date2->calc( $dm_date1 ); # # ( parse('now') . . , ) my $week_begin = $dm_date->new_date; $week_begin->parse('now'); $week_begin->prev(1,1,[0,0,0]); # 00:00:00 # , (1- ) # , # - (,,) my $week_end = $dm_date->new_date; $week_end->parse('now'); $week_end->prev(7,1,[0,0,0]); my $month_begin = $dm_date->new_date; $month_begin->parse('now'); $month_begin->set('time',[0,0,0]); $month_begin->set('d',1); my $delta = Date::Manip::Delta->new; $delta->parse('0:1:0:-1:0:0:0'); my $month_end = $dm_m1->calc( $delta );
localtime
and gmtime
. An empty object does not know how to create it; when called, new
does the same as localtime
, but if another Time::Piece
object is passed, it creates a copy of it.strptime
pattern, which is enough in most cases.Time::Seconds
( ONE_DAY
, for example). The results of the calculations are not quite obvious, so '2015-02-25 10:33:25' + ONE_YEAR = '2016-02-25 16:22:15'
. The thing is that ONE_YEAR
is 31556930 seconds or 365.24225 days (yes, with rounding). With the month the same: '2015-02-01 00:00:00' + ONE_MONTH = '2015-03-03 10:29:04'
. Understanding this problem, the author has provided two object methods: add_months
and add_years
. But they also work with features: taking away the month of 2008-03-31, we get 2008-03-02. It must always be remembered and taken into account.-
, +=
, <
, >=
, <=>
, etc.Time::Seconds
returned as the date difference. # . ->new localtime my $tp = Time::Piece->new; $tp = localtime; # UTC $tp = gmtime; # . $tp += 60*60*10; # # ( ) $tp = Time::Piece->strptime('2015-02-18T10:50:31+1000', '%Y-%m-%dT%H:%M:%S%z'); # my $str = $tp->strftime("%Y.%m.%d %H-%M-%S %z"); # 1 2 3 4 5 6 my $tp2 = $tp1 + 3 * ONE_DAY + 4 * ONE_HOUR + 5 * ONE_MINUTE + 6; $tp2->add_years(1); $tp2->add_months(2); # my $result = $tp1 <=> $tp2; # -1 # my $interval_in_seconds = $tp1 - $tp2;
Panda::Date::string_format("%Y%m%d%H%M%S");
Panda::Date::Int
objects can also be used. It knows how to add lines like '3Y 2D' (3 years and 2 days) or objects like Panda::Date::Rel
, such addition works even faster than addition with ARRAYREF
. When subtracting dates, returns the object Panda::Date::Int
.<=>
operator is used.day_of_week
, while the week starts on Sunday (value 0). Or you can use ewday
, then the week starts on Monday (value 1) and ends on Sunday (7). # # my $pd = Panda::Date->new; my $pd = Panda::Date->new( time ); my $pd = now; # # . # Rate Panda::Date new(time) Panda::Date new Panda::Date now # Panda::Date new(time) 742853/s -- -9% -23% # Panda::Date new 813840/s 10% -- -16% # Panda::Date now 967947/s 30% 19% -- # ( now). $pd = now; # UTC $pd = Panda::Date->new( time, 'UTC' ); # $pd->to_tz('UTC-10'); # ( ) $pd = Panda::Date->new('2015-02-18 10:50:31'); # my $str = $pd->strftime("%Y.%m.%d %H-%M-%S %z"); # 1 2 3 4 5 6 my $pd1 = Panda::Date::now; my $pd2 = $pd1 + [1,2,3,4,5,6]; my $pd2 = $pd1 + '1Y 2M 3D 4h 5m 6s'; # # my $result = $pd1 <=> $pd2; # my $interval = $pd1 - $pd2;
# my @dc = Today_and_Now(); # UTC . @dc = Today_and_Now(1); # UTC # my @delta_tz = (0, 10, 0, 0); # , , , my @dc_tz =Add_Delta_DHMS( Today_and_Now(1), @delta_tz ); # 1 2 3 4 5 6 @dc = Add_Delta_YMDHMS( @dc, (1,2,3,4,5,6) ); # my @dc1 = (2015,2,18,10,50,31); my @dc2 = (2015,2,18,10,50,32); my $result = 0+Delta_DHMS( @dc1, @dc2 ); # , # 2 1 # my @interval = Delta_YMDHMS( @dc1, @dc2 ); # / . . @dc =Today(); # my $dow =Day_of_Week( @dc ); my @week_begin =Add_Delta_Days( @dc, (1 - $dow) ); my @week_end =Add_Delta_Days( @week_begin, 6 ); my @month_begin = @dc; $month_begin[2] = 1; # my @month_end =Add_Delta_Days( Add_Delta_YMD( @month_begin, (0,1,0) ), -1 );
plus_years
for example). Unlike many other modules, when calculating 2013-01-31 + 1 month it gives the result 2013-02-28. What is right, in my opinion. Although, perhaps, someone expects a different behavior.<=>
, ==
, >=
, etc.epoch
method. This imposes limitations and accuracy is lost, but in some cases this accuracy may be enough ( Time::Piece
also works with seconds).with_day_of_week
(Monday 1, Sunday 7), with_day_of_month
and math methods to determine the start / end of the month / week. To reset the time of day, you must use the with_*
methods.Time::Piece
, for example). # my $tm = Time::Moment->new; # $tm = Time::Moment->now; # UTC $tm = Time::Moment->now_utc; # ( ) my $tm_with_offset = $tm->with_offset_same_instant(600); # # ( ISO8601) $tm = Time::Moment->from_string('2015-02-18T10:50:31.521345123+10:00'); # my $str = $tm->strftime("%Y.%m.%d %H-%M-%S (%f) %z"); # 1 2 3 4 5 6 my $tm2 = $tm1->plus_years(1)->plus_months(2)->plus_days(3) ->plus_hours(4)->plus_minutes(5)->plus_seconds(6); # my $result = $tm1 <=> $tm2; # : -1 # my $interval_in_seconds = $tm1->epoch - $tm2->epoch; # / $tm = $tm->with_hour(0) ->with_minute(0) ->with_second(0) ->with_nanosecond(0) my $week_begin = $tm->with_day_of_week(1); my $week_end = $tm->with_day_of_week(7) my $month_begin = $tm->with_day_of_month(1); my $month_end = $tm->with_day_of_month( $tm->length_of_month );
Date::Manip
= D::M
, DateTime
= DT
, etc. Rate D::M DT T::PD::CP::D (new time) P::D (new) T::MP::D (now) D::M 3373/s -- -73% -97% -98% -99% -100% -100% -100% DT 12582/s 273% -- -89% -92% -98% -98% -98% -99% T::P 119244/s 3435% 848% -- -20% -81% -82% -86% -86% D::C 149116/s 4321% 1085% 25% -- -77% -78% -82% -82% P::D (new time) 644519/s 19009% 5022% 441% 332% -- -5% -22% -23% P::D (new) 677138/s 19976% 5282% 468% 354% 5% -- -18% -19% T::M 830755/s 24531% 6503% 597% 457% 29% 23% -- -1% P::D (now) 839971/s 24804% 6576% 604% 463% 30% 24% 1% --
Panda::Date
is expected the fastest. But Time::Moment
unexpectedly almost as fast! Rate D::M DT T::PD::CP::DT::M D::M 1999/s -- -83% -98% -99% -100% -100% DT 11498/s 475% -- -90% -93% -98% -99% T::P 120130/s 5909% 945% -- -26% -82% -92% D::C 161964/s 8001% 1309% 35% -- -76% -89% P::D 671656/s 33495% 5741% 459% 315% -- -55% T::M 1476686/s 73761% 12743% 1129% 812% 120% --
Time::Moment
. It becomes much faster and comes out on top. Rate D::M DT D::CT::PP::DT::M D::M 1725/s -- -61% -95% -97% -100% -100% DT 4439/s 157% -- -87% -92% -99% -99% D::C 33939/s 1868% 665% -- -39% -92% -95% T::P 55584/s 3122% 1152% 64% -- -87% -92% P::D 438601/s 25327% 9782% 1192% 689% -- -40% T::M 735173/s 42520% 16463% 2066% 1223% 68% --
Date::Calc
can only parse dates (without time) and only in a certain format, so it does not participate in this test. Rate D::M DT (Strptime) DT (ISO8601) T::PP::DT::M D::M 1138/s -- -43% -63% -99% -100% -100% DT (Strptime) 1993/s 75% -- -36% -98% -100% -100% DT (ISO8601) 3090/s 171% 55% -- -98% -100% -100% T::P 127471/s 11097% 6297% 4025% -- -84% -90% P::D 792571/s 69519% 39675% 25547% 522% -- -37% T::M 1266979/s 111190% 63482% 40899% 894% 60% --
Date::Calc
does not know how to format strings by itself and this test also skips. Rate DT D::MT::PP::DT::M DT 10895/s -- -55% -95% -98% -98% D::M 24273/s 123% -- -88% -95% -95% T::P 202159/s 1756% 733% -- -57% -59% P::D 473339/s 4245% 1850% 134% -- -3% T::M 488258/s 4382% 1912% 142% 3% --
Rate D::M DT T::PD::CT::MP::D (array) P::D (string) D::M 3493/s -- -21% -71% -88% -99% -99% -100% DT 4403/s 26% -- -64% -85% -99% -99% -100% T::P 12092/s 246% 175% -- -58% -98% -98% -99% D::C 29019/s 731% 559% 140% -- -94% -95% -97% T::M 487483/s 13854% 10972% 3932% 1580% -- -16% -48% P::D (array) 579109/s 16477% 13053% 4689% 1896% 19% -- -38% P::D (string) 934644/s 26655% 21129% 7630% 3121% 92% 61% --
Time::Moment
and DateTime
- up to nanosecond (for them the difference between dates was 1 nanosecond), the rest up to a second (difference 1 second). Rate D::MD::C DT T::PP::DT::M D::M 27427/s -- -34% -64% -92% -99% -99% D::C 41837/s 53% -- -46% -88% -99% -99% DT 77067/s 181% 84% -- -79% -98% -98% T::P 363376/s 1225% 769% 372% -- -88% -89% P::D 3145500/s 11369% 7418% 3981% 766% -- -7% T::M 3399073/s 12293% 8025% 4311% 835% 8% --
Time::Moment
and then take first place. Rate DT D::MD::CT::PP::DT::M DT 6892/s -- -42% -88% -97% -99% -100% D::M 11964/s 74% -- -78% -95% -98% -99% D::C 55448/s 704% 363% -- -75% -93% -97% T::P 219763/s 3089% 1737% 296% -- -71% -89% P::D 767510/s 11036% 6315% 1284% 249% -- -63% T::M 2085234/s 30155% 17329% 3661% 849% 172% --
Time::Moment
, — epoch
(, ). DateTime
, , ( , ). Rate D::M DT T::PD::CP::DT::M D::M 93.9/s -- -88% -99% -99% -100% -100% DT 790/s 741% -- -92% -96% -99% -100% T::P 10060/s 10608% 1173% -- -45% -93% -94% D::C 18309/s 19388% 2217% 82% -- -87% -90% P::D 138748/s 147586% 17458% 1279% 658% -- -22% T::M 177777/s 189129% 22397% 1667% 871% 28% --
Time::Moment
— , . , . .DateTime
— , . .Date::Calc
— . .Panda::Date
— . Time::Moment
, (Perl 5.18) .Time::Piece
— core-, , .Date::Manip
— . — «8:00pm December tenth». , , , , .Time::Moment
. :Time::Moment
, , Time::Piece
( core-) DateTime
( ).Source: https://habr.com/ru/post/254453/
All Articles