Time interval between two dates with Perl - perl

I'm adding two dates and trying to calculate the time, but I'm getting the following error:
Error parsing time at /usr/local/lib/x86_64-linux-gnu/perl/5.30.0/Time/Piece.pm line 598.
I install Time::Piece with cpan: cpan Time::Piece.
This my code:
our #months = qw( 01 02 03 04 05 06 07 08 09 10 11 12 );
our #days = qw(Domingo Segunda Treça Quarta Quinta Sexta Sabado Domingo);
($sec,$min,$hour,$mday,$mon,$year,$wday,$day,$isdst) = localtime();
our $ano = "2021";
our $day = "$mday";
our $mes = $months[$mon];
our $data = $mes."-".$day."-".$ano;
our $horario = $hour.":".$min.":".$sec;
our $horario2 = $hour.":".$min.":".$sec;
our $data1 = $ano."-".$mes."-".$day;
our $data2 = $day."/".$mes."/".$ano;
our $str1 = 'Execution completed at '.$data2.' '.$horario.' AM';
our #mes = qw( Jan Feb Mar APr May Jun Jul Agu Sep Oct Nov Dec );
our #days = qw(Domingo Segunda Treça Quarta Quinta Sexta Sabado Domingo);
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
$nomeMes = $mes[$mon];
our #mes = qw( Jan Feb Mar APr May Jun Jul Agu Sep Oct Nov Dec );
our #days = qw(Domingo Segunda Treça Quarta Quinta Sexta Sabado Domingo);
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
our $data2 = $day."/".$mes."/".$ano;
our $horario = $hour.":".$min.":".$sec;
my $str2 = 'Execution completed at '.$data2.' '.$horario.' AM';
my #times = map Time::Piece->strptime(/(\d.+M)/, '%m/%d/%Y %H:%M:%S %p'), $str1, $str2;
my $delta = $times[1] - $times[0];
$tempo = $delta->pretty;
What am I doing wrong? What can I do to make this function work?

The matched pattern of $str1 is 20/12/2021 13:58:3 AM
Problems:
There's no 20th month
There's no 13 AM
Can give the wrong answer near a switch from Daylight-Saving Time.
Also, there's a couple of problems strptime ignores:
You should be using %I instead of %H for 12-hour time.
There's a lack of leading zeros where they are normally expected (minutes and seconds).
You appear to be asking the following:
Given the year, month, day, hour, minute and second components of a local time, how do I obtain the corresponding epoch time so I can perform a difference?
To achieve this, use Time::Local's timelocal*.
use Time::Local qw( timelocal_posix );
my $time = timelocal_posix( $sec, $min, $hour, $day, $month - 1, $year - 1900 );
You could also use DateTime. This more powerful module can give you differences in amounts other than seconds.
Either way, you will still have problems near a switch from DST. There's simply not enough information to address that. That's the problem with dealing with local times with no offset.

I use the script:
our $str2 = $ano.'/'.$mes.'/'.$day.' '.$hour.':'.$min.':'.$sec.'.267-05:00';
my #times = map Time::Piece->strptime( s/\..*//r, '%Y/%m/%d %H:%M:%S'), $str1, $str2;
our $delta = $times[1] - $times[0];
print $delta->pretty;
It's work.
Thanks very much Ikegami for help.

Related

How to calculate time difference in perl using POSIX module?

