2

I've seen other answers, but they're not working for me for some reason.

I'm trying to get yesterday's date in a Perl script using the following code.

For future reference, today's date is November 12, 2015

my (undef, undef, undef, $mday,$mon,$year) = localtime();
my $prev_day = timelocal(0, 0, 0, $mday-1, $mon, $year);
(undef, undef, undef, $mday,$mon,$year) = localtime($prev_day);
$year += 1900;

print "Yesterday's Day: $mday, Month: $mon, Year: $year\n";

Except my output looks like this

Yesterday's Day: 11, Month: 10, Year: 2015.

I should be reading yesterday's date as Day: 11, Month: 11, Year: 2015. Why is the month being subtracted?

Edit: This is different than the suggested answer because I'm wondering why local time seems to be writing the wrong month.

DeepDeadpool
  • 1,441
  • 12
  • 36
  • Search is your friend, don't be afraid of using it! Duplicates of [Code that generates yesterday's date in DDMMYY format?](http://stackoverflow.com/questions/7958231/looking-for-perl-5-12-code-that-generates-yesterdays-date-in-ddmmyy-format), [**How do I get yesterday's date using localtime?**](http://stackoverflow.com/a/3508717/342740) – Prix Nov 12 '15 at 16:44
  • 3
    The month goes from 0 to 11 in `timelocal()` – Mark Setchell Nov 12 '15 at 16:44
  • 1
    @Prix That doesn't answer the OP's question about why `localtime` returns 10 for November instead of 11, so I don't think it's a good dupe target. (There probably is another question about this, though.) – ThisSuitIsBlackNot Nov 12 '15 at 16:48
  • @ThisSuitIsBlackNot his title is "How to read yesterday's date in Perl" not "why it doesn't give me the right result" but thanks for the concern. – Prix Nov 12 '15 at 16:48
  • 1
    @Prix Read the body of the question, it's not a dupe of the questions you linked. People write bad titles all the time, but just because one person's question is titled "Perl problem" doesn't make it a duplicate of a question titled "problem with PERL." – ThisSuitIsBlackNot Nov 12 '15 at 16:53
  • 2
    @Prix [Perl Incorrect day generated by localtime](http://stackoverflow.com/q/25300858/176646) is an actual duplicate, although this question is better written, so I'm voting to close the other one as a dupe of this one. – ThisSuitIsBlackNot Nov 12 '15 at 17:00
  • 1
    For a better way to do this, see [How do I find yesterday's date?](http://perldoc.perl.org/perlfaq4.html#How-do-I-find-yesterday%27s-date?) in `perldoc perlfaq4`. – ThisSuitIsBlackNot Nov 12 '15 at 19:50
  • 1
    If you have a problem like this, it puzzles me why you wouldn't just go to the documentation for [`localtime()`](http://perldoc.perl.org/functions/localtime.html). That would have given you the answer right away. – Dave Cross Nov 13 '15 at 11:40

2 Answers2

7

The documentation for localtime says this

$mday is the day of the month and $mon the month in the range 0..11, with 0 indicating January and 11 indicating December.

So to get a month number with 1 for January you need $mon+1, or you can just add one to $mday at the same time as you add 1900 to $year

Like this

use strict;
use warnings 'all';

use Time::Local;

my ($y, $m, $d) = (localtime)[5,4,3];

my $yesterday = timelocal(0, 0, 0, $d-1, $m, $y);
($y, $m, $d) = (localtime($yesterday))[5,4,3];
$y += 1900;
++$m;

print "Yesterday's Day: $d, Month: $m, Year: $y\n";

output

Yesterday's Day: 11, Month: 11, Year: 2015

A better way is to use the core library Time::Piece like this

use strict;
use warnings 'all';

use Time::Piece;
use Time::Seconds 'ONE_DAY';

my $yesterday = localtime() - ONE_DAY;

printf "Yesterday's Day: %d, Month: %d, Year: %d\n",
    $yesterday->mday,
    $yesterday->mon,
    $yesterday->year;

output

Yesterday's Day: 11, Month: 11, Year: 2015
Borodin
  • 126,100
  • 9
  • 70
  • 144
  • 2
    Just subtracting one day from the date will not work on the first of a month. – dgw Nov 12 '15 at 18:33
  • That is the most fucking stupid programming decision I have ever heard of! "Let's just make this work wrong, so that timelocal and localtime don't work together! That'll show people how clever we are!" Dumb shits. – mired Jun 06 '23 at 02:31
4

localtime and friends work on a 6- (or 9-) element list that just expands the individual fields of a POSIX struct tm structure. In that structure, the tm_mon field is the 5th element, and represents the month in the range 0 to 11. I believe the intention here may be to allow you to use it as an index in an array of strings containing the names of the months in your locale.

The only sensible way to take a time element list and format it for human consumption involves going through the strftime function, which takes some format you wish to render. For example:

use POSIX qw( strftime );

print strftime( "Yesterday's Day: %d Month: %m, Year: %Y\n", localtime $epoch );

If you don't yet have a Unix epoch value, you can obtain one from mktime() or timelocal()

use POSIX qw( strftime mktime );

my ( $mday, $mon, $year ) = ... # as before

print strftime( "Yesterday's Day: %d Month: %m, Year: %Y\n",
   localtime mktime 0, 0, 0, $mday, $mon, $year );

It is important to send the values via this mktime-localtime roundtrip, because that is the way they become normalised - e.g. because of having subtracted 1 from the first day of the month; mktime()'s job here is to renormalise that to the final day of the previous month (and having to roll the year back if it was January, etc...)

ThisSuitIsBlackNot
  • 23,492
  • 9
  • 63
  • 110
LeoNerd
  • 8,344
  • 1
  • 29
  • 36