http://www.perlmonks.org?node_id=443499

TASdvlper has asked for the wisdom of the Perl Monks concerning the following question:

All,

I've read some threads on how to convert to epoch time, but I can't seem to find the exact answer I'm looking for. I would like to convert a date/time in the following format '2005-03-28 12:00:00' (I'm looking at dates/times in the past, not just the current date/time) and convert it to epoch time.

I know this should be a easy one for most.

Thanks all !!

Replies are listed 'Best First'.
Re: Converting to epoch time.
by Limbic~Region (Chancellor) on Mar 30, 2005 at 15:23 UTC
    TASdvlper,
    Take a look at Time::Local. If you are sure your data is rigidly formatted as you describe, a simple regex and that module is all you need.
    #!/usr/bin/perl use strict; use warnings; use Time::Local; print scalar localtime( epoch( '2005-03-28 12:00:00' ) ); sub epoch { my $stamp = shift; my($yr, $mon, $day, $hr, $min, $sec) = split /[ :-]/, $stamp; --$mon; return timelocal($sec, $min, $hr, $day, $mon, $yr); }

    Cheers - L~R

    Added code snippet after initial post
Re: Converting to epoch time.
by cazz (Pilgrim) on Mar 30, 2005 at 15:23 UTC
Re: Converting to epoch time.
by ikegami (Patriarch) on Mar 30, 2005 at 15:24 UTC
    You could parse the line yourself and use the core module Time::Local. Alternatively, I hear Date::Manip does a good job at parsing a wide variety of date formats.
      Date::Manip works very well, but is very slow. I'd be wary of using it in any high-performance situation. (although the author does mention speed isn't it's concern, correctness/flexibility is)
Re: Converting to epoch time.
by duct_tape (Hermit) on Mar 30, 2005 at 23:34 UTC

    What others have said about splitting up the string with a regex and using Time::Local will work great if all you need is to convert the date to epoch. But if you need to do anything else with the date, then you may want to look at the DateTime modules.

    Note: Since your date does not include the timezone, you may need to specify that to the DateTime object if you need it to be in a certain timezone. By default it is parsed into UTC.

    Here is an example using DateTime::Format::Strptime with the specified format. This is a good module to use if you know the format for the date. Check the manpage for strftime for information about the format.

    use DateTime::Format::Strptime; use strict; my $datetime = "2005-03-28 12:00:00"; my $parser = DateTime::Format::Strptime->new(pattern => '%Y-%m-%d %H +:%M:%S'); my $dt = $parser->parse_datetime($datetime); print $dt->epoch;

    And here it is using DateTime::Format::HTTP in case your date can be in many different formats. Check the documentation for the list of the many formats that it knows how to parse.

    use DateTime::Format::HTTP; use strict; my $datetime = "2005-03-28 12:00:00"; my $dt = DateTime::Format::HTTP->parse_datetime($datetime); print $dt->epoch;

    Hope that this is helpful.

Re: Converting to epoch time.
by rbi (Monk) on Mar 30, 2005 at 15:39 UTC
    this (untested) should print the UTC date in the format you require:
    sub get_date { use Time::gmtime; my $tm = gmtime; return sprintf "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d", $tm->year+1900, ($tm->mon)+1, $tm->mday, $tm->hour, $tm->min, $tm->sec; }
    if you need the epoch time, for example to compute distances in time you can try (untested):
    sub epoch { use Time::Local; my $time = shift(); my ($tmp_year,$tmp_month,$tmp_day,$tmp_hour,$tmp_min,$tmp_sec) = un +pack('a4xa2xa2xa2xa2xa2',$time); return timegm($tmp_sec,$tmp_min,$tmp_hour,$tmp_day,$tmp_month-1,$tmp +_year-1900); }
    Hope this helps.
Re: Converting to epoch time.
by bageler (Hermit) on Mar 30, 2005 at 19:35 UTC
    I've fallen in love with Date::Calc myself.

    $time = Date::Calc::Mktime($year,$month,$day, $hour,$min,$sec);
Re: Converting to epoch time.
by trammell (Priest) on Mar 30, 2005 at 18:30 UTC
    One module that has worked for me in the past is Date::Parse.
