I don't know if anyone has submitted this algorithm before but I'll risk it. I wrote a Perl script a couple of years ago, based on an algorithm proposed by Lewis Carroll in Nature magazine some 114 years ago, for computing the weekday of any given Gregorian date. No additional modules are required to run it, just the standard Perl dist.
I'm sorry to admit that I no longer work with Perl, Java is where the cash flows. But I still dream in Perl;-)
Godspeed with Perl 6!#!/local/bin/perl # # signature start #Name: weekday #Version: 1.0 #Project: Alice #Author: b_rostad@yahoo.com #Date: 09.08.1999 #Contents: This script emobodies a method proposed by British mathema +tician # Charles Lutwidge Dodgson (27.01.1832-14.01.1898) in a lett +er to # Nature magazine in 1887. Dodgson is best known under the a +lias # Lewis Carroll which he used when writing "Alice in wonderl +and". # # The method for calculating the day of week for a given dat +e is # split in four operations, each operation treats one part ( +or item # as Carroll labels it): century, the 2-digit year, the mont +h and # the day of the month. Each part is added to the previous p +art to # obtain a total. If a part or the total exceeds 7 replace i +t by the # modulus value MOD(part,7), i.e. keep the remainder after d +ividing # by 7. Carroll labels the result of the modulus operation a +s # "surplus". Some of the recipe is quoted directly from Carr +oll. # # 1) CENTURY_ITEM: If using Julian calendar (up to 1700 in D +enmark, # Norway and Germany; to 1752 in the british empire) "sub +ract # from 18". If using Gregorian calendar "divide by 4, tak +e # overplus from 3, multiply remainder by 2." # # 2) YEAR_ITEM: "Add together the number of dozens, the over +plus # and the number of 4's in the overplus". # # 3) MONTH_ITEM: "If it begins or ends with a vowel, subtrac +t the # number denoting its place in the year from 10. This, pl +us its" # number of days, gives the item for the following month. +" # # 4) DAY_ITEM: "is the day of the month". # # "The total, thus reached, must be corrected, by deducting +1 (first # adding 7, if the total be 0), if the date be January or Fe +bruary # in a Leap Year." From the final value, in the range 1-7, t +he day # of week is given by: # # 1->Monday, 2->Tuesday, 3->Wednesday, 4->Thursday, 5->Frida +y, # 6->Saturday, 7->Sunday # # This script will dump the result or a suitable error messa +ge to # STDOUT. # #Limitation: Will only work for Gregorian (new) calendar dates, but it + is # fairly simple to add support for Julian calendar, but the +n it # is necessary to add a control parameter to tell which one + to # use! # # signature end # Variable for accumulating the total value $total = 0; # The user specified date parsed from @ARGV and a parameter # $is_leap_year which is '1' for leap years and 0 otherwise. my($day, $month, $year, $is_leap_year) = parse_input(@ARGV); # Add century and year item # century = substr($year,0,2); # 2digit year = substr($year,2,2); &add_century_item(\$total,substr($year,0,2)); &add_year_item(\$total,substr($year,2,2)); # Add month and day items &add_month_item(\$total,$month); &add_day_item(\$total,$day); # dump a suitable result print "$day.$month.$year is a ", &get_weekday(\$total,$year,$month,$is_leap_year),"\n"; # # -- METHOD SUBROUTINES # # CENTURY_ITEM: Divide the century by 4, take overplus from 3, multipl +y # remainder by 2. sub add_century_item { my($tot, $century) = @_; $$tot = $$tot + 2*(3 - ($century%4)); } # YEAR_ITEM: Add together the number of dozens, the overplus and the n +umber # of 4's in the overplus in the two digits representing the year. sub add_year_item { my($tot, $year) = @_; my($remainder) = $year%12; $$tot = ($$tot + (int $year/12) + $remainder + (int $remainder/4)) + % 7; } # MONTH_ITEM: If it begins or ends with a vowel, subtract the number # denoting its place in the year from 10. This, plus its number of day +s, # gives the item for the following month. sub add_month_item { my($tot, $month_num) = @_; # Precalculated MONTH_ITEM values my(%month_item) = (1 => '0', 2 => '3', 3 => '3', 4 => '6', 5 => '1', 6 => '4', 7 => '6', 8 => '2', 9 => '5', 10 => '0', 11 => '3', 12 => '12' ); if (exists $month_item{$month_num}) { $$tot = ($$tot + $month_item{$month_num}) % 7; } else { warn "System-error: Illegal month number '$month_num'!"; } } # DAY_ITEM: Is the day of the month. sub add_day_item { my($tot, $day) = @_; $$tot = ($$tot + $day) % 7; } sub get_weekday { my($tot, $year, $month, $is_leap_year) = @_; if ($month < 3 && $is_leap_year == 1) { # do this only for January and February in leap years $$tot = $$tot -1; } # add 7 if total so far is below 1. $$tot = ($$tot < 1 ? $$tot+7 : $$tot); my(%weekday) = (1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday'); return $weekday{$$tot}; } # # --- HELPERS # sub parse_input { my(@input) = @_; if ($input[0] =~ m|^-h$|) { print_man_and_exit(); } elsif (scalar @input != 3) { print_usage_and_exit(); } else { my($day, $month, $year) = @ARGV; if (0 < $day && 0 < $month && $month < 13 && length $year == 4) { my($is_leap) = is_leap_year($year); if ($day <= days_in_month($month,$is_leap)) { return ($day, $month, $year, $is_leap); } else { print_error("Month $month in $year hasn't got $day days"); } } else { print_date_spec_error_and_exit(); } } } sub is_leap_year { my($year) = @_; my($is_leap_year) = 0; # it's a leap year if divisible by 4, but not if it's # divisible by 100 unless it's also divisible by 400. if ($year%4 == 0) { if ($year%100 == 0) { if ($year%400==0) { $is_leap_year = 1; } } else { $is_leap_year = 1; } } return $is_leap_year; } sub days_in_month { my($month, $is_leap_year) = @_; my(%month) = ('1' => '31', '2' => '28', '3' => '31', '4' => '30', '5' => '31', '6' => '30', '7' => '31', '8' => '31', '9' => '30', '10' => '31', '11' => '30', '12' => '31'); if ($is_leap_year) { $month{'2'}++; } return $month{$month}; } sub print_error { my($msg) = @_; print "\nError: $msg!\n"; print_usage_and_exit(); } sub print_date_spec_error_and_exit { print <<EOF; You must provide a valid date in the following format: <day> <month> <year> where <day> is the day of month, <month> is a number (1 for January through to 12 for December), <year> is a four-digit year (thus use 1999 instead of 99) EOF exit(); } sub print_usage_and_exit { print "\nUsage: \n"; print " perl weekday <day> <month-number> <four-digit-yea +r>\n"; print " perl weekday -h\n\n"; exit(0); } sub print_man_and_exit { print <<EOT; weekday NAME weekday - will compute the weekday for a given Gregorian date, where date is given as day, month number and a 4 digit year. SYNOPSIS perl weekday [ -h ] day month year DESCRIPTION The weekday utility will return the day of the week for a given date, specified on the commandline. It will only return days in the Gregorian calendar, thus asking for weekdays before AD 1700 makes no sense. The algorithm this utility is based on was proposed by British mathematician Charles Lutwidge Dodgson (27.01.1832-14.01.1898) in a letter to Nature magazine in 1887. Dodgson is best known under the alias Lewis Carroll which he used when writing "Alice in wonderland". OPTIONS The following options are supported: -h Displays this man page OPERANDS The following operands are supported (if -h option is not used these operands are required): day The day of the month (1-[28|29|30|31]) month The month number (January=1, ..., December=12) year The four digit year. CONTACT b_rostad\@yahoo.com Last change: 9 August 1999 EOT exit(0); }
2001-08-13 Edit by Corion - changed PRE tags to CODE tags.
|
|---|