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 !!
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);
}
Added code snippet after initial post | [reply] [d/l] |
Re: Converting to epoch time.
by cazz (Pilgrim) on Mar 30, 2005 at 15:23 UTC
|
| [reply] |
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. | [reply] |
|
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)
| [reply] |
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. | [reply] [d/l] [select] |
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. | [reply] [d/l] [select] |
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); | [reply] [d/l] |
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. | [reply] |
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. | [reply] [d/l] |
|
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 | [reply] [d/l] |
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
| [reply] |
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 | [reply] [d/l] |
|
|