Day of the week
You are encouraged to solve this task according to the task description, using any language you may know.
A company decides that whenever Xmas falls on a Sunday they will give their workers all extra paid holidays so that, together with any public holidays, workers will not have to work the following week (between the 25th of December and the first of January).
In what years between 2008 and 2121 will the 25th of December be a Sunday?
Using any standard date handling libraries of your programming language; compare the dates calculated with the output of other languages to discover any anomalies in the handling of dates which may be due to, for example, overflow in types used to represent dates/times similar to y2k type problems.
Ada
<lang ada> with Ada.Calendar.Formatting; use Ada.Calendar.Formatting; with Ada.Text_IO; use Ada.Text_IO;
procedure Yuletide is begin
for Year in Ada.Calendar.Year_Number loop -- 1901..2399 if Day_Of_Week (Time_Of (Year, 12, 25)) = Sunday then Put_Line (Image (Time_Of (Year, 12, 25))); end if; end loop;
end Yuletide; </lang> Sample output:
1904-12-25 00:00:00 1910-12-25 00:00:00 1921-12-25 00:00:00 1927-12-25 00:00:00 1932-12-25 00:00:00 1938-12-25 00:00:00 1949-12-25 00:00:00 1955-12-25 00:00:00 1960-12-25 00:00:00 1966-12-25 00:00:00 1977-12-25 00:00:00 1983-12-25 00:00:00 1988-12-25 00:00:00 1994-12-25 00:00:00 2005-12-25 00:00:00 2011-12-25 00:00:00 2016-12-25 00:00:00 2022-12-25 00:00:00 2033-12-25 00:00:00 2039-12-25 00:00:00 2044-12-25 00:00:00 2050-12-25 00:00:00 2061-12-25 00:00:00 2067-12-25 00:00:00 2072-12-25 00:00:00 2078-12-25 00:00:00 2089-12-25 00:00:00 2095-12-25 00:00:00 2101-12-25 00:00:00 2107-12-25 00:00:00 2112-12-25 00:00:00 2118-12-25 00:00:00 2129-12-25 00:00:00 2135-12-25 00:00:00 2140-12-25 00:00:00 2146-12-25 00:00:00 2157-12-25 00:00:00 2163-12-25 00:00:00 2168-12-25 00:00:00 2174-12-25 00:00:00 2185-12-25 00:00:00 2191-12-25 00:00:00 2196-12-25 00:00:00 2203-12-25 00:00:00 2208-12-25 00:00:00 2214-12-25 00:00:00 2225-12-25 00:00:00 2231-12-25 00:00:00 2236-12-25 00:00:00 2242-12-25 00:00:00 2253-12-25 00:00:00 2259-12-25 00:00:00 2264-12-25 00:00:00 2270-12-25 00:00:00 2281-12-25 00:00:00 2287-12-25 00:00:00 2292-12-25 00:00:00 2298-12-25 00:00:00 2304-12-25 00:00:00 2310-12-25 00:00:00 2321-12-25 00:00:00 2327-12-25 00:00:00 2332-12-25 00:00:00 2338-12-25 00:00:00 2349-12-25 00:00:00 2355-12-25 00:00:00 2360-12-25 00:00:00 2366-12-25 00:00:00 2377-12-25 00:00:00 2383-12-25 00:00:00 2388-12-25 00:00:00 2394-12-25 00:00:00
ALGOL 68
main:( # example from: http://www.xs4all.nl/~jmvdveer/algol.html - GPL # INT sun=0 # , mon=1, tue=2, wed=3, thu=4, fri=5, sat=6 #; PROC day of week = (INT year, month, day) INT: ( # Day of the week by Zeller’s Congruence algorithm from 1887 # INT y := year, m := month, d := day, c; IF m <= 2 THEN m +:= 12; y -:= 1 FI; c := y OVER 100; y %*:= 100; (d - 1 + ((m + 1) * 26) OVER 10 + y + y OVER 4 + c OVER 4 - 2 * c) MOD 7 ); print("December 25th is a Sunday in:"); FOR year FROM 2008 TO 2121 DO INT wd = day of week(year, 12, 25); IF wd = sun THEN print(whole(year,-5)) FI OD; new line(stand out) )
Output:
December 25th is a Sunday in: 2011 2016 2022 2033 2039 2044 2050 2061 2067 2072 2078 2089 2095 2101 2107 2112 2118
AutoHotkey
<lang autohotkey>year = 2008 stop = 2121
While year <= stop {
FormatTime, day,% year 1225, dddd If day = Sunday out .= year "`n" year++
} MsgBox,% out</lang>
C
<lang c>#include <stdio.h>
- include <time.h>
- include <string.h>
int main() {
struct tm mytime; int i; time_t m; for(i=2008; i<=2121; i++) { memset(&mytime, 0, sizeof(struct tm)); mytime.tm_mday = 25; mytime.tm_mon = 11; mytime.tm_year = i-1900; m = mktime(&mytime); if ( m < 0 ) { printf("%d is the last year we can specify\n", i-1); break; } if ( mytime.tm_wday == 0 ) { printf("25 December %d is Sunday\n", i); } }
}</lang>
The output of a run on a 32 bit machine is
25 December 2011 is Sunday 25 December 2016 is Sunday 25 December 2022 is Sunday 25 December 2033 is Sunday 2037 is the last year we can specify
C#
<lang csharp>using System;
class Program {
static void Main(string[] args) { for (int i = 2008; i <= 2121; i++) { DateTime date = new DateTime(i, 12, 25);
if (date.DayOfWeek == DayOfWeek.Sunday) { Console.WriteLine(date.ToString("dd MMM yyyy")); } }
}
}</lang>
Using LINQ:
<lang csharp>using System; using System.Linq;
class Program {
static void Main(string[] args) { string[] days = (from day in (from year in Enumerable.Range(2008, 2121 - 2007) select new DateTime(year, 12, 25)) where day.DayOfWeek == DayOfWeek.Sunday select day.ToString("dd MMM yyyy")).ToArray();
foreach (string day in days) Console.WriteLine(day); }
}</lang>
This looks better:
<lang csharp>using System; using System.Linq;
class Program {
static void Main(string[] args) { string[] days = Enumerable.Range(2008, 2121 - 2007) .Select(year => new DateTime(year, 12, 25)) .Where(day => day.DayOfWeek == DayOfWeek.Sunday) .Select(day => day.ToString("dd MMM yyyy")).ToArray();
foreach (string day in days) Console.WriteLine(day); }
}</lang>
25 Dec 2011 25 Dec 2016 25 Dec 2022 25 Dec 2033 25 Dec 2039 25 Dec 2044 25 Dec 2050 25 Dec 2061 25 Dec 2067 25 Dec 2072 25 Dec 2078 25 Dec 2089 25 Dec 2095 25 Dec 2101 25 Dec 2107 25 Dec 2112 25 Dec 2118
Forth
Forth has only TIME&DATE, which does not give day of week. Many public Forth Julian date calculators had year-2100 problems, but this algorithm works well.
\ Zeller's Congruence : zeller ( m -- days since March 1 ) 9 + 12 mod 1- 26 10 */ 3 + ; : weekday ( d m y -- 0..6 ) \ Monday..Sunday over 3 < if 1- then dup 4 / over 100 / - over 400 / + + swap zeller + + 1+ 7 mod ; : yuletide ." December 25 is Sunday in " 2122 2008 do 25 12 i weekday 6 = if i . then loop cr ;
cr yuletide December 25 is Sunday in 2011 2016 2022 2033 2039 2044 2050 2061 2067 2072 2078 2089 2095 2101 2107 2112 2118
Fortran
Based on Forth example <lang fortran>PROGRAM YULETIDE
IMPLICIT NONE
INTEGER :: day, year
WRITE(*, "(A)", ADVANCE="NO") "25th of December is a Sunday in" DO year = 2008, 2121
day = Day_of_week(25, 12, year) IF (day == 1) WRITE(*, "(I5)", ADVANCE="NO") year
END DO
CONTAINS
FUNCTION Day_of_week(d, m, y)
INTEGER :: Day_of_week, j, k INTEGER, INTENT(IN) :: d, m, y j = y / 100 k = MOD(y, 100) Day_of_week = MOD(d + (m+1)*26/10 + k + k/4 + j/4 + 5*j, 7)
END FUNCTION Day_of_week
END PROGRAM YULETIDE</lang> Output
25th of December is a Sunday in 2011 2016 2022 2033 2039 2044 2050 2061 2067 2072 2078 2089 2095 2101 2107 2112 2118
Groovy
Solution: <lang groovy>def yuletide = { start, stop -> (start..stop).findAll { Date.parse("yyyy-MM-dd", "${it}-12-25").format("EEE") == "Sun" } }</lang>
Test program: <lang groovy>println yuletide(2008, 2121)</lang>
Output:
[2011, 2016, 2022, 2033, 2039, 2044, 2050, 2061, 2067, 2072, 2078, 2089, 2095, 2101, 2107, 2112, 2118]
Haskell
Using the time library: <lang haskell>import Data.Time import Data.Time.Calendar.WeekDate
isXmasSunday year = wday == 7
where (_,_,wday) = toWeekDate $ fromGregorian year 12 25
main = mapM_ putStrLn ["25 December " ++ show year ++ " is Sunday"
| year <- [2008..2121], isXmasSunday year]</lang>
Output:
25 December 2011 is Sunday 25 December 2016 is Sunday 25 December 2022 is Sunday 25 December 2033 is Sunday 25 December 2039 is Sunday 25 December 2044 is Sunday 25 December 2050 is Sunday 25 December 2061 is Sunday 25 December 2067 is Sunday 25 December 2072 is Sunday 25 December 2078 is Sunday 25 December 2089 is Sunday 25 December 2095 is Sunday 25 December 2101 is Sunday 25 December 2107 is Sunday 25 December 2112 is Sunday 25 December 2118 is Sunday
The built-in System.Time module overflows at the Unix epoch in 2038: <lang haskell>import System.Time
isXmasSunday year = ctWDay cal == Sunday
where cal = toUTCTime $ toClockTime cal' cal' = CalendarTime { ctYear = year, ctMonth = December, ctDay = 25, ctHour = 0, ctMin = 0, ctSec = 0, ctPicosec = 0, ctWDay = Friday, ctYDay = 0, ctTZName = "", ctTZ = 0, ctIsDST = False }
main = mapM_ putStrLn ["25 December " ++ show year ++ " is Sunday"
| year <- [2008..2121], isXmasSunday year]</lang>
Output on 32-bit machine:
25 December 2011 is Sunday 25 December 2016 is Sunday 25 December 2022 is Sunday 25 December 2033 is Sunday *** Exception: user error (Time.toClockTime: invalid input)
J
load 'dates' NB. provides verb 'weekday' SunDec25=: #~ 0&=@:weekday@:(|:@,: ,. 12 25$~#,2:) SunDec25 2008 + i.114 2011 2016 2022 2033 2039 2044 2050 2061 2067 2072 2078 2089 2095 2101 2107 2112 2118
Java
<lang java>import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar;
public class Yuletide{ public static void main(String[] args) { for(int i = 2008;i<=2121;i++){ Calendar cal = new GregorianCalendar(i, Calendar.DECEMBER, 25); if(cal.get(Calendar.DAY_OF_WEEK)==Calendar.SUNDAY){ System.out.println(cal.getTime()); } } } }</lang> Output:
Sun Dec 25 00:00:00 CST 2011 Sun Dec 25 00:00:00 CST 2016 Sun Dec 25 00:00:00 CST 2022 Sun Dec 25 00:00:00 CST 2033 Sun Dec 25 00:00:00 CST 2039 Sun Dec 25 00:00:00 CST 2044 Sun Dec 25 00:00:00 CST 2050 Sun Dec 25 00:00:00 CST 2061 Sun Dec 25 00:00:00 CST 2067 Sun Dec 25 00:00:00 CST 2072 Sun Dec 25 00:00:00 CST 2078 Sun Dec 25 00:00:00 CST 2089 Sun Dec 25 00:00:00 CST 2095 Sun Dec 25 00:00:00 CST 2101 Sun Dec 25 00:00:00 CST 2107 Sun Dec 25 00:00:00 CST 2112 Sun Dec 25 00:00:00 CST 2118
Mathematica
<lang Mathematica>
Reap[If[DateString[{#,12,25},"DayName"]=="Sunday",Sow[#]]&/@Range[2008,2121]]2,1
</lang> gives back: <lang Mathematica>
{2011,2016,2022,2033,2039,2044,2050,2061,2067,2072,2078,2089,2095,2101,2107,2112,2118}
</lang>
Modula-3
Modula-3 represents time using a (safe) wrapper around the C time interface. Consequently, it suffers from the same problem as C.
<lang modula3>MODULE Yule EXPORTS Main;
IMPORT IO, Fmt, Date, Time;
VAR date: Date.T;
time: Time.T;
BEGIN
FOR year := 2008 TO 2121 DO date.day := 25; date.month := Date.Month.Dec; date.year := year;
TRY time := Date.ToTime(date); EXCEPT | Date.Error => IO.Put(Fmt.Int(year) & " is the last year we can specify\n"); EXIT; END;
date := Date.FromTime(time);
IF date.weekDay = Date.WeekDay.Sun THEN IO.Put("25th of December " & Fmt.Int(year) & " is Sunday\n"); END; END;
END Yule.</lang>
Output:
25th of December 2011 is Sunday 25th of December 2016 is Sunday 25th of December 2022 is Sunday 25th of December 2033 is Sunday 2038 is the last year we can specify
Objective-C
It should works also with Cocoa and OpenStep in general, but I can't test these.
<lang objc>#import <Foundation/Foundation.h>
int main() {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSUInteger i; for(i=2008; i<2121; i++) { NSCalendarDate *d = [[NSCalendarDate alloc] initWithYear: i month: 12 day: 25 hour: 0 minute: 0 second:0 timeZone: [NSTimeZone timeZoneWithAbbreviation:@"CET"] ]; if ( [d dayOfWeek] == 0 ) { printf("25 Dec %u is Sunday\n", i); } [d release]; } [pool release]; return 0;
}</lang>
Output:
25 Dec 2011 is Sunday 25 Dec 2016 is Sunday 25 Dec 2022 is Sunday 25 Dec 2033 is Sunday 25 Dec 2039 is Sunday 25 Dec 2044 is Sunday 25 Dec 2050 is Sunday 25 Dec 2061 is Sunday 25 Dec 2067 is Sunday 25 Dec 2072 is Sunday 25 Dec 2078 is Sunday 25 Dec 2089 is Sunday 25 Dec 2095 is Sunday 25 Dec 2101 is Sunday 25 Dec 2107 is Sunday 25 Dec 2112 is Sunday 25 Dec 2118 is Sunday
OCaml
<lang ocaml>#load "unix.cma" open Unix
try
for i = 2008 to 2121 do (* I'm lazy so we'll just borrow the current time instead of having to set all the fields explicitly *) let mytime = { (localtime (time ())) with tm_year = i - 1900; tm_mon = 11; tm_mday = 25 } in try let _, mytime = mktime mytime in if mytime.tm_wday = 0 then Printf.printf "25 December %d is Sunday\n" i with e -> Printf.printf "%d is the last year we can specify\n" (i-1); raise e done
with _ -> ()</lang>
The output of a run on a 32 bit machine is
25 December 2011 is Sunday 25 December 2016 is Sunday 25 December 2022 is Sunday 25 December 2033 is Sunday 2037 is the last year we can specify
Perl
<lang perl>#! /usr/bin/perl -w
use Time::Local; use strict;
foreach my $i (2008 .. 2121) {
my $time = timelocal(0,0,0,25,11,$i); my ($s,$m,$h,$md,$mon,$y,$wd,$yd,$is) = localtime($time); if ( $wd == 0 ) { print "25 Dec $i is Sunday\n"; }
}
exit 0;</lang>
Output:
25 Dec 2011 is Sunday 25 Dec 2016 is Sunday 25 Dec 2022 is Sunday 25 Dec 2033 is Sunday Day too big - 25195 > 24855 Sec too small - 25195 < 78352 Sec too big - 25195 > 15247 Cannot handle date (0, 0, 0, 25, 11, 2038) at ./ydate.pl line 8
Using the DateTime module from CPAN: <lang perl>#! /usr/bin/perl -w
use DateTime; use strict;
foreach my $i (2008 .. 2121) {
my $dt = DateTime->new( year => $i, month => 12, day => 25 ); if ( $dt->day_of_week == 7 ) { print "25 Dec $i is Sunday\n"; }
}
exit 0;</lang> Output:
25 Dec 2011 is Sunday 25 Dec 2016 is Sunday 25 Dec 2022 is Sunday 25 Dec 2033 is Sunday 25 Dec 2039 is Sunday 25 Dec 2044 is Sunday 25 Dec 2050 is Sunday 25 Dec 2061 is Sunday 25 Dec 2067 is Sunday 25 Dec 2072 is Sunday 25 Dec 2078 is Sunday 25 Dec 2089 is Sunday 25 Dec 2095 is Sunday 25 Dec 2101 is Sunday 25 Dec 2107 is Sunday 25 Dec 2112 is Sunday 25 Dec 2118 is Sunday
PHP
<lang php><?php for($i=2008; $i<2121; $i++) {
$datetime = new DateTime("$i-12-25 00:00:00"); if ( $datetime->format("w") == 0 ) { echo "25 Dec $i is Sunday\n"; }
} ?> </lang>
Output:
25 Dec 2011 is Sunday 25 Dec 2016 is Sunday 25 Dec 2022 is Sunday 25 Dec 2033 is Sunday 25 Dec 2039 is Sunday 25 Dec 2044 is Sunday 25 Dec 2050 is Sunday 25 Dec 2061 is Sunday 25 Dec 2067 is Sunday 25 Dec 2072 is Sunday 25 Dec 2078 is Sunday 25 Dec 2089 is Sunday 25 Dec 2095 is Sunday 25 Dec 2101 is Sunday 25 Dec 2107 is Sunday 25 Dec 2112 is Sunday 25 Dec 2118 is Sunday
Python
<lang python>import datetime
def yuletide():
sunday = 6 days = (day.strftime('%d %b %Y') for day in (datetime.date(year, 12, 25) for year in range(2008,2122)) if day.weekday() == sunday) print '\n'.join(days)
yuletide()</lang>Output:
25 Dec 2011 25 Dec 2016 25 Dec 2022 25 Dec 2033 25 Dec 2039 25 Dec 2044 25 Dec 2050 25 Dec 2061 25 Dec 2067 25 Dec 2072 25 Dec 2078 25 Dec 2089 25 Dec 2095 25 Dec 2101 25 Dec 2107 25 Dec 2112 25 Dec 2118
Ruby
<lang ruby>require 'date'
SUNDAY = 0
for year in 2008..2121
day = Date.new(year, 12, 25) if day.wday == SUNDAY puts '25 Dec %d' % year end
end</lang> Output:
25 Dec 2011 25 Dec 2016 25 Dec 2022 25 Dec 2033 25 Dec 2039 25 Dec 2044 25 Dec 2050 25 Dec 2061 25 Dec 2067 25 Dec 2072 25 Dec 2078 25 Dec 2089 25 Dec 2095 25 Dec 2101 25 Dec 2107 25 Dec 2112 25 Dec 2118
The Time class overflows at the Unix epoch in 2038: <lang ruby>SUNDAY = 0
for year in 2008..2121
begin day = Time.local(year, 12, 25) if day.wday == SUNDAY puts '25 Dec %d' % year end rescue ArgumentError puts '%d is the last year we can specify' % (year-1) break end
end</lang> Output on 32-bit machine:
25 Dec 2011 25 Dec 2016 25 Dec 2022 25 Dec 2033 2037 is the last year we can specify
Smalltalk
<lang smalltalk>2008 to: 2121 do: [ :year | |date|
date := Date newDay: 25 monthIndex: 12 year: year. date dayName = #Sunday ifTrue: [ date displayNl ]
]</lang>
Output:
25-Dec-2011 25-Dec-2016 25-Dec-2022 25-Dec-2033 25-Dec-2039 25-Dec-2044 25-Dec-2050 25-Dec-2061 25-Dec-2067 25-Dec-2072 25-Dec-2078 25-Dec-2089 25-Dec-2095 25-Dec-2101 25-Dec-2107 25-Dec-2112 25-Dec-2118
Tcl
<lang tcl>package require Tcl 8.5
for {set y 2008} {$y <= 2121} {incr y} {
if {[clock format [clock scan "$y-12-25" -format {%Y-%m-%d}] -format %w] == 0} { puts "xmas $y is a sunday" }
}</lang> outputs
xmas 2011 is a sunday xmas 2016 is a sunday xmas 2022 is a sunday xmas 2033 is a sunday xmas 2039 is a sunday xmas 2044 is a sunday xmas 2050 is a sunday xmas 2061 is a sunday xmas 2067 is a sunday xmas 2072 is a sunday xmas 2078 is a sunday xmas 2089 is a sunday xmas 2095 is a sunday xmas 2101 is a sunday xmas 2107 is a sunday xmas 2112 is a sunday xmas 2118 is a sunday
UNIX Shell
#! /bin/bash for i in `seq 2008 2121` do date -d "$i-12-25" |grep Sun done exit 0
The first lines of output (from a 32bit GNU/Linux system, date version 6.9) are
Sun Dec 25 00:00:00 CET 2011 Sun Dec 25 00:00:00 CET 2016 Sun Dec 25 00:00:00 CET 2022 Sun Dec 25 00:00:00 CET 2033 date: invalid date `2038-12-25'
I.e., starting from year 2038, the date command (which uses the glibc library, at least on GNU systems), is not able to recognise the date as a valid one!
Different machine/OS version (64 bit)
This is the same command run on RedHat Linux.
bash-3.00$ date --version date (coreutils) 5.2.1 Written by David MacKenzie. Copyright (C) 2004 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. bash-3.00$ uname -a Linux brslln01 2.6.9-67.ELsmp #1 SMP Wed Nov 7 13:56:44 EST 2007 x86_64 x86_64 x86_64 GNU/Linux bash-3.00$ for((i=2009; i <= 2121; i++)); do date -d "$i-12-25" |egrep Sun; done Sun Dec 25 00:00:00 GMT 2011 Sun Dec 25 00:00:00 GMT 2016 Sun Dec 25 00:00:00 GMT 2022 Sun Dec 25 00:00:00 GMT 2033 Sun Dec 25 00:00:00 GMT 2039 Sun Dec 25 00:00:00 GMT 2044 Sun Dec 25 00:00:00 GMT 2050 Sun Dec 25 00:00:00 GMT 2061 Sun Dec 25 00:00:00 GMT 2067 Sun Dec 25 00:00:00 GMT 2072 Sun Dec 25 00:00:00 GMT 2078 Sun Dec 25 00:00:00 GMT 2089 Sun Dec 25 00:00:00 GMT 2095 Sun Dec 25 00:00:00 GMT 2101 Sun Dec 25 00:00:00 GMT 2107 Sun Dec 25 00:00:00 GMT 2112 Sun Dec 25 00:00:00 GMT 2118 bash-3.00$
UnixPipes
Thanks to UNIX Shell implementation
seq 2008 2121 | xargs -IYEAR -n 1 date +%c -d 'Dec 25 YEAR' | grep Sun
(Output same as UNIX Shell)
Vedit macro language
<lang vedit> Buf_Switch(Buf_Free) for (#3 = 2008; #3 < 2122; #3++) {
Reg_Set(10, "12/25/") Num_Str(#3, 10, LEFT+APPEND) if (JDate(@10) % 7 == 0) {
Num_Ins(#3, NOCR)
}
} </lang>
Output:
2011 2016 2022 2033 2039 2044 2050 2061 2067 2072 2078 2089 2095 2101 2107 2112 2118