Perl Past Date How To? - perl

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";

Related

How can I change date to swedish time zone in a Perl script?

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

Perl - DateTime module - How to compare two DateTime's for a 24 hour time lapse

I saved a DateTime stamp to my database:
2015-02-23T16:59:25
I have a new DateTime Stamp which represents current time.
2015-02-24T16:59:25
I need to compare the two with DateTime to check if 24 hours has passed.
#!/usr/bin/perl
use strict;
use DateTime;
my $longenough;
Testing123();
exit;
sub Testing123{
my $yesterday = DateTime->now;
$yesterday->add( days => -1 );
#This $yesterday time gets saved to my database.
#For question purposes, I'll just pass it along instead of from db.
CheckLapse($yesterday);
if ($longenough eq 'y'){ print qq~24 hours have passed<br />~; }
else{print qq~24 hours have not passed<br />~;}
}
sub CheckLapse{
$yesterday = shift;
my $now = DateTime->now;
# leftovers from many many hours of different attempts from my old bramaged drain
# my $elapse = $now - $yesterday;
# $now->subtract_duration( $yesterday ) ;
# $TimeLapse = $elapse->in_units('seconds'); #Left over from another try
# print "Elapsed time : ".$elapse->in_units('seconds')."m\n";
## I need to compare the two times and pass $longenough y/n back:
if ($TimeLapse >= [24 hours whatever seconds minutes to do calc]){
$longenough = 'y';
}
else {$longenough = 'n';}
return $longenough;
}
exit;
I have read and read the cpan DateTime docs and tried everything except for obviously, the correct solution.
I just keep getting errors like "Can't call method "yada yada" without a package or object reference".
Could someone educate me here?
You can construct a DateTime object from your string as follows:
use DateTime::Format::Strptime qw( );
my $dt_format = DateTiFormat::Strptime->new(
pattern => '%Y-%m-%dT%H:%M:%S',
time_zone => 'local',
on_error => 'croak',
);
my $dt = $dt_format->parse_datetime('2015-02-23T16:59:25');
Then you can check if it's 24 hours old like this:
my $now = DateTime->now( time_zone => $dt->time_zone );
if ($dt->clone->add( hours => 24 ) >= $now) {
# It's been more than 24 hours.
...
}
Now, the above does a lot of work every time you check if 24 hours have passed. If you do this repeatedly, you can use reduce the workload as follows:
use DateTime::Format::Strptime qw( );
my $dt_format = DateTime::Format::Strptime->new(
pattern => '%Y-%m-%dT%H:%M:%S',
time_zone => 'local',
on_error => 'croak',
);
my $dt = $dt_format->parse_datetime('2015-02-23T16:59:25');
$dt->add( hours => 24 );
my $target_time = $dt->epoch;
Then check then simplifies to
if ($target_time >= time)
# It's been more than 24 hours.
...
}
You probably want ->add( days => 1 ) (same time on the next calendar day) instead of ->add( hours => 24 ).
The DateTime module is enormous and slow, and is a complete toolbox for anything date-time related. Time::Piece is a much lighter weight core module (so it shouldn't need to be installed) and is quite up to this task.
Here's a demonstration of a solution using Time::Piece
use strict;
use warnings;
use 5.010;
use Time::Piece ();
use Time::Seconds 'ONE_DAY';
my ($t1, $t2) = qw/
2015-02-23T16:59:25
2015-02-24T16:59:25
/;
my $diff = delta_time($t1, $t2);
say $diff >= ONE_DAY ? 'over one day' : 'less than one day';
sub delta_time {
my ($t1, $t2) = map Time::Piece->strptime($_, '%Y-%m-%dT%H:%M:%S'), #_;
return $t2 > $t1 ? $t2 - $t1 : $t1 - $t2;
}
output
over one day

How to calculate the difference between two timestamp strings in Perl

I searched through all the possible questions but couldn't find the answer,
so can Perl experts help me on this one?
I have two timestamps like 05/25/2011 05:22:03 PM and 05/25/2011 05:34:08 PM. They are stored in string form.
my $str1 = '05/25/2011 05:22:03';
my $str2 = '05/25/2011 05:34:08';
The latter being the time of a job ending and former being the time it started.
How do I find out the difference in dates and time? The dates are the same in this case but they could differ as well.
I recommend that you use the Time::Piece module. It has been a core module since the release of version 9.5 of Perl 5, so it shouldn't need installing.
This code demonstrates
use strict;
use warnings;
use Time::Piece;
my $str1 = 'Execution started at 05/25/2011 05:22:03 PM';
my $str2 = 'Execution completed at 05/25/2011 05:34:08 PM';
my #times = map Time::Piece->strptime(/(\d.+M)/, '%m/%d/%Y %H:%M:%S %p'), $str1, $str2;
my $delta = $times[1] - $times[0];
print $delta->pretty;
output
12 minutes, 5 seconds
You can take advantage of DateTime and its subtract_datetime() method, which returns a DateTime::Duration object.
use Date::Parse;
use DateTime;
my $t1 = '05/25/2011 05:22:03';
my $t2 = '05/25/2011 05:34:08';
my $t1DateTime = DateTime->from_epoch( epoch => str2time( $t1 ) );
my $t2DateTime = DateTime->from_epoch( epoch => str2time( $t2 ) );
my $diff = $t2DateTime->subtract_datetime( $t1DateTime );
print "Diff in minutes: " . $diff->in_units('minutes') . "\n";
print "Diff in hours: " . $diff->in_units('hours') . "\n";
print "Diff in months: " . $diff->in_units('months') . "\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

How can I change the timezone of a datetime value in Perl?

Using this function:
perl -e 'use Time::Local; print timelocal("00","00","00","01","01","2000"),"\n";'
It will return an epochtime - but only in GMT - if i want the result in GMT+1 (which is the systems localtime(TZ)), what do i need to change?
Thanks in advance,
Anders
use DateTime;
my $dt = DateTime->now;
$dt->set_time_zone( 'Europe/Madrid' );
There is only one standard definition for epochtime, based on UTC, and not different epochtimes for different timezones.
If you want to find the offset between gmtime and localtime, use
use Time::Local;
#t = localtime(time);
$gmt_offset_in_seconds = timegm(#t) - timelocal(#t);
While Time::Local is a reasonable solution, you may be better off using the more modern DateTime object oriented module. Here's an example:
use strict;
use DateTime;
my $dt = DateTime->now;
print $dt->epoch, "\n";
For the timezones, you can use the DateTime::TimeZone module.
use strict;
use DateTime;
use DateTime::TimeZone;
my $dt = DateTime->now;
my $tz = DateTime::TimeZone->new(name => "local");
$dt->add(seconds => $tz->offset_for_datetime($dt));
print $dt->epoch, "\n";
CPAN Links:
DateTime
You just need to set the timezone. Try:
env TZ=UTC+1 perl -e 'use Time::Local; print timelocal("00","00","00","01","01","2000"),"\n";'
Time::Local::timelocal is the inverse of localtime. The result will be in your host's local time:
$ perl -MTime::Local -le \
'print scalar localtime timelocal "00","00","00","01","01","2000"'
Tue Feb 1 00:00:00 2000
Do you want the gmtime that corresponds to that localtime?
$ perl -MTime::Local' -le \
'print scalar gmtime timelocal "00","00","00","01","01","2000"'
Mon Jan 31 23:00:00 2000
Do you want it the other way around, the localtime that corresponds to that gmtime?
$ perl -MTime::Local -le \
'print scalar localtime timegm "00","00","00","01","01","2000"'
Tue Feb 1 01:00:00 2000
An other example based on DateTime::Format::Strptime
use strict;
use warnings;
use v5.10;
use DateTime::Format::Strptime;
my $s = "2016-12-22T06:16:29.798Z";
my $p = DateTime::Format::Strptime->new(
pattern => "%Y-%m-%dT%T.%NZ",
time_zone => "UTC"
);
my $dt = $p->parse_datetime($s);
$dt->set_time_zone("Europe/Berlin");
say join ' ', $dt->ymd, $dt->hms; # shows 2016-12-22 07:16:29
The Algorithm
If you want to change a time value from one timezone to another timezone, you must be able to indicate both timezones.
After all, if you set if you want to convert "12:30" to GMT or US/Eastern or Venezuelan time, which means adding/subtracting some amount of hours or hours and minutes, you need to know what timezone is the starting time zone, otherwise, the calculation won't know how much to add or subtract.
If you use DateTime->now;, the timezone is defaulted to the system-time, which may not be the timezone you want to convert from.
In the below code, I demonstrate how to initialize the datetime object to the right starting timezone (fromtimezone) and how to convert that time to the ending timezone (totimezone)...
Working Code
I could not find a Perl sandbox online with the DateTime CPAN module installed.
use strict;
use DateTime;
sub convertTimeZonesForTime {
my ($args) = #_;
my $time = $args->{time};
my $date = $args->{date};
my $totimezone = $args->{totimezone};
my $fromtimezone = $args->{fromtimezone};
my $format = $args->{format} || '%H:%M:%S';
my ($year, $month, $day) = map {int $_} split('-', $date);
my ($hour, $minute, $second) = map {int $_} split(':', $time);
$year ||= 1999 if !defined $year;
$month ||= 1 if !defined $month;
$day ||= 1 if !defined $day;
$hour ||= 12 if !defined $hour;
$minute ||= 30 if !defined $minute;
$second ||= 0 if !defined $second;
my $dt = DateTime->new(
year=>$year,
month=>$month,
day=>$day,
hour=>$hour,
minute=>$minute,
second=>$second,
time_zone => $fromtimezone,
);
my $formatter = new DateTime::Format::Strptime(pattern => $format);
$dt->set_formatter($formatter);
$dt->set_time_zone($totimezone);
return "$dt";
}
print(convertTimeZonesForTime({
'totimezone'=>'America/Denver',
'fromtimezone'=>'US/Eastern',
'time'=>'12:30:00',
}));
Output:
10:30:00