How do I get yesterday's date using localtime?
Asked Answered
A

8

14

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;
}

~

Anu answered 17/8, 2010 at 20:4 Comment(0)
E
13

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.

Exemption answered 18/8, 2010 at 3:21 Comment(0)
T
24
use DateTime qw();
DateTime->now->subtract(days => 1); 

The expression on the second line returns a DateTime object.

Thereat answered 17/8, 2010 at 22:26 Comment(1)
It does work, I just tested. Maybe you simply forgot to set the time_zone attribute in the constructor, or neglected to call the set_time_zone method? Maybe you have an outdated release of the DateTime::TimeZone distro?Thereat
H
18

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";
Humphries answered 17/8, 2010 at 22:36 Comment(4)
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?Bonzer
@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()".Humphries
As a one-liner: perl -MPOSIX -le '@t=localtime;--$t[3];print strftime "%Y%m%d",@t'Epochmaking
This is the only correct solution that i'm aware of now, which uses only standard (core) modules.Epochmaking
E
13

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.

Exemption answered 18/8, 2010 at 3:21 Comment(0)
J
6

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.

Jubbah answered 17/8, 2010 at 21:10 Comment(3)
Same DST problem hides under the fancy API.Thereat
Good point, daxim. I've added a version that doesn't have this problem.Jubbah
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?).Humphries
M
5

Solution suggested by most users is wrong!

localtime(time() - 24*60*60)

The worst thing you can do is to assume that 1 day = 86400 seconds.

Example: Timezone is America/New_York, date is Mon Apr 3 00:30:00 2006

timelocal gives us 1144038600

localtime(1144038600 - 86400) = Sat Apr 1 23:30:00 EST 2006

oops!

The right and the only solution is to let system function normalize values

$prev_day = timelocal(0, 0, 0, $mday-1, $mon, $year);

Or let datetime frameworks (DateTime, Class::Date, etc) do the same.

That's it.

Munro answered 18/12, 2013 at 19:43 Comment(0)
D
1
localtime(time() - 24*60*60)
Delegation answered 17/8, 2010 at 20:10 Comment(2)
There is an edge case around DST. I do not advise this method if you need this to work all of the time.Humphries
The DST edge case can be "worked around", see sample code in another answer.Exemption
D
0

my $yesterday = time(); $yesterday = $yesterday - (24*60*60);

24 as 24 hours, 60 as 60 minutes in hour and 60 as 60 seconds in minute time() will return actual timestamp, and 246060 will remove seconds for exactly one day After this simply do: localtime($yesterday);

Doro answered 19/11, 2021 at 16:55 Comment(1)
A day is not always 86400 seconds, so your method could lead to errors in some cases. Please read Pronin's answer, they give a good example.Tarmac
S
-1
This is how I do it.

#!/usr/bin/perl

use POSIX qw(strftime);

$epoc = time();
$epoc = $epoc - 24 * 60 * 60;

$datestring = strftime "%F", localtime($epoc);

print "Yesterday's date is $datestring \n";
Sure answered 2/7, 2014 at 14:38 Comment(1)
This can fail around the beginning of DST for reasons explained in other answers.Abscess

© 2022 - 2024 — McMap. All rights reserved.