Perl Time, date conversion Error - perl

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;

Related

Perl Past Date How To?

I have a perl script that is getting the current time coming through in a but I am also looking to get the date 45 days prior to the current time as well. Here is what I have:
*already tried using date::calc DHMS which is why the second is formatted the way it is but it keeps returning an error
# get the current time stamp
use POSIX qw( strftime );
my $current_time = strftime("%Y-%m-%d %H:%M:%S", localtime);
print "\n$current_time\n";
# get the date 45 days ago
my $time = strftime("%Y, %m, %d, %H, %M, %S", localtime);
print "\n$time\n\n";
Preferably use DateTime, DateManip, or Date::Calc, but you can also:
use POSIX 'strftime', 'mktime';
my ($second,$minute,$hour,$day,$month,$year) = localtime();
my $time_45_days_ago = mktime($second,$minute,$hour,$day-45,$month,$year);
print strftime("%Y-%m-%d %H:%M:%S", localtime $time_45_days_ago), "\n";
Have you tried DateTime?
my $now = DateTime->now( time_zone => 'local' );
my $a_while_ago = DateTime->now( time_zone => 'local' )->subtract( days => 45 );
print $a_while_ago->strftime("%Y, %m, %d, %H, %M, %S\n");
use DateTime;
my $now = DateTime->now( time_zone=>'local' );
my $then = $now->subtract( days => 45 );
print $then->strftime("%Y, %m, %d, %H, %M, %S");
Set the time_zone, it's important here.
Here's a simple solution using DateTime:
use strict;
use warnings;
use DateTime;
my $forty_five_days_ago = DateTime->now(time_zone=>"local")->subtract(days => 45);
my $output = $forty_five_days_ago->ymd(", ");
$output .= ", " . $forty_five_days_ago->hms(", ");
print "$output\n";

perl date calculation with dates of the format 2012-02-03 00:00:00

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

Convert Old Unix Date to Perl and compare

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);
}

In Perl, how can I ensure that a string corresponds to a valid date?

I was wondering if there is a simple way in Perl to ensure that a date string corresponds to a valid date.
For example, 2012 02 30 is incorrect because it doesn't exist.
The DateTime module will validate dates when creating a new object.
$ perl -we 'use DateTime; my $dt;
eval { $dt = DateTime->new(
year => 2012,
month => 2,
day => 30);
}; print "Error: $#" if $#;'
Error: Invalid day of month (day = 30 - month = 2 - year = 2012) at -e line 1
It also works dynamically on a given DateTime object:
$dt->set(day => 30);
Something like this using Class::Date should work
perl testit.pl
Range check on date or time failed
use Class::Date;
my $d=Class::Date->new('2021-02-30');
unless ( $d->error ) {
print "good date\n";
} else {
print $d->errstr(). "\n";
}
exit;
Check here:
http://www.perlmonks.org/?node_id=564594
I believe you'll get the answers you seek from the wise monks.
You can do this through the use of POSIX mktime, but apparently only if you have a flexible-enough implementation of mktime.
What I do is plug the numbers in and then use local time to get them back and if I get the same day value back, it's a valid number. So, given your string:
my ( $y, $m, $d ) = split ' ', $date_string;
die "$date_string is not a valid date!"
unless ( $d == ( localtime mktime( 0, 0, 0, $d, $m - 1, $y - 1900 ))[3] )
;
See, in the versions of mktime that I'm used to, mktime( 0, 0, 0, 30, 1, 112 ) would make '2012-03-01' and 30 != 1
You can also use Time::Local:
#!/usr/bin/env perl
use strict; use warnings;
use Carp qw( croak );
use Time::Local qw( timegm );
my #to_check = ('1927 06 18', '2012 02 30');
for my $date ( #to_check ) {
printf "'%s' is %s\n", $date, check_date($date) ? 'valid' : 'invalid';
}
sub check_date {
my ($date) = #_;
my ($year, $month, $mday) = split ' ', $date;
my $ret;
eval {
$ret = timegm(0, 0, 0, $mday, $month - 1, $year - 1900);
};
return $ret && $ret;
}
May be this will help too:
use Time::Piece; #in perl CORE distro since 5.10
use 5.010;
say Time::Piece->strptime("2011-02-29","%Y-%m-%d")->strftime("%Y-%m-%d");
#2011-03-01
say Time::Piece->strptime("2012-02-29","%Y-%m-%d")->strftime("%Y-%m-%d");
#2012-02-29

Perl - How to convert a date?

How to convert date format YYYY-MM-DDTHH:MM:SSZ to YYYY-MM-DD HH:MM + 8 hours?
For example:
Input: 2011-07-07T18:05:45Z
Output: 2011-07-08 02:05
Let's start with Rahul's snippet, and add in the date math and output formatting...
use DateTime;
use DateTime::Format::ISO8601;
use DateTime::Format::Strptime;
my $string = '2011-07-07T18:05:45Z';
my $dt = DateTime::Format::ISO8601->parse_datetime( $string );
die "Impossible time" unless $dt;
my $formatter = new DateTime::Format::Strptime(pattern => '%Y-%m-%d %T');
$dt->add( hours => 8 )->set_formatter($formatter);
print "$dt\n";
I've added the use of DateTime::Format::Strptime, in order to specify the desired output format.
Then I've added three more lines:
First I create a formatter, and feed it the output pattern I desire.
Next I add eight hours to the original date, and I assign the output
formatter by chaining the set_formatter() call to the add() call.
Then I print it.
Are you using the DateTime modules?
Specifically, here's a link to DateTime::Format::ISO8601 that reads/writes ISO 8601 format you mentioned as your input.
If you don't have DateTime, you surely have Time::Piece:
use strict;
use warnings;
use Time::Piece;
use Time::Seconds qw(ONE_HOUR);
my $str = '2011-07-07T18:05:45Z';
my $t = Time::Piece->strptime($str, "%Y-%m-%dT%TZ");
$t += 8 * ONE_HOUR;
print $t->strftime("%Y-%m-%d %H:%M"),"\n";
Taken From
How can I validate a "yyyy-MM-dd'T'HH:mm:ssZ" date/timestamp in UTC with Perl?
use DateTime;
use DateTime::Format::ISO8601;
my $string = '2010-02-28T15:21:33Z';
my $dt = DateTime::Format::ISO8601->parse_datetime( $string ); die "Impossible time" unless $dt;
It doesn't work, result is 2010-02-28T15:21:33
Then, do it the hard way...
use Time::Local
use warnings;
use strict;
$time = '2010-02-28T15:21:33Z';
my ($year, month, day) = split (/-/, $time)
$year -= 1900; #Year is an offset of 1900
$month -= 1; #Months are 0 - 11
#Now split the time off of the day (DDTHH:MM:SS)
$day = substr($day, 0, 2);
time = substr($day, 3)
#Now split the time
(my $hour, $minute, $second) = split(/:/, $time);
$second =~ s/Z$//; #Remove Z
my $time_converted = timelocal($second, $minute, $hour, $day, $month, $year);
#Now you have the time, Add eight hours
my $hours_in_seconds = 8 * 60 * 60;
$time_converted += $hours_in_seconds;
# Almost done: Convert time back into the correct array:
($second, $minute, $hour, $day, $month, $year) = localtime($time_converted);
$year += 1900;
$month += 1;
# Now, reformat:
my $formatted_time = sprint (%04d-%02d-%02d %02d:%02d),
$year, $month, $day, $hour, $minute;