I am trying to find the week that a date falls in, in a certain year. I have a bunch of files that need to be sorted into folders like "week1-2012" and "week34-2011". I tried searching but a lot of the results aren't really helping because I am currently using perl v5.6.1, super old and I can't download any modules. I also found this link ( How do I calculate the week number given a date?) of interest but was wondering how I would go about getting the day of year and week easily. Was thinking of getting the month, and adding the appropriate amount of days to find out the day in the year. Any help would be appreciated. An example of the year format I am looking for is
//year 2012
S M T W R F S
1 2 3 <-- week #1
4 5 6 7 8 9 10 <-- week #2 //came from the link
//dec year 2011
S M T W T F S
27 28 29 31 <-- week #52 or 53, not to sure the actual week
You can use core modules: POSIX and Time::Local
1.parse your date to (sec, min, hour, mday, month, year)
2.convert your date to seconds (epoch)
3.use function strftime to get week from current date
use strict;
use Time::Local;
use POSIX qw(strftime);
my $date = '08/15/2012';
my ($month, $day, $year) = split '/', $date;
my $epoch = timelocal( 0, 0, 0, $day, $month - 1, $year - 1900 );
my $week = strftime( "%U", localtime( $epoch ) );
printf "Date: %s № Week: %s\n", $date, $week;
OUTPUT
Date: 08/15/2012 № Week: 33
Perl 5.6.1 dates from April 2001. Someone is making your life much harder than it needs to be by not giving you modern tools to use. I suggest it's worth spending some time fixing that problem.
If you had Perl 5.10 or greater, then it would include the Time::Piece module. And your problem would become trivial.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Time::Piece;
my $date = '08/15/2012';
my $dt = Time::Piece->strptime($date, '%m/%d/%Y');
say $dt->strftime('week%W-%Y');
Running it gives:
$ ./week
week33-2012
This counts the week as Mon-Sun, not Sun-Sat.
Here's an alternate solution, using DateTime:
use strict;
use warnings;
use DateTime;
my $dt=DateTime->now(time_zone=>"local");
print $dt->week_number . "\n";
The output is, of course:
33
Edit: Of course you can download modules! If nothing else, you can copy and use the relevant code from DateTime.
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 wanna calculate the date a week ago from today with a specific format and put it in to a variable. For example, today is Nov 21st. 2014, and I wanna print out: Last week is 2014-11-14.
I know we can use Date::Calc module, but I don't know how.
Check Time::Piece and Time::Seconds core modules,
use Time::Piece;
use Time::Seconds;
my $t = localtime() - ONE_WEEK;
print $t->ymd;
output
2014-11-14
DateTime version
use DateTime;
my $now = DateTime->now(time_zone => 'local')->subtract(weeks => 1);
print $now->ymd, ' ',$now->hms;
Date::Calc version
Instead of one week you can subtract 7 days using Date::Calc module
use Date::Calc qw(Add_Delta_Days);
my #date = Add_Delta_Days( 2014, 11, 21, -7 );
print join('-', #date);
OUTPUT
2014-11-14
This is very simple using Date::Manip
use Date::Manip;
my $today = ParseDate("today");
my $weeksago = DateCalc($today,"-7d");
Why not just subtract X days from the "mday" field of localtime? This example shows subtracting 60 days from the end of august. I'm not sure who corrects the month but I think I'm getting the right answer...
$ date
Wed Aug 30 14:34:14 DFT 2017
$ perl -MPOSIX -e '#t=localtime time; $t[3] -= 60; print strftime( "%Y/%m/%d", #t), "\n";'
2017/07/01
I wanna calculate the date a week ago from today with a specific format and put it in to a variable. For example, today is Nov 21st. 2014, and I wanna print out: Last week is 2014-11-14.
I know we can use Date::Calc module, but I don't know how.
Check Time::Piece and Time::Seconds core modules,
use Time::Piece;
use Time::Seconds;
my $t = localtime() - ONE_WEEK;
print $t->ymd;
output
2014-11-14
DateTime version
use DateTime;
my $now = DateTime->now(time_zone => 'local')->subtract(weeks => 1);
print $now->ymd, ' ',$now->hms;
Date::Calc version
Instead of one week you can subtract 7 days using Date::Calc module
use Date::Calc qw(Add_Delta_Days);
my #date = Add_Delta_Days( 2014, 11, 21, -7 );
print join('-', #date);
OUTPUT
2014-11-14
This is very simple using Date::Manip
use Date::Manip;
my $today = ParseDate("today");
my $weeksago = DateCalc($today,"-7d");
Why not just subtract X days from the "mday" field of localtime? This example shows subtracting 60 days from the end of august. I'm not sure who corrects the month but I think I'm getting the right answer...
$ date
Wed Aug 30 14:34:14 DFT 2017
$ perl -MPOSIX -e '#t=localtime time; $t[3] -= 60; print strftime( "%Y/%m/%d", #t), "\n";'
2017/07/01
I was wondering if there is an in-built Perl function that adjusts the date if you take a month from it. E.g. if date is the 31st, it will adjust to be the end of the previous month if it doesn't have 31 days.
I would just change it to 30th easily if it weren't for the months with 31 days next to each other (Dec/Jan, Jul/Aug) and February.
I just want to store the date a certain amount of time away from the current date, e.g.
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$current_date = join("-", (1900+$year), ($mon+1), $mday);
$one_month_ago = join("-", (1900+$year), ($mon), $mday);
$one_year_ago = join("-", (1899+$year), ($mon+1), $mday);
I can deal with the February instance as it only applies to years, but if this was taken on the 31st December 2012 then taking away a month would mean 31st Nov 2012, which of course didn't exist. I thought I would ask if there was a function before complicating things for myself... thanks :)
DateTime is not a built-in module, but once you've installed it, it makes this math trivial:
#!/usr/bin/perl
use strict;
use warnings;
use feature qw( say );
use DateTime;
my $dt = DateTime->now;
say $dt->ymd;
$dt->truncate( to => month );
say $dt->ymd;
$dt->add( days => -1 );
say $dt->ymd;
foreach ( 1 .. 12 ) {
$dt->add( months => -1 );
say $dt->ymd;
}
When I run this today (Aug 29, 2012) I get the following output:
[~] $ perl dt.pl
2012-08-29
2012-08-01
2012-07-31
2012-06-30
2012-05-31
2012-04-30
2012-03-31
2012-02-29
2012-01-31
2011-12-31
2011-11-30
2011-10-31
2011-09-30
2011-08-31
2011-07-31
Others have suggested DateTime, but it's quite large, non-core, and can be slow.
A much simpler solution is to use the builtin localtime and POSIX::mktime functions:
use POSIX qw( mktime );
my #t = localtime $epoch;
$t[4] -= 2; # $t[4] is tm_mon
my $two_months_ago = mktime #t;
The mktime() function specifically handles denormalised values; it will cope with the fact that Janurary minus 2 months is November of the previous year, etc.. It will keep the same second/minute/hour of the day, and the same day of the month.
If you have a chance to install module DateTime. It gives your a lot of perks, when you have deal with dates.
use strict;
use DateTime;
my $epoch = ...;
my $dt = DateTime->from_epoch( epoch => $epoch );
$dt->subract(months => 1);
printf "%s", $dt->datetime();
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