Karger78 has asked for the wisdom of the Perl Monks concerning the following question:

Here is my situation, I have a date time function. So I am building the date in a specific format. I need to go one day behind today, so I just minus 1 off of $mday. I just realized that if it's the 1st of the month, and I go back a day, then it's 00 of the same month, not the last day of the previous month.

Anyone know of any functions that would compinsate for this logic?

##Set LocalTime. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime() +; $year = $year + 1900; $mday=$mday-1; my $curLogDate = $year."0".$mon."0".$mday;

Replies are listed 'Best First'.
Re: Date Time problem.
by markkawika (Monk) on Sep 04, 2009 at 17:47 UTC

    Consider using DateTime.

    use strict; use warnings; use DateTime; my $dt = DateTime->now; $dt->subtract( days => 1 ); my $curLogDate = $dt->year . '0' . $dt->mon . '0' . $dt->mday;
      While you copied the OP's behaviour of using "...012025" for Christmas, he probably wants "...1225".
      my $curLogDate = $dt->ymd('');
Re: Date Time problem.
by Marshall (Canon) on Sep 05, 2009 at 02:09 UTC
    I think there have been some great replies, but I think some further clarification would help. I can see that there are some mis-understandings.

    There are three time functions included within Perl itself:
    1. localtime()
    2. gmtime()
    3. time() - makes an epoch second value for "now", can be used by either gmtime() or localtime()

    The module Time::Local contains the inverse of those functions:
    1. timelocal()
    2. timegm()
    3. (there is no "inverse" of time() per sea)

    The operating system keeps track of "time" based upon a continually incrementing integer number of seconds since a specific start date/time. This is called the "epoch time" (returned time() value of zero). For Unix and Windows this is 00:00:00 Jan 1, 1970. I seem to remember that some versions of Apple's OS'es use a different value for this "epoch date/time". The point being is that this "epoch time" value is not transportable in a general sense between platforms. Converting an "epoch time" to a text string is a good way to ensure portability.

    The time() function produces the current value of this continuously incrementing number of "epoch seconds" since the start of the "epoch" and corresponds to "now". This number never decreases and is independent of daylight savings time. When we "set our clocks back one hour", this "seconds since epoch" number just keeps growing. Just because we set our clocks back one hour, that doesn't change the fact that more seconds are continuing to accrue since the "epoch time".

    The general way to do "time math" is to convert to epoch seconds, do math in seconds and then convert back to a string. The DateTime module does it that way too and it can apply some fancy "correction values for leap seconds, etc.

    Some simple example code...

    localtime($time) is just a user presentation of the $time, "seconds since epoch" integer value.

    I recommend and use UTC, Zulu, GMT time in log files (that basically is all the same thing).

    Update: the OP's idea of adding zero is a GREAT idea. The idea is that default alpha sort will produce correct answers. A simple substitution to add a zero if only a single digit appears to suffice.

      The general way to do "time math" is to convert to epoch seconds, do math in seconds and then convert back to a string.

      Except it doesn't work. Not all days have 24*60*60 seconds.

      the OP's idea of adding zero is a GREAT idea.

      And POSIX's strftime is the way to do it if you're already dealing with localtime or gmtime (It handles the y+1900 and the m+1 for you.)

      use POSIX qw( strftime ); print( strftime('%Y%m%d', localtime), "\n");
        Dealing with "leap seconds" is a complex thing.
        For leap seconds for awhile see:
        http://en.wikipedia.org/wiki/Leap_second

        For most logging and reporting activities, leap seconds are of no consequence. One "day" is 86,400 seconds (24*60*60) seconds. There may or may not be a "leap second" in a given year. The Op's question was for one day ago.

        No problem with strftime. It "works" albeit slower than other ways.

Re: Date Time problem.
by Illuminatus (Curate) on Sep 04, 2009 at 18:28 UTC
    You could also use the following:
    my $SecPerDay = 24*60*60; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(t +ime - $SecPerDay);
    This may not be accurate, of course, on days when leap-seconds are applied :)

      It's also inaccurate twice a year, on the day when Daylight Saving Time begins, and on the day that it ends.

      DateTime takes DST into account.

      I don't think that leap seconds will affect the result. Time zone changes from DST, on the other hand, can.
Re: Date Time problem.
by Sewi (Friar) on Sep 04, 2009 at 18:35 UTC
    You're completly right :-)
    use Time::Local; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(t +ime); # Yesterday is one second before the beginning of today: my $Yesterday = timelocal(0,0,0,$mday,$mon,$year) - 1; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($ +Yesterday); ++$mon; $year += 1900; print "Yesterday: $mday.$mon.$year\n";