Day of the week

From Rosetta Code
Revision as of 16:52, 13 December 2008 by rosettacode>Paddy3118 (First link points to several problems including 2038.)
Task
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 that 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

<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; </ada> 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

C

<c>#include <stdio.h>

  1. include <time.h>
  2. 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);
   }
 }

}</c>

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

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 >r
  r@   4 / +
  r@ 100 / -
  r> 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

Java

<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++){ GregorianCalendar cal = new GregorianCalendar(i, Calendar.DECEMBER, 25); if((cal.get(Calendar.DAY_OF_WEEK))==Calendar.SUNDAY){ System.out.println(new Date(cal.getTimeInMillis())); } } } }</java> 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

Objective-C

Works with: GNUstep

It should works also with Cocoa and OpenStep in general, but I can't test these.

<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 %d is Sunday\n", i);
     }
     [d release];
  }
  
  [pool release];
  return 0;

}</objc>

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

Translation of: C

<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 _ -> ()</ocaml>

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

<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 eq 0 )
 {
   print "25 Dec $i is Sunday\n";
 }

}

exit 0;</perl>

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

I suppose there's a CPAN package that handles dates without relying on hardware/implementation dependent sizes (the Perl code basically seems to use the same functions used by C code)

PHP

<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";
 }

} ?> </php>

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

<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()</python>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

require 'date'

SUNDAY = 0

for year in 2008..2121

   day = Date.new(year, 12, 25)
   if day.wday == SUNDAY
       puts '12 Dec %d' % year
   end

end 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: SUNDAY = 0

for year in 2008..2121

   begin
       day = Time.local(year, 12, 25)
       if day.wday == SUNDAY
           puts '12 Dec %d' % year
       end
   rescue ArgumentError
       puts '%d is the last year we can specify' % (year-1)
       break
   end

end Output on 32-bit machine:

12 Dec 2011
12 Dec 2016
12 Dec 2022
12 Dec 2033
2037 is the last year we can specify

UNIX Shell

Works with: bash
#! /bin/bash

for((i=2009; i <= 2121; i++))
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$