I am new to perl scripting. I have a need where in I require to find the difference of two dates in the Days/Hrs/minutes/seconds, provided I just have POSIX module ( I can't use Time::Piece or DateTime or... module)
Like,
$date1 = Tue Nov 30 10:53:38 2021;
$date2 = Fri Dec 10 02:12:25 2021;
$output = $date2 - $date1 :: here $output should be 09 days, 3hrs, 19 mins, 47 secs.
Can you please tell me how do we achieve this: Parsing, calculation?
Your help will be highly appreciated.
Thanks!
Algorithm to a solution of the problem can be achieved with POSIX::mktime function.
Input dates require some 'massaging' to bring provided dates to expected representation by mktime function what is achieved by date2epoch function which returns representation of the date in seconds.
Once dates was converted to seconds it is a matter of trivial computation to obtain time difference represented in days/hours/minutes/seconds.
Note #1: It is assumed that dates obtained in same timezone.
Note #2: OP's date difference computation is incorrect
use strict;
use warnings;
use feature 'say';
use POSIX;
my $date1 = 'Tue Nov 30 10:53:38 2021';
my $date2 = 'Fri Dec 10 02:12:25 2021';
my $diff = date_diff($date1,$date2);
printf "Date difference: %02d days %02d hours %02d min %02d sec\n",
$diff->#{qw/days hours min sec/};
sub date_diff {
my $date1 = shift;
my $date2 = shift;
my $diff;
my $sec_diff = date2epoch($date2) - date2epoch($date1);
$diff->{sec} = $sec_diff % 60;
$diff->{min} = ($sec_diff % 3600 - $diff->{sec} ) / 60;
$diff->{hours} = $sec_diff / 3600 % 24;
$diff->{days} = int( $sec_diff / ( 24 * 3600 ) );
return $diff;
}
sub date2epoch {
my $str_date = shift;
my($months,$date,$epoch);
$months->#{qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/} = (0..11);
$date->#{qw/week_day month month_day time year/} = split(' ', $str_date);
$date->#{qw/hour min sec/} = split(':', $date->{time});
$date->{month} = $months->{ $date->{month} };
$date->{year} -= 1900;
$epoch = POSIX::mktime( $date->#{qw/sec min hour month_day month year/} );
return $epoch;
}
Output
Date difference: 09 days 15 hours 18 min 47 sec

perl to open file with yesterday's date in localtime()

i need to open files with todays date and yesterdays date i can open todays file ok but am don't know how to open yesterdays, i am using localtime because my perl verson is 5.8.8.
so the other time/date modules are not available
this is what i have so far
#!/usr/local/bin/perl
#months = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
#days = qw(Sun Mon Tue Wed Thu Fri Sat Sun);
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
$year = $year+1900;
print "c:\\DBR_$year\\$months[$mon]\\Failures_output\\$mday$months[$mon]report.csv"
|| die "can't open output file for reading: $!";
this prints
c:\DBR_2014\May\Failures_output\5Mayreport.csv
now how do i open same files with yesterdays date
A localtime/mktime roundtrip will give you the epoch number for midnight at the start of yesterday (i.e. the time where mday is 1 less than now)
use POSIX 'mktime';
my #now = localtime();
my $yesterday = mktime 0, 0, 0, $now[3]-1, $now[4], $now[5];
You can then put this into strftime to give you the time string; you don't need to have an array of months like that
use POSIX 'strftime';
my $path = strftime("c:\\DBR_%Y\\%b\\Failures_output\\%d%breport.csv", localtime($yesterday));
(You don't have to worry about the case where mday is already 1 because mktime will handle that properly).
localtime can take an epoch seconds argument, so do get yesterday's date, just call
#yesterday = localtime( time - 86400 ); # 86400 = 24 * 60 * 60

How can I convert dates to required format in Perl?

I have current date as 1/10/2010 I need to convert it into 1 October 2010. Is there any module to convert?
Use DateTime::Format::Strptime.
use DateTime::Format::Strptime;
my $Strp = DateTime::Format::Strptime->new(
pattern => '%d/%m/%Y',
time_zone => 'UTC',
);
my $dt = $Strp->parse_datetime('1/10/2010');
print $dt->strftime('%d %b %Y');
Edit: Thanks to #davorg for a hint with new.
You can try:
my $date = '1/10/2010';
my #abbr = qw( dummy Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
my($d,$m,$y) = split/\//g,$date;
my $new_date = $d.' '.$abbr[$m].' '.$y;

How can I convert of the unix date output across multiple time zones to UTC, in Perl?

In Perl, how would one efficiently parse the output of unix's date command, taking into account time zone, and also convert to UTC?
I've read many similar questions on stackoverflow, but few seem to take into account parsing multiple time zones. Instead they seem to set the timezone manually and assume it to stay fixed.
# Example Input Strings:
my #inputs = (
'Tue Oct 12 06:31:48 EDT 2010',
'Tue Oct 12 07:49:54 BST 2010',
);
I tried the following to no avail:
foreach my $input ( #inputs ) {
my $t = Time::Piece->strptime( $input,
'%a %b %d %T %Z %Y' );
print $t->cdate, "\n";
}
It seems the problem is the time zone (%Z). Additionally, a time zone field does not seem to exist in Time::Piece, which would require me to write custom code to convert to UTC, which just seems... wrong.
Context:
I'm attempting to parse legacy logs from a variety of sources that use the unix date command for timestamps. Ideally, I'd like to convert all timestamps to UTC.
Any help would be greatly appreciated.
If you know how to disambiguate the TZs, just pop them into a dispatch table:
use strict; use warnings;
use DateTime::Format::Strptime ();
my #inputs = (
'Tue Oct 12 06:31:48 EDT 2010',
'Tue Oct 12 07:49:54 BST 2010',
);
my %tz_dispatch = (
EDT => build_parser( 'EST5EDT' ),
BST => build_parser( '+0100' ),
# ... etc
default => build_parser( ),
);
for my $input (#inputs) {
my ($parser, $date) = parse_tz( $input, %tz_dispatch );
print $parser->parse_datetime( $date ), "\n";
}
sub build_parser {
my ($tz) = #_;
my %conf = (
pattern => '%a %b %d %T %Z %Y',
on_error => 'croak',
);
#conf{qw/time_zone pattern/} = ($tz, '%a %b %d %T %Y')
if $tz;
return DateTime::Format::Strptime->new( %conf );
}
sub parse_tz {
my ($date, %tz_dispatch) = #_;
my (#date) = split /\s/, $date;
my $parser = $tz_dispatch{splice #date, 4, 1};
return $parser
? ($parser, join ' ', #date)
: ($tz_dispatch{default}, $date);
}
The Perl DateTime FAQ on timezones has a good background on why EDT and EST cannot be used in most conversions. The issue is that other countries also have an Eastern time zone with the same 3 letter abbreviation. EST EDT is ambiguous without other clues.
You might look at other modules, or just assume that "EDT" is the same as "EST5EDT" if that is true.
If you are using Date::Time::Strptime, you can use %O for the Olson Time Zone name and do a manual fixup before parsing.
i.e. if you know that EDT in your input means America/New_York, do this:
$time_in =~ s{EDT}{America/New_York};
and instead of
%a %b %d %T %Z %Y
for your time zone spec use
%a %b %d %T %O %Y
I've always found Date::Manip::ParseDate to be good for these sorts of situations.
use strict;
use warnings qw<FATAL all>;
use Date::Manip qw<ParseDate UnixDate>;
my #inputs = (
q<Tue Oct 12 06:31:48 EDT 2010>,
q<Tue Oct 12 07:49:54 BST 2010>,
);
sub date2epoch($) {
my $user_string = shift();
my $timestamp = ParseDate($user_string);
my $seconds = UnixDate($timestamp, "%s");
return $seconds;
}
sub epoch2utc($) {
my $seconds = shift();
return gmtime($seconds) . q< UTC>;
}
for my $random_date (#inputs) {
my $epoch_seconds = date2epoch($random_date);
my $normal_date = epoch2utc($epoch_seconds);
print "$random_date == $normal_date\n";
}
When run, that produces this:
Tue Oct 12 06:31:48 EDT 2010 == Tue Oct 12 10:31:48 2010 UTC
Tue Oct 12 07:49:54 BST 2010 == Tue Oct 12 06:49:54 2010 UTC
which seem to be what you're looking for.
I'm a little late on this, but GNU date itself is good at parsing dates:
$ date -u -d 'Thu Oct 14 01:17:00 EDT 2010'
Thu Oct 14 05:17:00 UTC 2010
I don't know how it resolves the EDT ambiguity though.
I agree with Jander on date command. -d and -u are great and save a lot of code lines.

How do you read the system time and date in Perl?

I need to read the system clock (time and date) and display it in a human-readable format in Perl.
Currently, I'm using the following method (which I found here):
#!/usr/local/bin/perl
#months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
#weekDays = qw(Sun Mon Tue Wed Thu Fri Sat Sun);
($second, $minute, $hour, $dayOfMonth, $month, $yearOffset, $dayOfWeek, $dayOfYear, $daylightSavings) = localtime();
$year = 1900 + $yearOffset;
$theTime = "$hour:$minute:$second, $weekDays[$dayOfWeek] $months[$month] $dayOfMonth, $year";
print $theTime;
When you run the program, you should see a much more readable date and time like this:
9:14:42, Wed Dec 28, 2005
This seems like it's more for illustration than for actual production code. Is there a more canonical way?
Use localtime function:
In scalar context, localtime() returns
the ctime(3) value:
$now_string = localtime; # e.g., "Thu Oct 13 04:54:34 1994"
You can use localtime to get the time and the POSIX module's strftime to format it.
While it'd be nice to use Date::Format's and its strftime because it uses less overhead, the POSIX module is distributed with Perl, and is thus pretty much guaranteed to be on a given system.
use POSIX;
print POSIX::strftime( "%A, %B %d, %Y", localtime());
# Should print something like Wednesday, January 28, 2009
# ...if you're using an English locale, that is.
# Note that this and Date::Format's strftime are pretty much identical
As everyone else said "localtime" is how you tame date, in an easy and straight forward way.
But just to give you one more option. The DateTime module. This module has become a bit of a favorite of mine.
use DateTime;
my $dt = DateTime->now;
my $dow = $dt->day_name;
my $dom = $dt->mday;
my $month = $dt->month_abbr;
my $chr_era = $dt->year_with_christian_era;
print "Today is $dow, $month $dom $chr_era\n";
This would print "Today is Wednesday, Jan 28 2009AD". Just to show off a few of the many things it can do.
use DateTime;
print DateTime->now->ymd;
It prints out "2009-01-28"
Like someone else mentioned, you can use localtime, but I would parse it with Date::Format. It'll give you the timestamp formatted in pretty much any way you need it.
The simplest one-liner print statement to print localtime in clear, readable format is:
print scalar localtime (); #Output: Fri Nov 22 14:25:58 2019