Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:
Would anybody know of a module to help identify last month, last two months, etc taking into consideration leap dates? Essentially, I need to be able to say based on this month, give me:
This month => 03 or March
last month => 02 or February
Month before last => 01 or January
Month before => 12 or December (of last year)
This code is not working:
my $tm = localtime;
my $this_month = sprintf("%04d%02d?", $tm->year+1900, ($tm->mon)+1);
my $last_month = sprintf("%04d%02d?", $tm->year+1900, ($tm->mon));
my $last_month_1 = sprintf("%04d%02d?", $tm->year+1900, ($tm->mon)-1);
my $last_month_2 = sprintf("%04d%02d?", $tm->year+1900, ($tm->mon)-2);
Any ideas? Thanks.
Re: Month Dates
by Herkum (Parson) on Mar 01, 2007 at 21:25 UTC
|
DateTime would be a better module to use than localtime,
use DateTime;
my $dt = DateTime->now();
print "Current Month " . $dt->month_name . "\n";
$dt->subtract( months => 1 );
print "Last Month " . $dt->month_name . "\n";
$dt->subtract( months => 1 );
print "2 Months ago " . $dt->month_name . "\n";
$dt->subtract( months => 1 );
print "3 Months ago " . $dt->month_name . "\n";
| [reply] [d/l] |
Re: Month Dates
by ww (Archbishop) on Mar 01, 2007 at 20:59 UTC
|
The usual recommendations include Date::Calc and company.
Depending on what your system is, ppm (for ActiveState) or CPAN for others will let you get and use the module.
You'll also want to note that localtime does not count months as "1"-based, as you appear to be doing (pardon asked if I'm wrong):
From perdoc -f localtime
...$mon is the month itself, in
the range 0..11 with 0 indicating January and 11 indicating
December. ....
Just as an aside, you'll find that search and/or SuperSearch right here at the Monastery will be very helpful. | [reply] |
Re: Month Dates
by fenLisesi (Priest) on Mar 01, 2007 at 21:20 UTC
|
| [reply] |
Re: Month Dates
by Sidhekin (Priest) on Mar 01, 2007 at 21:33 UTC
|
I'm probably missing something, as this looks just too easy ... but have you considered modulus?
Your code with minimal changes:
my $tm = localtime;
my $this_month = sprintf("%04d%02d?",
$tm->year+1900,
$tm->mon()+1,
);
my $last_month = sprintf("%04d%02d?",
$tm->year+1900-($tm->mon()<1 ? 1 : 0),
($tm->mon()-1)%12+1,
);
my $last_month_1 = sprintf("%04d%02d?",
$tm->year+1900-($tm->mon()<2 ? 1 : 0),
($tm->mon()-2)%12+1,
);
my $last_month_2 = sprintf("%04d%02d?",
$tm->year+1900-($tm->mon()<3 ? 1 : 0),
($tm->mon()-3)%12+1,
);
Edit: Bah, yeah, that was too easy. Forgot the year. Fixed now.
print "Just another Perl ${\(trickster and hacker)},"
The Sidhekin proves Sidhe did it!
| [reply] [d/l] [select] |
Re: Month Dates
by ikegami (Patriarch) on Mar 01, 2007 at 22:07 UTC
|
use Time::Local qw( timelocal_nocheck );
use POSIX qw( strftime );
my ($sec,$min,$hour,$day,$mon,$year) = localtime;
for (0..3) {
my $time = timelocal_nocheck($sec,$min,$hour,$day,$mon-$_,$year);
print(strftime("%Y-%m", localtime($time)), "\n");
}
Update: Nevermind. Buggy because of the following change to Time::Local:
Removed the code from the docs that implied that the *_nocheck
variants were created for doing date math. They're just for speeding
things up when you have known valid data. If you pass them invalid
data, you'll probably get an incorrect answer. See #31421 on
rt.perl.org for discussion.
I do get invalid data. | [reply] [d/l] |
Re: Month Dates
by jettero (Monsignor) on Mar 01, 2007 at 21:41 UTC
|
my $this_month = &UnixDate(&ParseDate("today"), "%m");
my $two_ago = &UnixDate(&ParseDate("2 months ago"), "%B");
| [reply] [d/l] |
Re: Month Dates
by hangon (Deacon) on Mar 01, 2007 at 22:17 UTC
|
This is fairly straightforward as long as you're not concerned with days of the month. Just roll your own code.
# set numeric values
my $month =
my $year =
my $monthsback =
# subtract from current month
$month -= $monthsback;
# convert to proper range of values
if($month <= 0){$year--}
$year += int($month/12);
$month = $month%12;
if ($month == 0){$month = 12}
Well, maybe a little more straightforward if you were looking for future months. If you need to take days of the month into account, you would have to figure out how you want to deal with such thinge as Feb 31st.
| [reply] [d/l] |
|
use POSIX qw( floor strftime );
my $monthsback = 3; # 0=this month -1=last, -2, ...
my ($day,$mon,$year) = (localtime())[3,4,5];
$mon -= $monthsback;
$year += floor($mon / 12);
$mon = $mon % 12;
print(strftime("%Y-%m", 0,0,0, $day,$mon,$year), "\n");
A more general solution:
use POSIX qw( floor strftime );
my $mon_delta = -3; # ..., -2, -1=last, 0=this month +1=next, +2, ...
my ($day,$mon,$year) = (localtime())[3,4,5];
$mon += $mon_delta;
$year += floor($mon / 12);
$mon = $mon % 12;
print(strftime("%Y-%m", 0,0,0, $day,$mon,$year), "\n");
| [reply] [d/l] [select] |
|
You're rigorous as usual ikegami, your realname wouldn't be Spock? ;)
My point is that a simple algorithm could be used instead of importing a module, but I should have specified it's parameters more thoroughly:
# positive integer, current year
my $year =
# integer 1 to 12, current month
my $month =
# positive integer, number of months to go back
my $monthsback =
| [reply] [d/l] |
Re: Month Dates
by Thelonius (Priest) on Mar 02, 2007 at 13:26 UTC
|
use Time::localtime;
my $tm = localtime;
my $this_month = sprintf("%04d%02d?", $tm->year+1900, ($tm->mon)+1);
my $last_month = ($tm->mon == 0)
? sprintf("%04d12?", $tm->year+1899)
: sprintf("%04d%02d?", $tm->year+1900, $tm->mon);
| [reply] [d/l] |
Re: Month Dates
by siva kumar (Pilgrim) on Mar 02, 2007 at 06:47 UTC
|
You can use "inc_month" function in "DateTime::Precise" module.
use DateTime::Precise;
use strict;
my $t1 = DateTime::Precise->new;
my %months = (
1 => q{January},
2 => q{February},
3 => q{March},
4 => q{April},
5 => q{May},
6 => q{June},
7 => q{July},
8 => q{Auguest},
9 => q{Septemper},
10 => q{October},
11 => q{November},
12 => q{December},
);
my $thisMonth = $months{$t1->month};
my $prevMonth = $months{$t1->inc_month(-1)->month};
my $t1 = DateTime::Precise->new;
my $monthB4rLast = $months{$t1->inc_month(-2)->month};
my $t1 = DateTime::Precise->new;
my $monthB4rLast1 = $months{$t1->inc_month(-3)->month};
print "This Month is $thisMonth\n";
print "Previous Month is $prevMonth\n";
print "Month before last is $monthB4rLast\n";
print "Month before 2 months is $monthB4rLast1\n";
Output:
-------
ThisMonth is March
Next Month is February
Month before last is January
Month before 2 months is December
| [reply] [d/l] |
Re: Month Dates
by shandor (Monk) on Mar 01, 2007 at 21:29 UTC
|
instead of using a module, maybe you can use something like this:
my $month = (localtime())[4];
my $last_month = (($month)%12);
my %months = (
0 => "Dec", 1 => "Jan", 2 => "Feb", 3 => "Mar", 4 => "Apr",
5 => "May", 6 => "Jun", 7 => "Jul", 8 => "Aug", 9 => "Sep",
10 => "Oct", 11 => "Nov",
);
print "$months{$last_month}\n";
| [reply] [d/l] |
|
I'm puzzled as to why you use a hash with December associated with zero and a modulo operation. That will only work for the first decrement of a month. To go back another month you would need a different hash with November associated with zero, and so on and so forth. I think it might be better to have a single hash with zero for January to eleven for December (as returned by localtime) and a simple algorith for decrementing the month value, something like
my %months = (
0 => q{Jan},
1 => q{Feb},
2 => q{Mar},
3 => q{Apr},
4 => q{May},
5 => q{Jun},
6 => q{Jul},
7 => q{Aug},
8 => q{Sep},
9 => q{Oct},
10 => q{Nov},
11 => q{Dec},
);
my $month = (localtime)[4];
$month --;
$month = 11 if $month < 0;
This way you can keep decrementing the month until the cows come home.
Cheers, JohnGG | [reply] [d/l] [select] |
Re: Month Dates
by Moron (Curate) on Mar 02, 2007 at 14:16 UTC
|
my @lt = localtime();
print map ("month $_ is " . 1 + ($lt[4]-- % 12) . "\n",
( 0..-3 ));
| [reply] [d/l] |
|
|