Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

comment on

( #3333=superdoc: print w/replies, xml ) Need Help??

Some time ago, i uploaded Acme::September::Eternal to cpan. Then, on request by stevieb at Re^3: Help with my crap, i got a variant of that project.

To quote: "I'm Canadian, so for us folk, it would be preferred if it was eternaldecemberize() where there's a single month of warm weather (August), and the rest December"

I have uploaded Acme::December::Eternal to CPAN. But before that, i wrote a small test program to check out the math involved. The premise is that the year starts on 1st of August, and except of August everything else is the month of December. Let's take a look (you might need to click, i've put most of the article into a readmore tag)...

#!/usr/bin/env perl use strict; use warnings; use Date::Manip; use Lingua::EN::Numbers::Ordinate; my $date = Date::Manip::Date->new(); $date->parse('2019-07-31'); my $month = $date->printf('%m'); $month =~ s/^0+//g; my $dayofyear = $date->printf('%j'); $dayofyear =~ s/^0+//g; my $dayofmonth = $date->printf('%d'); $dayofmonth =~ s/^0+//g; my $year = $date->printf('%Y'); # December starts on 1st of September... my $septstart = Date::Manip::Date->new(); $septstart->parse($year . '-09-01'); my $septday = $septstart->printf('%j'); $septday =~ s/^0+//g; my $outstring = ''; if($month == 8) { # August $outstring = ordinate($dayofmonth) . ' August ' . $year; } else { # December if($month > 8) { my $daycount = $dayofyear - $septday + 1; $outstring = ordinate($daycount) . ' December ' . $year } else { # Uh-oh, this is the continuation of last years december... my $decend = Date::Manip::Date->new(); $decend->parse($year . '-12-31'); my $decday = $decend->printf('%j'); $decday =~ s/^0+//g; my $prevyeardays = $decday - $septday + 1; my $daycount = $dayofyear + $prevyeardays; $year--; # previous years december $outstring = ordinate($daycount) . ' December ' . $year } } print $outstring, "\n";

Ok, that was quite a lot, let's go through it step by step:

#!/usr/bin/env perl use strict; use warnings; use Date::Manip; use Lingua::EN::Numbers::Ordinate; my $date = Date::Manip::Date->new(); $date->parse('2019-07-31');

The scripts starts with the usual boilerplate stuff, then loads Date::Manip. We also load Lingua::EN::Numbers::Ordinate, because that helps us to turn the number "1" into "1st", "2" to "2nd" and whatever else you crazy english speaking people do to numbers ;-)

We also make a Date::Manip::Date object with the date we want to turn into an EternalDecembertm date (this is a simple test script, no interactivity required).

my $month = $date->printf('%m'); $month =~ s/^0+//g; my $dayofyear = $date->printf('%j'); $dayofyear =~ s/^0+//g; my $dayofmonth = $date->printf('%d'); $dayofmonth =~ s/^0+//g; my $year = $date->printf('%Y');

In this step, we extract some values out of the date object. We need to know the year, the month, the day of the month (in case of August) and the day of the year (in case of not-August). We also remove leading zeros from the strings. The CPAN module does some extra stuff, but for the date calculation test this is enough.

# December starts on 1st of September... my $septstart = Date::Manip::Date->new(); $septstart->parse($year . '-09-01'); my $septday = $septstart->printf('%j'); $septday =~ s/^0+//g;

In case of not-August, we need to know the day of the year for the first of September as well (e.g. the first day of our EternalDecember). For this, we use the year extracted from the original date.

my $outstring = ''; if($month == 8) { # August $outstring = ordinate($dayofmonth) . ' August ' . $year;

August is easy, we just basically reassemble the string based on the original date values.

} else { # December if($month > 8) { my $daycount = $dayofyear - $septday + 1; $outstring = ordinate($daycount) . ' December ' . $year

If the original date involves the months september, october, november or december, we just calculate the number of days since first of september, call it december and we are good.

} else { # Uh-oh, this is the continuation of last years december... my $decend = Date::Manip::Date->new(); $decend->parse($year . '-12-31'); my $decday = $decend->printf('%j'); $decday =~ s/^0+//g; my $prevyeardays = $decday - $septday + 1; my $daycount = $dayofyear + $prevyeardays; $year--; # previous years december $outstring = ordinate($daycount) . ' December ' . $year } }

This is the special case of the lot. When the original date is before August, in our calendar this is still last years december. So need to add all the days beginning from last years 1st of September to year end, plus all the days in the current year up to our original date (e.g. day of the year).

You may note here that i don't account for the previous year being a leap year when getting the "day of the year" values for september-december. While leap years shift the offset of those days by one, they shift BOTH offsets at the same time. Since we calculate the difference, this will still result in the correct value.

Note: This is still technically incorrect, because there have been instances where countries skipped days on the calendar. For example, Samoa skipped the 30th of December 2011 so they could get to the other side of the dateline for some business reason. I'll have to fix my CPAN module to account for that kind of stuff.

print $outstring, "\n";

We output the calculated date string and we are done.

Note 2: Calculating exact dates in the future is not possible, no matter how good your date library is. Not only for reasons like countries skipping (or adding) days, switching calendars, changing timezones on short notice or redefining daylight savings time (or banning/reintroducing it) willy-nilly after every change of government. No, even if you could fix all that (hint: go into politics), the nice folks at the International Earth Rotation and Reference System Service who keep our atomic clocks in sync with our solar system release a new Bulletin C twice a year to tell you if there is going to be a leap second at the end of the six month period. So, good luck estimating future timestamps ;-)

perl -e 'use Crypt::Digest::SHA256 qw[sha256_hex]; print substr(sha256_hex("the Answer To Life, The Universe And Everything"), 6, 2), "\n";'

In reply to Custom date calculations using Date::Manip by cavac

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others romping around the Monastery: (4)
    As of 2019-12-05 21:42 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?
      Strict and warnings: which comes first?



      Results (151 votes). Check out past polls.

      Notices?