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

I need to know how to make today's date (localtime()) put the day, month and year in a format like: 01.08.04.

I almost have it completed using:

my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); ++$mon; my $newyear = $year - 100; $newyear = "0$newyear"; my $todaysdate = join(".", $mday, $mon, $year); print "Today's date is: $todaysdate";
The only problem with this code is I don't get the leading 0 if the day or month is less than 10. For example, today is the 8th of January. This would post 1.08.04 instead of 01.01.04.

Along with that, here's the tough part. I am trying to make an auto-delete with this script for my guestbook so really old messages get deleted when the script is run. Can someone show me how I would compare a date string like 01.08.04 with a different $time?

Ie.: Let's say I wanted to test to see if the date is older than 20 days older than today's date (meaning 20 days ago)

I have an idea on how to go about this using substrings and compare to see if the day is older, but I can't figure out how you would compare a whole string in this format to check for year month AND day. I really only need to calculate how many days ago it was and if it is greater than $autodelete="70" or so, it'll delete.

My question is, how could you do that because if today is the first of January, my method would error the script out trying to subract 70 from 01.

Any ideas on how to get the localtime into this format and check to see if it's too old would be very much appreciated. I made a start, but I think I've bitten off more than I can chew at this point.

Thanks everyone

Replies are listed 'Best First'.
Re: Calculating between times
by Joost (Canon) on Jan 08, 2004 at 18:01 UTC
    I need to know how to make today's date (localtime()) put the day, month and year in a format like: 01.08.04.
    I use POSIX::strftime() for most of my date formatting.
    Can someone show me how I would compare a date string like 01.08.04 with a different $time?
    If you have your times in seconds since epoch (and you don't care about too much exactness:
    if ($time < time - $days * 24 * 60 * 60) { .... }
    If you have formatted dates, or higher standards, go for Date::Manip.

    HTH,
    Joost.

      What do you mean by epoch? Just an idea, is it possible to save $time in a huge mess like 1233455763432 where I could always subtract it from earlier times? If so, how could I do that? I could just setup another hash just for times like this and not worry about playing with the formatting.
Re: Calculating between times
by ysth (Canon) on Jan 08, 2004 at 18:02 UTC
    For the leading zero problem, use sprintf:
    $formatted_date = sprintf "%.2d.%2d.%2d", $mon+1, $mday, $year%100;
    Update: everything from this point on, except the very last sentence, is intended mainly for humor value. For practical advice, go straight to the end.

    For the other issue, just convert your day,month,year into a number of days since a fixed date, e.g.:

    =head2 greg2rd $rd = greg2rd( $year, $month, $day ); Convert gregorian year,month,day to days on or after Jan 1, 1 CE (Gregorian). Normalization is performed (e.g. month of 28 means April two years after given year) for month < 1 or > 12 or day < 1 or > last day of month. =cut sub greg2rd { use integer; my ( $y, $m, $d ) = @_; my $adj; # make month in range 3..14 (treat Jan & Feb as months 13..14 of p +rev year) if ( $m <= 2 ) { $y -= ( $adj = ( 14 - $m ) / 12 ); $m += 12 * $adj; } elsif ( $m > 14 ) { $y += ( $adj = ( $m - 3 ) / 12 ); $m -= 12 * $adj; } # make year positive (oh, for a use integer 'sane_div'!) if ( $y < 0 ) { $d -= 146097 * ( $adj = ( 399 - $y ) / 400 ); $y += 400 * $adj; } # add: day of month, days of previous 0-11 month period that began + w/March, # days of previous 0-399 year period that began w/March of a 400-m +ultiple # year, days of any 400-year periods before that, and -306 days to + adjust # from Mar 1, year 0-relative to Jan 1, year 1-relative (whew) $d += ( $m * 367 - 1094 ) / 12 + $y % 100 * 1461 / 4 + ( $y / 100 * 36524 + $y / 400 ) - 306; } # is date $formatted_then more than $autodelete days ago? ($mon,$mday,$year) = (localtime)[4,3,5]; $now = greg2rd($year+1900, $mon+1, $day); ($mon,$mday,$year) = split /\./, $formatted_then; $then = greg2rd($year+1900, $mon, $day); delete if ($now - $then > $autodelete);
    Or instead of going to all of that bother, learn one of the fine modules that handle dates for you: DateTime,Date::Calc,Date::Manip.
Re: Calculating between times
by crabbdean (Pilgrim) on Jan 08, 2004 at 19:00 UTC
    I've always found that when doing calcuations for things as in your example (eg. things older than x days) it can be easier to leave it in the time format of "seconds since the epoch". This way you can simply get your result by finding anything older than x days (times the number of seconds in those days eg "X" times 24 hours times 60 seconds times 60 seconds.

    For example work got me to write a script to purge our share drive everyday of any files older than 7 days. Instead of adjusting times bewtween times formats I read the creation and modified dates for each file and simply compared them to a current time stamp. If the files mod time and creation time where both greater then 7*24*60*60 seconds I delete them.

    Its easier to compute things to a base and then work from that base. Seconds is a good base.

    If you have time in a mm/dd/yy format push this through the Time::Local() module of Perl to convert it back seconds. This module is the inverse of the localtime() function.

    Also the Date::Calc module should blow your hair back as far as date calcuations go.

    As for the leading zero try this:

    #!perl $var = 5; $var = "0".$var if (length($var) < 2); print $var;


    Dean
      It turns out I can't use epoch. The way it's setup, I have three pull down menus (one for each: day, month, year) then I join them my $newdate = join(".", $day, $month, $year). And since that's as specific as the time is, I can't calculate epoch as a way to determine how long ago it was.

        You have users specifying what the date is when they create entries? Do they ever remember to do so? Why not simply use the current time, as provideed by localtime?

        --
        TTTATCGGTCGTTATATAGATGTTTGCA

Re: Calculating between times
by Art_XIV (Hermit) on Jan 08, 2004 at 18:26 UTC

    Date::EzDate may be of help:

    use warnings; use strict; use Date::EzDate; my $date1 = Date::EzDate->new('01/01/2003'); my $date2 = Date::EzDate->new('1 October 2003'); my $before = ''; $before = 'not ' if $date1 >= $date2; print $date1->{'%D'}, ' is ', $before, 'before ', $date2->{'%D'}, "\n" +;
    Hanlon's Razor - "Never attribute to malice that which can be adequately explained by stupidity"
Re: Calculating between times
by Paulster2 (Priest) on Jan 08, 2004 at 21:11 UTC

    Date::Manip allows you to do many things with dates, including checking equality between two dates not formatted the same. You can also do all kinds of conversions. The perldoc is quite extensive, yet clear as to useage. As stated above in a previous Re:, get to know this bad boy, I don't think you will ever go back.

    Paulster2

Re: Calculating between times
by Anonymous Monk on Jan 08, 2004 at 20:50 UTC
    You can format your output, as another poster noted, with printf or sprintf; The code below does that. To find the difference between dates, you can use module Time::Local and it's function, timelocal().
    #!/usr/bin/perl use strict; use warnings; use Time::Local; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); printf("Today's date is: %02d.%02d.%02d\n", $mon+1, $mday, $year % 100); my $date = "12.25.03"; my ($m,$d,$y) = split /\./, $date; my $xmas_time = timelocal(0,0,0,$d,$m-1,$y); my $today = time; my $days_diff = int( ($today - $xmas_time) / (24*60*60) ); print "Difference is $days_diff days\n";
    Chris
      Your code worked great! The only problem with that was you had month.day.year . I tried changing the script to allow the new format but I can't seem to get it to work now. The script below prints these results:
      05.05.06 09.03.04 31.02.04 15.01.04 16.01.04 15.01.04 01.01.04 01.01.04 14.09.04 01.01.04 01.01.04 01.01.04 04.05.06 Day: 05 Month: 05 Year: 06 Day: 09 Month: 03 Year: 04 Software error: Day '31' out of range 1..29 at line 43
      Can you help me set it up so it works in this new format? it always dies on the third entry because the day is 31 when the error says it can only be 1..29.

      Thank you!

      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); my $today = time; print "$today<br><br>"; foreach (keys %entries) { my ($date, $course, $location, $job_type, $contact) = split(/::/, $e +ntries{$_}); print "$date<br>"; } foreach (keys %entries) { my ($date, $course, $location, $job_type, $contact) = split(/::/, $e +ntries{$_}); my ($d,$m,$y) = split /\./, $date; my $saved_date = timelocal(0,0,0,$d,$m-1,$y); my $days_diff = int( ($today - $saved_date) / (24*60*60) ); print "Day: $d Month: $m Year: $y<br>"; }
        Last I checked, February, being the second month, doesn't in fact have 31 days. If you expected that to be March, then you have an off by one error and you should add one to the month column before attempting to format it.

        On the other, if you expect february to have 31 days, well, you have more problems then these perl ones!
Re: Calculating between times
by Anonymous Monk on Jan 08, 2004 at 18:57 UTC
    I will try to reask my questions in a better way..

    Question 1) How can I get localtime() to print the day, month and year in this format 01.08.04 (if the day or month is less than 10, have a leading zero).

    Question 2) After localtime is in it's nice little format, how do I check it against an already saved $time to determine if the date has already passed? I need to make it detect whether or not the saved date is more than a week old.

    Thanks again

      1. $formatted = sprintf '%.02d.%.02d.%.02d', $mon, $day, $yr
        assuming you've already adjusted for $mon counting from zero
      2. You could convert back to seconds using timelocal and see if there's more than 24*60*60*7 seconds' difference.

      The PerlMonk tr/// Advocate