What is the best way to convert yyyymmddhhmmss to YYYY-MM-DD hh:mm:ss and back in perl?
for example: 20130218165601 to 2013-02-18 16:56:01 and back?
(can https://metacpan.org/module/Rose::DateTime do it)?
without regexp, if possible ;)
A module is overkill for this.
# Packed -> ISO
(my $iso_date = $packed_date) =~
s/^(....)(..)(..)(..)(..)(..)\z/$1-$2-$3 $4:$5:$6/s;
# ISO -> Packed
(my $packed_date = $iso_date) =~
s/^(....)-(..)-(..) (..):(..):(..)\z/$1$2$3$4$5$6/s;
Rose::DateTime cannot parse the "packed" format as intended, but you could use DateTime::Format::Strptime.
use DateTime::Format::Strptime qw( );
my $packed_format = DateTime::Format::Strptime->new(
pattern => '%Y%m%d%H%M%S',
on_error => 'croak',
);
my $iso_format = DateTime::Format::Strptime->new(
pattern => '%Y-%m-%d %H:%M:%S',
on_error => 'croak',
);
# Packed -> ISO
my $iso_date = $iso_format->format_datetime(
$packed_format->parse_datetime($packed_date)
);
# ISO -> Packed
my $packed_date = $packed_format->format_datetime(
$iso_format->parse_datetime($iso_date)
);
Quick solution with sprintf.
my $date = sprintf "%s%s-%s-%s %s:%s:%s", $string =~ /(..)/g;
And back:
my $foo = join '', $date =~ /\d+/g;
without regular expressions, you can just use substr to grab the characters you want:
$year = substr $d, 0, 4;
$month = substr $d, 4, 2;
...
$secs = substr $d, 12, 2;
Related
How can I convert a date in format '20170119121941Z' to Swedish time zone in a Perl script?
My current snippet is:
sub get_Date {
my $content = shift;
my $iso8601 = DateTime::Format::ISO8601 -> new;
my $dt = $iso8601->parse_datetime( $content );
###Set the time zone to "Europe/Stockholm" .
$dt->set_time_zone("Europe/Stockholm");
my $dayofmonth = $dt->strftime("%d");
$dayofmonth =~ s/^0//;
my $hour = $dt->strftime("%I");
$hour =~ s/^0//;
my $ISODate = $dt->strftime("%b " . $dayofmonth . ", %Y, " . $hour . ":%M %p ", localtime(time));
return($ISODate);
}
The output you are getting is
Invalid date format: 20170119121941Z
Your code is failing with that message because 20170119121941Z doesn't match a valid ISO8601 format.
There's also the issue that you used strftime correctly twice, then did something nonsense for the third use.
Solution:
use strict;
use warnings;
use feature qw( say state );
use DateTime::Format::Strptime qw( );
sub localize_dt_str {
my ($dt_str) = #_;
state $format = DateTime::Format::Strptime->new(
pattern => '%Y%m%d%H%M%S%Z',
on_error => 'croak',
);
my $dt = $format->parse_datetime($dt_str);
$dt->set_time_zone('Europe/Stockholm');
return $dt->strftime('%b %e, %Y, %l:%M %p');
}
say localize_dt_str('20170119121941Z'); # Jan 19, 2017, 1:19 PM
I have a series of string pairs
$str1 = '2016-09-29 10:02:29';
$str2 = '2016-09-29 10:05:45';
the first string is always before the second
I would like to calucate how much seconds have passed between the two timestamps
the above would be 196 seconds
is there a quick way to calculate the difference between two timestamps?
(obsolete) Update: I wrote the following code and i m getting this error
my $format = DateTime::Format::Strptime->new(
format => '%Y-%m-%d %H:%M:%S',
time_zone => 'UTC',
on_error => 'croak',
);
The following parameter was passed in the call to DateTime::Format::Strptime::new but was not listed in the validation options: format
at /usr/software/lib/perl5/site_perl/5.8.8/DateTime/Format/Strptime.pm line 130.
DateTime::Format::Strptime::new(undef, 'format', '%Y-%m-%d %H:%M:%S', 'time_zone', 'UTC', 'on_error', 'croak') called at ./show_startup.pl line 150
Using DateTime (and DateTime::Format::Strptime):
use DateTime::Format::Strptime qw( );
my $str1 = '2016-09-29 10:02:29';
my $str2 = '2016-09-29 10:05:45';
my $format = DateTime::Format::Strptime->new(
pattern => '%Y-%m-%d %H:%M:%S',
time_zone => 'local', # If they are local time timestamps
-or-
time_zone => 'UTC', # If they are UTC timestamps
on_error => 'croak',
);
my $dt1 = $format->parse_datetime($str1);
my $dt2 = $format->parse_datetime($str2);
my $seconds = $dt2->delta_ms($dt1)->in_units('seconds');
Using Time::Piece:
use Time::Piece qw( localtime );
my $str1 = '2016-09-29 10:02:29';
my $str2 = '2016-09-29 10:05:45';
my $format = '%Y-%m-%d %H:%M:%S';
my $t1 = Time::Piece->strptime($str1, $format); # If they are UTC timestamps
my $t2 = Time::Piece->strptime($str2, $format); #
-or-
my $t1 = localtime->strptime($str1, $format); # If they are local time timestamps
my $t2 = localtime->strptime($str2, $format); #
my $seconds = ($t2 - $t1)->seconds;
Here's one way, using the Time::Piece module, which comes with Perl:
use strict;
use warnings;
use Time::Piece;
my $str1 = '2016-09-29 10:02:29';
my $str2 = '2016-09-29 10:05:45';
my $format = '%Y-%m-%d %H:%M:%S';
my $t1 = Time::Piece->strptime($str1, $format);
my $t2 = Time::Piece->strptime($str2, $format);
my $seconds = ($t2 - $t1)->seconds;
print "Result: $seconds\n";
This code doesn't take the local timezone into account, i.e. it assumes the times are in UTC.
This is a perl script for sql data pulling each day for 100 days starting from Oct 1 and
SQL is quite picky in date formats(yyyy-mm-dd), so I've written the script as follows.
However, at a specific day, on 2011-11-06, the time to date conversion is incorrect, and start and end date become the same.
$srt_date='2011-11-06'
$end_date='2011-11-06'
I don't know if this is perl error or something else.
use DBI;
use DBD::Oracle qw(:ora_types);
use Compress::Zlib;
use FileHandle;
use Date::Parse;
use Date::Format;
$st_day=str2time('2011-10-1');
#days=(0..100);
foreach $daynum (#days){
$dt1 = $st_day+3600*(24*$daynum);
$dt2 = $st_day+3600*(24*($daynum+1));
$srt_date = time2str("%d-%h-%Y", $dt1);
$end_date = time2str("%d-%h-%Y", $dt2);
print $srt_date, ',' ,$end_date, '\n';
my $sqlGetEid = "select x,y from z where DATETIME>='$srt_date' and DATETIME<'$end_date'";
}
Here's how DateTime handles the DST transitions correctly:
use strict; #ALWAYS!
use warnings; #ALWAYS!
use DateTime;
my $st_day = '2011-10-1';
my ($year, $month, $day) = split /-/, $st_day;
my $dt = DateTime->new(
year => $year,
month => $month,
day => $day,
time_zone => 'local',
);
my #days = 0..100;
foreach my $daynum (#days) {
my $dt1 = $dt->ymd;
my $dt2 = $dt->add(days => 1)->ymd;
printf "%s,%s\n", $dt1, $dt2;
}
I'm not sure what you want to achieve exactly, but why bother executing 100 SQL statements when you can get away with something like:
SELECT trunc(datetime, 'DD') truncdate, x,y
FROM z WHERE datetime between '2011-10-01'
AND to_date('20111001', 'YYYYMMDD') + 99
Populate a hash with truncdate as key, and if your dates are ISO 8601, you'll get the same ordering by looping over the hash with a regular (cmp) sort.
EDIT: I'll clarify how you could do this:
my $sth = $mdbh->prepare("SELECT trunc(datetime, 'DD') truncdate, x,y
FROM z WHERE datetime between '2011-10-01'
AND to_date('20111001', 'YYYYMMDD') + 99
ORDER BY truncdate");
$sth->execute();
my $lastdate = "";
my $fh;
while (my $row = $sth->fetchrow_hashref()) {
# If new date, create new file
if ($row->{truncdate} ne $lastdate) {
close($fh) if $fh;
open($fh, ">", "$row->{truncdate}.csv") or die "Unable to create file '$row->{truncdate}.csv': $!\n";
}
print $fh "$row->{x},$row->{y}\n";
$lastdate = $row->{truncdate};
}
close($fh) if $fh;
I need some help with date calculations in perl with dates for the format "2012-02-03 00:00:00". In particular is there a tool I could use to just increment the days and it switches to month and year correctly? Thanks.
See DateTime.
#!/usr/bin/env perl
use strict; use warnings;
use DateTime;
my $ts = '2012-02-03 00:00:00';
my ($y, $m, $d) = ($ts =~ /([0-9]{4})-([0-9]{2})-([0-9]{2})/);
my $dt = DateTime->new(year => $y, month => $m, day => $d);
$dt->add( months => 2, days => 3 );
print $dt->strftime('%Y-%m-%d %H:%M:%S'), "\n";
It's actually a little cleaner to use a DateTime::Format class, and you get error checking for free.
use DateTime::Format::Strptime qw( );
my $format = DateTime::Format::Strptime->new(
pattern => '%Y-%m-%d %H:%M:%S',
time_zone => 'local',
on_error => 'croak',
);
my $ts = '2012-02-03 00:00:00';
my $dt = $format->parse_datetime($ts);
$dt->add( months => 2, days => 3 );
print $format->format_datetime($dt), "\n";
The Time::Piece module is a standard part of the Perl installation and probably does all that you need.
This program uses your example date and adds two months and three days, then a further 400 days. Two alternative ways of displaying the values are shown
use strict;
use warnings;
use Time::Piece;
use Time::Seconds 'ONE_DAY';
my $format = '%Y-%m-%d %H:%M:%S';
my $dt = Time::Piece->strptime('2012-02-03 00:00:00', $format);
$dt = $dt->add_months(2);
$dt += 3 * ONE_DAY;
print $dt->strftime($format), "\n";
$dt += 400 * ONE_DAY;
printf "%s %s\n", $dt->ymd, $dt->hms;
output
2012-04-06 00:00:00
2013-05-11 00:00:00
This is all perfectly possible within core using the POSIX time-handling functions.
The standard POSIX::mktime function already copes with denormalised values, and can correct for days/months out of range. Additionally, POSIX::strftime actually calls this on the given values before formatting them, so it will adjust correctly.
use POSIX qw( strftime mktime );
use POSIX::strptime qw( strptime );
my $format = "%Y-%m-%d %H:%M:%S";
my #t = strptime( "2012-02-03 00:00:00", $format );
#t = #t[0..5]; # Throw away wday and yday
$t[3] += 3; # mday
$t[4] += 2; # mon
say strftime $format, #t;
$t[3] += 400; # mday
say strftime $format, #t;
Gives
2012-04-06 00:00:00
2013-05-11 00:00:00
Requirement - I have file name called "Rajesh.1202242219". Numbers are nothing but a date "date '+%y''%m''%d''%H''%M'" format.
Now I am trying to write a perl script to extract the numbers from file name and compare with current system date and time and based on output of this comparison, print some value using perl.
Approach:
Extract the Digit from File name:
if ($file =~ /Rajesh.(\d+).*/) {
print $1;
}
Convert this time into readable time in perl
my $sec = 0; # Not Feeded
my $min = 19;
my $hour = 22;
my $day = 24;
my $mon = 02 - 1;
my $year = 2012 - 1900;
my $wday = 0; # Not Feeded
my $yday = 0; # Not Feeded
my $unixtime = mktime ($sec, $min, $hour, $day, $mon, $year, $wday, $yday);
print "$unixtime\n";
my $readable_time = localtime($unixtime);
print "$readable_time\n";
find Current time and compare...
my $CurrentTime = time();
my $Todaydate = localtime($startTime);
But the problem here is, I am not getting solution of how to extract 2 digit from $1 and assign to $sec, $min, etc. Any help?
Also, if you have good approach for this problem statement, Please share with me
I like to use time objects to simplify the logic. I use Time::Piece here because it is simple and light weight (and part of the core). DateTime can be another choice.
use Time::Piece;
my ( $datetime ) = $file =~ /(\d+)/;
my $t1 = Time::Piece->strptime( $datetime, '%y%m%d%H%M' );
my $t2 = localtime(); # equivalent to Time::Piece->new
# you can do date comparisons on the object
if ($t1 < $t2) {
# do something
print "[$t1] < [$t2]\n";
}
Might as well teach DateTime::Format::Strptime to make the comparison much simpler:
use DateTime qw();
use DateTime::Format::Strptime qw();
if (
DateTime::Format::Strptime
->new(pattern => '%y%m%d%H%M')
->parse_datetime('Rajesh.1202242219')
< DateTime->now
) {
say 'filename timestamp is earlier than now';
} else {
say 'filename timestamp is later than now';
};
my ($year, $month, $day, $hour, $min) = $file =~ /(\d{2})/g;
if ($min) {
$year += 100; # Assuming 2012 and not 1912
$month--;
# Do stuff
}
I think unpack might be a better fit.
if ( my ( $num ) = $file =~ /Rajesh.(\d+).*/ ) {
my ( $year, $mon, $day, $hour, $min ) = unpack( 'A2 A2 A2 A2 A2', $num );
my $ts = POSIX::mktime( 0, $min, $hour, $day, $mon - 1, $year + 100 );
...
}
Using a module that parses dates might be nice. This code will parse the date and return a DateTime object. Refer to the documentation to see the many ways to manipulate this object.
use DateTime::Format::Strptime;
my $date = "1202242219";
my $dt = get_obj($date);
sub get_obj {
my $date = shift;
my $strp = DateTime::Format::Strptime->new(
pattern => '%y%m%d%H%M'
);
return $strp->parse_datetime($date);
}