Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Doomsday algorithm

by QuillMeantTen (Friar)
on Sep 06, 2015 at 14:06 UTC ( [id://1141196]=CUFP: print w/replies, xml ) Need Help??

Thanks to this piece on wired I learnt about conway's doomsday algorithm to get the day of the week of any date.
Trying to wrap my head around the algorithm I decided to implement it as a learning exercise.
Here is the code, enjoy :D

Update, now with use strict!

#!/usr/bin/perl use 5.14.2; use warnings; use strict; use autodie; if(!defined($ARGV[0])){ die "gimme a date DD/MM/YYYY!\n"; } my $date = $ARGV[0]; my($day,$month,$year); if(!($date =~ m#(?<day>\d\d)/(?<month>\d\d)/(?<year>\d\d\d\d)#)){ print "$date did not match format DD/MM/YYYY\n"; die; } else{ $day = $+{day}; $month = $+{month}; $year = $+{year}; } my $isleap; if( $year =~ m/\d\d00/){ my $century = $year / 100; $isleap = $century % 4 == 0; } elsif($year =~ m/\d\d(?<decade>\d\d)/){ $isleap = $+{decade} % 4 == 0; } if(!$isleap && $month == 02 && $day == 29){ die "this ain't a leap year so $date is wrong!\n"; } my $century_anchor_day = ((5 * (($year/100)%4))%7); my $decade; if($year =~ m/\d\d(?<decade>\d\d)/){ $decade = $+{decade}; } if($decade % 2 == 0){ $decade /= 2; } else{ $decade += 11; $decade /=2; } if($decade %2 == 0){ $decade %= 7; } else{ $decade += 11; $decade %= 7; } my $anchorday_drift = 7- $decade; my @anchorday_week; my $year_anchor_day = ($century_anchor_day+$anchorday_drift)%7; given ($year_anchor_day){ when(0){@anchorday_week = qw(tuesday wednesday thursday friday sat +urday sunday monday );} when(1){@anchorday_week = qw(wednesday thursday friday saturday su +nday monday tuesday);} when(2){@anchorday_week=qw(thursday friday saturday sunday monday +tuesday wednesday);} when(3){@anchorday_week=qw(friday saturday sunday monday tuesday w +ednesday thursday);} when(4){@anchorday_week=qw(saturday sunday monday tuesday wednesda +y thursday friday);} when(5){@anchorday_week=qw(sunday sunday monday tuesday wednesday +friday saturday);} when(6){@anchorday_week = qw(monday tuesday wednesday thursday fri +day saturday sunday);} } my $doomsdates = { '01'=>3, '02'=>28, '03'=>0, '04'=>4, '05'=>9, '06'=>6, '07'=>11, '08'=>8, '09'=>5, '10'=>10, '11'=>7, '12'=>12, }; if($isleap){ $doomsdates->{'01'} = 4; $doomsdates->{'02'} = 29; } my $diff_to_doomsdates = $day - $doomsdates->{"$month"}; if($diff_to_doomsdates < 0){ $diff_to_doomsdates *= -1; } my $nbdays = $diff_to_doomsdates % 7; print "$ARGV[0] was a $anchorday_week[$nbdays]\n";</readmore>

Replies are listed 'Best First'.
Re: Doomsday algorithm
by u65 (Chaplain) on Sep 06, 2015 at 14:22 UTC

    Just a brief glance--methinks adding use strict; would show some problems. Also I believe  $isleap should be initialized.

      I know, I corrected that in the second script, the quizz one.
Re: Doomsday algorithm
by soonix (Canon) on Sep 07, 2015 at 13:56 UTC
    given ($year_anchor_day){ when(0){@anchorday_week = qw(tuesday wednesday thursday friday sat +urday sunday monday );} when(1){@anchorday_week = qw(wednesday thursday friday saturday su +nday monday tuesday);} when(2){@anchorday_week=qw(thursday friday saturday sunday monday +tuesday wednesday);} when(3){@anchorday_week=qw(friday saturday sunday monday tuesday w +ednesday thursday);} when(4){@anchorday_week=qw(saturday sunday monday tuesday wednesda +y thursday friday);} when(5){@anchorday_week=qw(sunday sunday monday tuesday wednesday +friday saturday);} when(6){@anchorday_week = qw(monday tuesday wednesday thursday fri +day saturday sunday);} }
    This looks ugly to me, and it's not the programmer's task to repeat the same information multiple times.
    @anchorday_week = qw(tuesday wednesday thursday friday saturday sunday + monday); my @tmp = splice @anchorday_week, 0, $year_anchor_day; push @anchorday_week, @tmp;
    (see splice). Probably there are more elegant solutions, but at least it is shorter and won't warn you about given/when

    Update: even simpler: drop that complete @anchorday_week, and then change the calculation of your $nbdays to

    my $nbdays = ($diff_to_doomsdates + $year_anchor_day) % 7; return qw(tuesday wednesday thursday friday saturday sunday monday)[$n +bdays];
      To prove your point, when(5) duplicates "sunday".

      And you probably want something like:

      @anchor_day_week = qw(tuesday wednesday thursday friday saturday sunda +y monday) [$year_anchor_day+1..6,0..$year_anchor_day];

      ...though I dislike repeating $year_anchor_day. This just changes the repetition:

      push @anchor_day_week, splice @anchor_day_week, 0, $year_anchor_day;

      ...so a function to do this for you would be appropriate, even if it's only used once.

      -QM
      --
      Quantum Mechanics: The dreams stuff is made of

        Yes, that's what I meant with "more elegant solution". The next step would be to further "normalize" that $nbdays such that Sunday is the usual 0 or 7 (like in strftime %u or %w) by calculating
        my $nbdays = ($diff_to_doomsdates + $year_anchor_day + 2) % 7; # or -5

      Indeed indeed, its much more elegant to do it that way!

Re: Doomsday algorithm
by QuillMeantTen (Friar) on Sep 06, 2015 at 14:49 UTC

    And now the quizz script :
    Update, added use strict and now get a warning about given when being experimenta

      Hello QuillMeantTen,

      Thanks for drawing attention to the Doomsday algorithm, it looks interesting.

      Given that your code was written as a learning exercise, I will highlight one aspect that stands out as a problem: the use of subroutine prototypes.

      First, Perl prototypes don’t function as a programmer coming from C or Java might expect them to, and they’re mostly unneeded anyway. This is explained at length in Tom Christiansen’s article “Far More Than Everything You’ve Ever Wanted to Know about Prototypes in Perl,” which is reprinted as a PerlMonks tutorial here.

      But, second, none of the subroutine prototypes in your code is ever actually used — because the & sigil prepended to a subroutine call disables them! See perlsub#Prototypes. Unless you have a good reason to disable prototypes, you should call a subroutine like this:

      frobnicate();

      and not like this:

      &frobnicate;

      The latter form is seen mainly in legacy code dating from Perl’s earlier days.

      Update: Fixed typo.

      Hope that helps,

      Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

        Well sir I thank you very much for your comment and shall start reading at once!
        To be honest I once saw that way of writing subs and decided to use it because it reminded me of C and made subs somewhat easier to understand (like, from the first line I know what kind of parameters this sub wants).
        I honestly hadn't the foggiest regarding the depth of their role and will start plumbing it right away.

      My old eyes still don't see use strict;.

        damn I thought I added it.. I will update it as soon as I re run the code with it, but I recall checking with it enabled...

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://1141196]
Approved by Athanasius
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (9)
As of 2024-04-19 16:39 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found