Re: Converting to epoch time.
by VladSu (Acolyte) on Mar 30, 2005 at 23:30 UTC
    Hi.
    I wrote some functions to manipulate the date I hope it will useful for you too.
    use Time::Local qw(timelocal); # This function will return the date in a second from epoch time # Example: # my $timeInSec = getTimeInSec('2005-03-28 12:00:00'); # BUT # actually function work with format '2005-03-28' if you need full dat +e/time you have to make some changes. # If you will have problem with it please contact me. sub getTimeInSec { my $time = shift; my ( $year,$month,$day ); if ( $time =~ /^ (\d\d\d\d) # Parsing Year [-\x20.\/]? # Delimiter (1[0-2]|0?[1-9]) # Parsing Month [-\x20.\/]? # Delimiter (3[0-1]|[1-2][0-9]|0?[1-9]) # Parsing Day $ /x or die "Date format '$time' is wrong" ) { ( $year,$month,$day ) = ( $1,$2,$3 ); die "Date format '$time' is wrong" unless &checkDateFormat( $ +year,$month,$day ); } return timelocal(00,00,00,$day,$month-=1,$year-=1900); } # This function check if the given string is a correct date. sub checkDateFormat { my ( $year,$month,$day ) = @_; my %month_day = ('01' => 31 ,'03' => 31 ,'02' => &checkLeapYear($year) ,'04' => 30 ,'05' => 31 ,'06' => 30 ,'07' => 31 ,'08' => 31 ,'09' => 30 ,'10' => 31 ,'11' => 30 ,'12' => 31); $month = '0' . $month if length( $month ) == 1; return 1 if ($day <= $month_day{ $month }); return 0; } sub checkLeapYear { return 29 unless $_[0] % 400; return 28 unless $_[0] % 100; return 29 unless $_[0] % 4; return 28; } # To use this function you can write # printf "%04d-%02d-%02d %02d:%02d:%02d", &timenow(); # or # my $dateTime = sprintf "%04d-%02d-%02d %02d:%02d:%02d", &timenow(get +TimeInSec('2005-03-28 12:00:00')); sub timenow { my ($sec,$min,$hour,$day,$month,$year) = localtime(defined $_[0] ? + $_[0] : time); return $year+1900,$month+1,$day,$hour,$min,$sec; }

    Vlad.
      Very nice script. I changed some parts so it is more generic. Now I can convert dates to epoch-seconds and epoch-seconds back to different formatted dates. Nice exersice. I wonder if there is any module out there that can do the same thing.... ?

      Here it is:
      #! /usr/bin/perl package TimeFormat ; use strict ; use warnings ; require Exporter ; our ($VERSION, $ISA, @EXPORT) ; our @ISA = qw(Exporter); @EXPORT = qw( sec2date date2sec ) ; $VERSION = 1.0 ; our @month_day = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) ; # This function accepts epoch-seconds and returns a formatted date. sub sec2date { my ( $self, $time, $format) = @_ ; if ( ref ( $self ) !~ /TimeFormat/ ) { $format = $time ; $time = $self ; } else { $format = $self->{format} if ( ! defined $format ) ; } $format = "%Y-%m-%d %H:%M:%S" if ( @_ == 1 && ! defined $format ) +; # no format defined, use default my $mtime = $time ; my $year = 1970 ; # start year my ($month, $day, $julian) ; my $hour = "00" ; my $min = "00" ; my $sec = "00" ; my $msec = "0" ; if ( $time =~ /\./ ) { # compute fraction of seconds ($time, $msec) = ($time =~ /(\d+)\.(\d+)/) ; } my $lyear = 31622400 ; # 366 days (leap year) my $nyear = 31536000 ; # 365 days my $dyear ; # dummy var my %month_day ; # calculate year while ( $time >= 0 ) { &_checkFebruary($year) ; # fix @month_day array $dyear = (&_checkLeapYear($year) == 28 ? $nyear : $lyear) ; if ( $time - $dyear < 0 ) { last ; } else { $time -= $dyear ; $year ++ ; } } # calculate month/day and julian day $julian = int $time / 86400 + 1 ; # start = 001 (not 000) $time -= ($julian - 1) * 86400 ; $julian = "0" x (3 - length($julian)).$julian ; ($month, $day) = _getMonthDay($year, $julian) ; # calculate hour $hour = int $time / 3600 ; $time -= $hour * 3600 ; $hour = "0" x (2 - length($hour)).$hour ; # calculate min $min = int $time / 60 ; $time -= $min * 60 ; $min = "0" x (2 - length($min)).$min ; # calculate seconds and fraction of seconds $sec = "0" x (2 - length($time)).$time ; $msec = "0" x (3 - length($msec)).$msec ; # format output ($format = $format ) =~ s/\%Y/$year/g ; # xxxx ($format = $format ) =~ s/\%m/$month/g ; # 1-12 ($format = $format ) =~ s/\%d/$day/g ; # 01-31 ($format = $format ) =~ s/\%j/$julian/g ;# 001-366 ($format = $format ) =~ s/\%H/$hour/g ; # 00-23 ($format = $format ) =~ s/\%M/$min/g ; # 00-59 ($format = $format ) =~ s/\%S/$sec/g ; # 00-59 ($format = $format ) =~ s/\%s/$msec/g ; # 000-999 ($format = $format ) =~ s/\%E/$mtime/g ; # xxxxxxxxxxx.xxx return $format ; } # Accepted input dates # 2005-03-28 12:00:00 # 2005-03-28 # 2005-102 12:00:00 # 2005-102 # 2005102 12:00:00 # 2005102 sub date2sec { my ($self, $time) = @_ ; # object if ( ref ($self) !~ /TimeFormat/ ) { $time = $self ; } $time = $self->{date} if ! defined $time ; return 0 if ! defined $time ; my ( $year,$month, $day, $julian, $hour, $minute, $second ) ; # split date if ( $time =~ /\d{4}-?(\d{3})\s{0,}?(\d\d:\d\d:\d\d)?/ ) { ( $year, $julian, $hour, $minute, $second ) = ( $time =~ m/ (\d{4}) # year (?: # group the day - time portions -? # optional hyphen (\d{1,3}) # julian day (?: # group the time portions \s+ # one or more whitespace (\d\d) # hour : (\d\d) # minute : (\d\d.*) # second + fractions )? # time is optional )? # day - time is optional /xg ); return 0 unless _checkDateFormat( $year, $julian ); ($month, $day) = _getMonthDay($year,$julian) ; } elsif ( $time =~ /\d{4}-\d{2}-\d{2}\s{0,}?(\d\d:\d\d:\d\d)?/ + ) { ( $year, $month, $day, $hour, $minute, $second ) = ( $time =~ m/ (\d{4}) # year - # (\d{2}) # month - # (\d{2}) # day (?: # group the time portions \s+ # one or more whitespace (\d\d) # hour : (\d\d) # minute : (\d\d.*) # second + fractions )? # time is optional /xg ); return 0 unless _checkDateFormat( $year,$month,$day ); $julian = _getJulian($year, $month, $day) ; } else { return $time =~ /\d+/ || $time =~ /\d+\.\d+/ ? $time : 0 ; # 0 = f +ailed to parse data } # convert data2seconds, should be good until the year 2100 my $depoch = 0 ; $depoch += (($year - 1970) * 31536000); $depoch += (int (($year - 1969) / 4)) * 86400; # correct for le +ap years $depoch += (($julian - 1) * 86400); $depoch += ($hour * 3600); $depoch += ($minute * 60); $depoch += $second; return $depoch ; } sub setDate { my $self = shift ; $self->{date} = shift ; return 1 ; # OK } # predefine the output format (only works when using OOPerl) sub format { my $self = shift ; $self->{format} = shift ; if ( defined $self->{seconds} ) { $self->sec2date() ; } return 1 ; # OK } sub _getJulian { my ($year, $month, $day) = @_ ; &_checkFebruary($year) ; my $julian = 0 ; my $key ; for( my $i = 0; $i < $month -1; $i++) { $key = "0" x(2 - length($i)).$i ; $julian += $month_day[$key] ; } $julian += $day ; return $julian ; } sub _getMonthDay { my ($year, $julian) = @_ ; my $month ; _checkFebruary($year) ; for(my $i = 0; $i < 12; $i++) { if ( $month_day[$i] >= $julian ) { $month = $i + 1 ; last ; } $julian -= $month_day[$i] ; $month = $i + 1; } return ("0" x (2 - length($month)).$month,"0" x (2 - length($julia +n) ).$julian ) ; } # This function check if the given string is a correct date. sub _checkFebruary { $month_day[1] = _checkLeapYear(shift) ; } sub _checkDateFormat { my ( $year,$month,$day ) = @_; &_checkFebruary($year) ; if ( defined $day ) { # input format is %Y-%m-%d $month = '0'. $month if length( $month ) == 1; return $day <= $month_day[ $month - 1 ] ? 1 : 0 ; } else { my $jday = $month ; # month represents julian day return _checkLeapYear($year) == 28 ? $jday <= 365 ? 1 : 0 : $jd +ay <= 366 ? 1 : 0 ; } } sub _checkLeapYear { my ( $self, $year ) = @_ ; $year = $self if ( ! defined $year ) ; return 29 unless $year % 400; return 28 unless $year % 100; return 29 unless $year % 4; return 28; } # constructor sub new { my ( $class, $time, $format ) = @_ ; $time = date2sec($time) if defined $time ; # convert input to epoc +h-seconds return bless { seconds => $time, format => $format }, __PACKAGE__ +; } __END__


      Cheers
      Luca
Is that a datetime from a DB?
by TomDLux (Vicar) on Mar 31, 2005 at 15:39 UTC

    To me, that looks like a datetime string out of a database .... at least that's the way it looks in my work, with Informix. I would start by investigating your database to see if it can return the data as an epoch time. After all, it isn't stored as 'y-m-d h:m:s', but in a more condensed format.

    Tom

    --
    TTTATCGGTCGTTATATAGATGTTTGCA

Re: Converting to epoch time.
by Thelonious (Scribe) on Mar 31, 2005 at 18:46 UTC
    There's no need to reinvent any part of this wheel. Time::Piece:

    use Time::Piece; my $str = '2005-03-28 12:00:00'; my $fmt = '%Y-%m-%d %H:%M:%S'; print Time::Piece->strptime($str,$fmt)->epoch; __END__ 1112011200

    There are many other handy shortcut methods as well...

    hth