I had a problem with date manipulation.This is my scenario.I have a daily generating report files,they will be generated from Monday to Friday.Now I need to compare those reports.My perl code should work in such a way that if today is Monday I need to compare todays report with previous week Friday's report.And my date format will be like this 20100803 if i give like this i need to be compared with 20100802. If i give Monday's report i.e 20100802 it should compare with 20100730 i.e 30th of July. Can anyone please help me.
Thanks in Advance.
You'll probably want to look at some kind of Date/DateTime object like Date::Calc. With that one, for example, you could split the string into a day, month, and year (using Regex or whatever you'd like to use) for something like:
#
#Set $date = "20100308" somehow, based on your file structure
#
$date =~ /^(\d{4})(\d{2})(\d{2})$/;
$year = $1;
$month = $2;
$day = $3;
$dow = Day_of_Week($day, $month, $year);
if ( $dow == 1 )
$offset = -3; #Date is a Monday
} else {
$offset = -1; #Date is Tuesday-Friday
}
#Find the date of the last report
$prev_report_date = Add_Delta_Days($day, $month, $year, $offset);
#
#Compare reports
#
The above is just a generalized example, of course. I don't know exactly how your files are structure or where the date field is coming from, but you can also go to Date::Calc's CPAN page for more help. There are a plethora of other packages that deal with dates, too. This is just one.
This won't handle holidays (and you should really think about those), but it's the minimal implementation in Perl:
use strict;
use warnings;
use POSIX ();
my $date_string = '20100802';
my ( $year, $month, $day ) = unpack 'A4 A2 A2', $date_string;
my $today = POSIX::mktime( 0, 0, 0, $day, $month - 1, $year - 1900 );
( $day, $month, $year, my $wday ) = ( localtime $today )[3..6];
my $day_back
= POSIX::mktime( 0, 0, 0, $day - ( $wday == 1 ? 3 : 1 ), $month, $year )
;
my $day_str = POSIX::strftime( '%Y%m%d', localtime( $day_back ));
Where $time is a specified time value.
With Time::Piece there meanwhile is a base distribution module, that offers date manipulation, comparison etc.
Time::Piece objects e.g. support - and + operators. From the POD:
The following are valid ($t1 and $t2 are Time::Piece objects):
$t1 - $t2; # returns Time::Seconds object
$t1 - 42; # returns Time::Piece object
$t1 + 533; # returns Time::Piece object
Depending on the level of performance that you require, there is a Perl package that is a swiss-army-knife of date manipulation and conversion.
It is called Date::Manip.
From that, you could easily ask for any part of the date (like day-of-week or week-of-year), or compare or subtract dates in a really wide variety of formats.
It is doable with other tools too, I'm sure. I've used Date::Manip for funky stuff like dealing with the date string "last friday" and getting a real value.
There are lots of date/time modules on CPAN, but DateTime is the canonical Perl module for dates and times.
use DateTime;
print DateTime->now()->subtract(3)->ymd('');
If you are ever in doubt about which CPAN module to use, a good first step is to see if Task::Kensho says anything about it.
Related
I am trying to convert a date from epoch to year month day and get the correct date.
my $day = 18322;
my ($y, $m, $d) = (gmtime 86400*$day)[5,4,3];
The epoch date is 1583020800 The conversion is as follows $y is 120 $m is 2 $d is 1
I guess I have to add $y = $y+1900 I get the correct year, I can add 1 to $m to get the correct month the day $d I don't have to add anything to. Is this correct. I am taking over code for someone but I have no idea what [5,4,3] does.
Epoch time 1583020800 is Sun Mar 1 00:00:00 2020.
You can use gmtime, but it's awkward. It returns an array of values and they need to be converted. The year is the number of years since 1900 and the month starts at 0. This is because it is a thin wrapper around struct tm from the C programming language Perl is written in.
my($y,$m,$d) = (gmtime(1583020800))[5,4,3];
$y += 1900;
$m += 1;
printf "%04d-%02d-%02d\n", $y, $m, $d;
Instead, use the built in Time::Piece.
use v5.10;
use Time::Piece;
my $time = Time::Piece->gmtime(1583020800);
say $time->ymd;
Or the more powerful DateTime.
use v5.10;
use DateTime;
my $dt = DateTime->from_epoch(epoch => 1583020800);
say $dt->ymd;
The (...)[5,4,3] is a literal list slice. The thing inside the parens creates a list, but this selects only elements 5, 4, and 3.
The gmtime docs point to localtime, which shows you the position of each thing in its list:
localtime
Converts a time as returned by the time function to a 9-element
list with the time analyzed for the local time zone. Typically
used as follows:
# 0 1 2 3 4 5 6 7 8
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime(time);
I would use Time::Piece as in Schwern's answer. But just to cover all bases, you can use the strftime() function from POSIX.pm as well.
use feature 'say';
use POSIX qw[strftime];
say strftime('%Y-%m-%d', gmtime(1583020800));
Output:
2020-03-01
You can pass different format strings to strftime().
I am trying to compare a file creation time which is in the format: 08-07-2016 08:16:26 GMT with the current time using time() in perl.
Since time() returns epoch time, I am not sure how to find the time difference between these two different time formats.
I tried something like below and for obvious reasons, I get an error saying: "Argument 08-07-2016 08:16:26 GMT" isn't numeric in subtraction".
my $current_time = time();
my $time_diff = $creation_time - $current_time;
if ($time_diff > 10) { #compare if the difference is greater than 10hours
# do something...
}
Some of the questions I have:
Since I want to compare only the hour difference, how can I extract just the hours from both these time formats?
I am unsure if the comparison of $time_diff > 10 is right. How to represent 10hours? 10*60?
OR is there a way to at least convert any given time format into epoch using DateTime or Time::Local?
How can I pass a a date parameter to a DateTime constructor?
my $dt1 = DateTime-> new (
year =>'1998',
month =>'4',
day =>'4',
hour =>'21',
time_zone =>'local'
);
Instead can we do something like
my $date = '08-07-2016 08:16:26 GMT';
my $dt1 = DateTime->new($date); # how can i pass a parameter to the constructor
print Dumper($dt1->epoch);
Thanks in advance for any help.
Time::Piece has been a standard part of Perl since 2007.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Time::Piece;
use Time::Seconds;
my $creation_string = '08-07-2016 08:16:26 GMT';
my $creation_time = Time::Piece->strptime($creation_string, '%d-%m-%Y %H:%M:%S %Z');
my $current_time = gmtime;
my $diff = $current_time - $creation_time;
say $diff; # Difference in seconds
say $diff->pretty;
I need some help trying to figure out how to format dates in perl. I have a working perl script, with a regular expression, that works fine if I use hard coded date strings like this:
my $mon = 'Aug';
my $day = '05';
my $year = '2010';
These vars are used in a regular expression like this:
if ($line =~ m/(.* $mon $day) \d{2}:\d{2}:\d{2} $year: ([^:]+):backup:/)
Now, I need to automate this date portion of the code and use current date systematically.
I looked into perl localtime and tried using unix date and throw them into variables.
I need to have the days of the week, single digit, be padded with '0'. As in today, 'Aug' '05'
'2010' because the input file I am using for the regex has dates like this.
My 2nd try with the unix and formatting is returning numbers, but I need to have them be strings:
my $mon2=`date '+%b'`;
my $day2=`date '+%d'`;
my $year2=`date '+%Y'`;
My test code for playing with date formats:
#!/usr/local/bin/perl -w
use strict;
my $mon = 'Aug';
my $day = '05';
my $year = '2010';
my $mon2=`date '+%b'`;
my $day2=`date '+%d'`;
my $year2=`date '+%Y'`;
print "$mon";
print "$day";
print "$year";
print "$mon2";
print "$day2";
print "$year2";
My Output:
Aug052010Aug
05
2010
I hate to break it to you, but you're reinventing the wheel. All this is implemented quite comprehensively in the DateTime distribution and the DateTime::Format:: family of classes:
use DateTime;
my $dt = DateTime->now;
print 'It is currently ', $dt->strftime('%b %d %H:%M:%S'), ".\n";
prints:
It is currently Aug 05 23:54:01.
I'm using Date::Manip for a variety of things, and want to create an array of days of the month.
I think I need:
#date = &ParseRecur("2010:4:0:0:0:0:0");
but, it doesn't do it.
I've read & reread the man page but can't get the syntax.
#date = &ParseRecur("2010:4:0:1:0:0:0");
#date = &ParseRecur("2010:4:0:1*:0:0:0");
don't work either!
You could build the list with your own loop, instead of using ParseRecur.
$month = 4;
for ($day = 1; $day <= 31; $day++) {
my $date = UnixDate( "$month/$day/2010", "%m-%d-%Y" );
push( #list, $date ) if (defined $date);
}
From the man pages:
"There are a small handful of English strings which can be parsed in place of a numerical recur description."
Check out the examples in the man page.
So, if you want an array of days of a month - say for June in 2010 you would do:
#dates = ParseRecur("every day in June 2010");
How do I find a date which is 3 days earlier than a given date in Perl where the format is YYYY-MM-DD?
Date::Calc is the champion module here:
use strict;
use warnings;
use Date::Calc qw(Add_Delta_YMD);
my $startDate = '2000-01-01';
my ($startYear, $startMonth, $startDay) = $startDate =~ m/(\d{4}-(\d{2})-\d{2})/;
# 1 year, 2 months, 3 days, after startDate
my $endDate = join('-', Add_Delta_YMD($startYear, $startMonth, $startDay, 1, 2, 3));
The module has a huge number of time conversion routines, particularly those dealing with deltas. DateTime and Date::Manip are also worth checking out.
Date::Calc can be used for such calculations:
#!/usr/bin/perl
use strict;
use warnings;
use Date::Calc qw(Add_Delta_Days);
my ( $yyyy, $mm, $dd ) = ( 2009, 9, 2 );
my #date = Add_Delta_Days( $yyyy, $mm, $dd, -3 );
print join( '-', #date );
DateTime is the canonical way for dealing with dates in modern Perl:
use DateTime;
my ($year, $month, $day) = split '-', '2009-09-01';
my $date = DateTime->new( year => $year, month => $month, day => $day );
$date->subtract( days => 3 );
# $date is now three days earlier (2009-08-29T00:00:00)
There are so many options that it is moderately embarrassing. It depends in part on what other calculations you might need in the future, and whether times and time zones will ever be a factor, and similar things.
You could look at any of these
Date::Calc
Date::Manip
DateTime::* - see also datetime.perl.org (though that site did not seem to be responding on 2009-09-01T22:30-07:00)
to name but three (sets of) modules. I'd suggest Date::Calc or Date::Manip for simplicity - but if you're going to need to get fancy in future, the DateTime modules may be better.
See perldoc POSIX for the function mktime().
It will help you convert dates and times to a simple number, which is the Unix time (the number of seconds since January 1, 1970, I believe). Then, just subtract 3 (days) times 24 (hours in a day) times 60 (minutes in an hour) times 60 (seconds in a minute), or 259200 seconds from that number, and use localtime() to convert that number of seconds back to a string representation.
This is probably the best solution*, because it will handle month and year changes automatically. Any other solution will probably end up being more complicated after factoring in checking to see if we ran out of days in a month, or ran out of months in a year.
EDIT: *Outside of looking on CPAN.
The neat thing about mktime is that it will handle any time of offset. It uses January=0; and Year 2009 = 109 in this scheme. Thus, printed month - 1 and full year - 1900.
use POSIX qw<mktime>;
my ( $year, $month, $day ) = split '-', $date;
my $three_day_prior = mktime( 0, 0, 0, $day - 3, $month - 1, $year - 1900 );
mktime is useful for finding the last day of the month as well. You just go to day 0 of the next month.
mktime( 0, 0, 0, 0, $month, $year - 1900 );
This is simple with Date::Simple
C:\>perl -MDate::Simple=today -e "print today()-3"
2009-08-30