Novice here. Sorry and Thanks in advance.
I have a future date
(ie:2013-06-09 / $fields[12])
I need to subtract today
(ie:2013-03-08)
to get the number of days remaining.
I'd use DateTime. If you start with the date as a string, you could use DateTime::Format::Strptime to parse it.
use DateTime qw( );
use DateTime::Format::Strptime qw( );
my $format = DateTime::Format::Strptime->new(
pattern => '%Y-%m-%d',
time_zone => 'local',
on_error => 'croak',
);
my $ref = DateTime->today( time_zone => 'local' );
my $dt = $format->parse_datetime('2013-06-09');
my $days = $ref->delta_days($dt)->in_units('days');
print(
$dt < $ref ? "$days days ago\n" :
$dt > $ref ? "$days days from now\n" :
"today\n");
use the DateTime module:
use DateTime;
my $d1 = DateTime->new(
year => 2013,
month => 9,
day => 6
);
my $d2 = DateTime->now;
my $diff = $d2->delta_days($d1);
print $diff->delta_days, "\n"; # 182 (from 8/3/2013)
Related
I'm trying to use Perl's DateTime to subtract one day from another with a sign. I can get the days in between easily:
sub delta_days {
my $date1 = shift;
my $date2 = shift;
my $d1 = str_to_date_object($date1);
my $d2 = str_to_date_object($date2);
return $d2->delta_days($d1)->delta_days();
}
say delta_days('2021-10-21', '1980-8-20');
say delta_days('1980-8-20', '2021-10-21');
but both of these calls give the difference as 15037, without a sign.
Following the documentation https://metacpan.org/pod/DateTime
I see
# not DST
my $dt1 = DateTime->new(
year => 2003,
month => 5,
day => 6,
time_zone => 'America/Chicago',
);
# is DST
my $dt2 = DateTime->new(
year => 2003,
month => 11,
day => 6,
time_zone => 'America/Chicago',
);
my $dur = $dt2->subtract_datetime($dt1)->days();
I have checked a similar question in How to make DateTime::Duration output only in days? but I don't see how to get a signed difference there.
How can I get the difference in days with a sign?
Two issues: You're doing the subtraction on dates in the wrong order if you want a negative offset, and with the dates you're testing subtract_datetime() with, the duration object is going to have 0 days.
Compare with this version:
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw/say/;
use DateTime;
use Data::Dumper;
# not DST
my $dt1 = DateTime->new(
year => 2003,
month => 5,
day => 6,
time_zone => 'America/Chicago',
);
# is DST
my $dt2 = DateTime->new(
year => 2003,
month => 11,
day => 6,
time_zone => 'America/Chicago',
);
say $dt1;
say $dt2;
my $dur = $dt1->subtract_datetime($dt2);
print Dumper({$dur->deltas});
which when run produces
2003-05-06T00:00:00
2003-11-06T00:00:00
$VAR1 = {
'minutes' => 0,
'nanoseconds' => 0,
'months' => -6,
'seconds' => 0,
'days' => 0
};
Note the -6 months offset. DateTime::Duration objects will not convert between months and days according to the documentation so this looks like a dead end.
The delta_days method you looked at first returns the actual difference in days, but as noted it's an absolute value. You can add a check to convert it to negative if the first date is before the second:
my $days = $dt1->delta_days($dt2)->in_units('days');
$days *= -1 if $dt1 < $dt2;
say $days; # Prints -184
and a variation of your delta_days() function:
sub delta_days {
my $date1 = shift;
my $date2 = shift;
my $d1 = str_to_date_object($date1);
my $d2 = str_to_date_object($date2);
return $d1->delta_days($d2)->in_units('days') * ($d1 < $d2 ? -1 : 1);
}
say delta_days('2021-10-21', '1980-8-20');
say delta_days('1980-8-20', '2021-10-21');
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.
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
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";
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