
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.
Probably the most popular module. More precisely, even the group of modules, since the different functional is divided into different namespaces. In general, the 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 );
The main feature of this module, I would call omnivorous. He is able to do very cunning manipulations, for example, to determine the date by the line "8:00 pm December tenth", and even in different languages. But the documentation for this module is the most incomprehensible (at least to me). Like 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 );
Another very popular module. In addition, this is a core module included in the Perl distribution. In essence, it is an OO wrapper over standard functions. By default, overlaps 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;
The module is written using XS and is positioned as very fast. It has a significant limitation - assembly requires Perl 5.18 or higher. 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;
Another popular module. A distinctive feature of it is that it uses a simple array to store information about the date and time instead of a special object. The interface is quite simple and straightforward. Many different functions for manipulating dates, a little less for manipulating time. Works up to seconds. # 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 );
A fairly new module (current version is 0.22), I only found out about it when I began to prepare this article.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