Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Re: Date::Parse - how to correctly parse dates between 1901 and 1969

by eniad (Acolyte)
on Feb 20, 2018 at 00:22 UTC ( [id://1209520]=note: print w/replies, xml ) Need Help??


in reply to Date::Parse - how to correctly parse dates between 1901 and 1969

Solution 1

To handle the $year - 1900 bug mentioned by tangent, I have switched to the Date::Parse strptime function. That allows me to ensure a 4-digit year. It fails for dates before 1000. I address that if it comes up, which is unlikely, but possible.

Here is my updated example script with better formatted output:

#!/usr/bin/perl #--------------------------------------------------------------------- # format_date.pl # # format variable date inputs #--------------------------------------------------------------------- use strict; use warnings; use Date::Parse; use DateTime; my $DEFAULT_TIME_ZONE = "GMT"; my @dates = ( "0618-01-01 00:00:00", "1066-10-14 00:00:00", "1899-06-24 09:44:00", "1900-12-31 23:59:59", "1901-01-01 00:00:00", "1960-12-31 23:59:59", "1966-06-24 09:44:00", "1968-12-31 23:59:59", "1969-01-01 00:00:00", "1969-12-31 23:59:59", "1970-01-01 00:00:01", "2000-01-01 00:00:00", "2017-06-24 23:59:59", "2018-06-24 09:44:00", "2238-06-24 09:44:00" ); # define format for printf statements my $pstr = "%-19s %02s,%02s,%02s,%02s,%02s,%04s,%01s %-19s\n"; my $pfrm = "%19s %02u,%02u,%02u,%02u,%02u,%04u,%01u %19s\n"; printf( $pstr, "value", "ss", "mm", "hh", "dy", "mo", "year", "z", "da +te" ); foreach my $string (@dates) { # format datetime field from any valid datetime input # default time zone is used if timezone is not included in string my @datetime = strptime( $string, $DEFAULT_TIME_ZONE ); # error if date is not correctly parsed if ( scalar @datetime == 0 ) { die( "ERROR ====> invalid datetime ($string), " . "datetime format should be YYYY-MM-DD HH:MM:SS" +); } my ( $ss, $mm, $hh, $day, $month, $year, $zone ) = @datetime; if ( $year < 1000 ) { $year += 1900; } my %datetimehash = ( year => $year, month => $month + 1, day => $day, hour => $hh, minute => $mm, second => $ss, nanosecond => 0, time_zone => $zone, ); my $date = DateTime->new(%datetimehash); printf( $pfrm, $string, $ss, $mm, $hh, $day, $month, $year, $zone, + $date ); } exit 0;

Here is the output:

value ss,mm,hh,dy,mo,year,z date 0618-01-01 00:00:00 00,00,00,01,00,2518,0 2518-01-01T00:00:00 1066-10-14 00:00:00 00,00,00,14,09,1066,0 1066-10-14T00:00:00 1899-06-24 09:44:00 00,44,09,24,05,1899,0 1899-06-24T09:44:00 1900-12-31 23:59:59 59,59,23,31,11,1900,0 1900-12-31T23:59:59 1901-01-01 00:00:00 00,00,00,01,00,1901,0 1901-01-01T00:00:00 1960-12-31 23:59:59 59,59,23,31,11,1960,0 1960-12-31T23:59:59 1966-06-24 09:44:00 00,44,09,24,05,1966,0 1966-06-24T09:44:00 1968-12-31 23:59:59 59,59,23,31,11,1968,0 1968-12-31T23:59:59 1969-01-01 00:00:00 00,00,00,01,00,1969,0 1969-01-01T00:00:00 1969-12-31 23:59:59 59,59,23,31,11,1969,0 1969-12-31T23:59:59 1970-01-01 00:00:01 01,00,00,01,00,1970,0 1970-01-01T00:00:01 2000-01-01 00:00:00 00,00,00,01,00,2000,0 2000-01-01T00:00:00 2017-06-24 23:59:59 59,59,23,24,05,2017,0 2017-06-24T23:59:59 2018-06-24 09:44:00 00,44,09,24,05,2018,0 2018-06-24T09:44:00 2238-06-24 09:44:00 00,44,09,24,05,2238,0 2238-06-24T09:44:00

Thank you all for the help. It took a bit to get my head wrapped around this one.

EDIT: Solution 2

Based on discussion with thanos1983, I explored using the Date::Manip module to parse datetimes.

This simplified parsing variable inputs greatly. It even handles 2-digit years correctly.

Here is the updated code and output:

#!/usr/bin/perl use strict; use warnings; use Date::Manip; use feature 'say'; my $DEFAULT_TIME_ZONE = "GMT"; my @dates = ( "0618-01-01 00:00:00", # intpreted as 2518-01-01 "1066-10-14 00:00:00", "1899-06-24 09:44:00", "1900-12-31 23:59:59", "1901-01-01 00:00:00", "1960-12-31 23:59:59", "1968-12-31 23:59:59", "1969-01-01 00:00:00", "1969-12-31 23:59:59", "1970-01-01 00:00:01", "2000-01-01 00:00:00", "2018-02-20 00:00:00", "20180220", "02/20/2018", "02/20/18", # interpreted as 1918-02-20 "2018-02-20", "2238-02-20 09:44:00" ); # define format for printf statements say "Well formatted date Variable input date"; say UnixDate( ParseDate($_), '%Y-%m-%d %T' ) . qq{ $_} for (@dates); exit 0; __END__ $ format_date.pl Well formatted date Variable input date 0618-01-01 00:00:00 0618-01-01 00:00:00 1066-10-14 00:00:00 1066-10-14 00:00:00 1899-06-24 09:44:00 1899-06-24 09:44:00 1900-12-31 23:59:59 1900-12-31 23:59:59 1901-01-01 00:00:00 1901-01-01 00:00:00 1960-12-31 23:59:59 1960-12-31 23:59:59 1968-12-31 23:59:59 1968-12-31 23:59:59 1969-01-01 00:00:00 1969-01-01 00:00:00 1969-12-31 23:59:59 1969-12-31 23:59:59 1970-01-01 00:00:01 1970-01-01 00:00:01 2000-01-01 00:00:00 2000-01-01 00:00:00 2018-02-20 00:00:00 2018-02-20 00:00:00 2018-02-20 00:00:00 20180220 2018-02-20 00:00:00 02/20/2018 2018-02-20 00:00:00 02/20/18 2018-02-20 00:00:00 2018-02-20 2238-02-20 09:44:00 2238-02-20 09:44:00

Replies are listed 'Best First'.
Re^2: Date::Parse - how to correctly parse dates between 1901 and 1969
by Anonymous Monk on Feb 20, 2018 at 04:55 UTC

      The input can be in variable formats:

      my @dates = ( "2018-02-20 00:00:00", "20180220", "02/20/2018", "02/20/18", # interpreted as 1918-02-20 "2018-02-20" );

      Date::Parse handles those (with some massaging). DateTime::Format::Strptime requires well formatted dates to begin with.

      Am I missing a module of DateTime or Time::Piece that can handle the different input formats?

        #!/usr/bin/perl -- use strict; use warnings; use DateTime; use DateTime::Format::Natural; my @dates = ( "2018-02-20 00:00:00", "20180220", "02/20/2018", "02/20/18", # interpreted as 1918-02-20 "2018-02-20", "today", "now", ); my $dfn = DateTime::Format::Natural->new; print $dfn->parse_datetime($_),$/ for @dates; __END__ 2018-02-20T00:00:00 2018-02-20T21:12:19 2018-02-20T21:12:19 2018-02-20T21:12:19 2018-02-20T00:00:00 2018-02-20T00:00:00 2018-02-20T21:12:19
        So you simply make up a pattern for each format ... ... Could try ..Format::Natural

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1209520]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (4)
As of 2024-04-20 04:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found