tags:

views:

129

answers:

5

How do I tweak this to get yesterday's date using localtime?

use strict;

sub spGetCurrentDateTime;
print spGetCurrentDateTime;

sub spGetCurrentDateTime {
my ($sec, $min, $hour, $mday, $mon, $year) = localtime();
my @abbr = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
my $currentDateTime = sprintf "%s %02d %4d", $abbr[$mon], $mday, $year+1900; #Returns => 'Aug 17 2010' 
return $currentDateTime;
}

~

+2  A: 
localtime(time() - 24*60*60)
Arkadiy
@Arkadiy - Thanks!
jda6one9
There is an edge case around DST. I do not advise this method if you need this to work all of the time.
Chas. Owens
Downvote as per above. It's not a good answer.
daxim
The DST edge case can be "worked around", see sample code in another answer.
bigiain
+1  A: 

use Time::Piece.

use strict;
use warnings;
use 5.010;

# These are core modules in Perl 5.10 and newer
use Time::Piece;
use Time::Seconds;

my $yesterday = localtime() - ONE_DAY;
say $yesterday->strftime('%b %d %Y');

Note that this can go wrong in certain borderline cases, such as the start of daylight saving time. The following version does behave correct in such cases:

use strict;
use warnings;
use 5.010;

# These are core modules in Perl 5.10 and newer
use Time::Piece;
use Time::Seconds;

my $now = localtime();
my $yesterday = $now - ONE_HOUR*($now->hour + 12);
say $yesterday->strftime('%b %d %Y');

Alternatively, you can use the DateTime module as described in a different answer. That is not a core module, though.

mscha
Same DST problem hides under the fancy API.
daxim
Good point, daxim. I've added a version that doesn't have this problem.
mscha
`Time::Piece` and `Time::Seconds` didn't get added to the core until Perl 5.10 (well 5.9.5, but who uses dev releases?).
Chas. Owens
+3  A: 
use DateTime qw();
DateTime->now->subtract(days => 1); 

The expression on the second line returns a DateTime object.

daxim
+8  A: 

As tempting as it is to just subtract a day's worth of seconds from the current time, there are times when this will yield the wrong answer (leap seconds, DST, and possibly others). I find it easier to just let strftime (available in the Perl 5 core module POSIX) take care of all of that for me.

#!/usr/bin/perl

use strict;
use warnings;

use Time::Local;
use POSIX qw/strftime/;

#2010-03-15 02:00:00
my ($s, $min, $h, $d, $m, $y) = (0, 0, 0, 15, 2, 110);

my $time      = timelocal $s, $min, $h, $d, $m, $y;    
my $today     = strftime "%Y-%m-%d %T", localtime $time;
my $yesterday = strftime "%Y-%m-%d %T", $s, $min, $h, $d - 1, $m, $y;
my $oops      = strftime "%Y-%m-%d %T", localtime $time - 24*60*60;
print "$today -> $yesterday -> $oops\n";
Chas. Owens
Thanks for your example. Very helpful.
jda6one9
So if the 'day' value is less than 1 `strftime` counts backwards day + 1 days into the previous month (and possibly year). Is this behaviour a documented part of POSIX or is it platform-specific?
Grant McLean
@Grant Mclean I had thought the normalizing behavior was specified, but it looks like it isn't: "If any of the specified values are outside the normal range, the characters stored are unspecified."; however, every implementation I have come across normalizes the the `tm` `struct` before formatting it.
Chas. Owens
@Grant Mclean It may not be standard in POSIX (or SUS), but it is standard in Perl 5. The documentation says `arguments are made consistent as though by calling "mktime()" before calling your system's "strftime()"`.
Chas. Owens
+4  A: 

The DST problem can be worked around by taking 3600s from midday today instead of the current time:

#!/usr/bin/perl
use strict;
use warnings;
use Time::Local;

sub spGetYesterdaysDate;
print spGetYesterdaysDate;

sub spGetYesterdaysDate {
my ($sec, $min, $hour, $mday, $mon, $year) = localtime();
my $yesterday_midday=timelocal(0,0,12,$mday,$mon,$year) - 24*60*60;
($sec, $min, $hour, $mday, $mon, $year) = localtime($yesterday_midday);
my @abbr = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
my $YesterdaysDate = sprintf "%s %02d %4d", $abbr[$mon], $mday, $year+1900;
return $YesterdaysDate;
}

In light of the "unspecified" documented behaviour of the strftime solution suggested by Chas, this approach might be better if you're not able to test for expected-but-not-guaranteed results across multiple platforms.

bigiain
@bigiain - thank you. this works well with my existing routine.
jda6one9
@bigiain Perl 5 guarantees that the `tm` `struct` will be normalized before calling the system's `strftime`, so it should work on all systems (modulo bugs). My mistake was going to the SUS3 standard rather than the Perl 5 documentation.
Chas. Owens