12
\$\begingroup\$

Task

Write a program or a function that calculates a week-day name of a date which a user inputs.

Input & Output

Input is a string, YYYYMMDD.

Example of input values:

20110617 : June 17, 2011
19040229 : February 29, 1904
06661225 : December 25, 666
00000101 : January 1, 0
99991231 : December 31, 9999

You may assume that all inputs are valid. Note that year zero is valid.

Output is an integer between 0 and 6. Each integer represents a week-day name. You can decide freely which integer represents a week-day name, like this one

0 : Monday
1 : Tuesday
2 : Wednesday
...
6 : Sunday

(in order) or this one

0 : Monday
1 : Wednesday
2 : Sunday
...
6 : Saturday

(not in order).

Test Cases

Input     Week-day   Output ([0..6 -> Monday..Sunday] is used in this example.)

20110617  Friday     4
19500101  Sunday     6
22220202  Saturday   5
19000228  Wednesday  2
19000301  Thursday   3
19450815  Wednesday  2
19040229  Monday     0
19040301  Tuesday    1
17760704  Thursday   3
20000228  Monday     0
20000229  Tuesday    1
20000301  Wednesday  2
20121223  Sunday     6
00000401  Saturday   5
66660606  Wednesday  2
59161021  Saturday   5

Restriction

You must not use any kind of function/class/... which are related to timestamp or date, like Date class in Java/JavaScript/ActionScript, or getdate function in PHP.

You should use Gregorian calender, which is used by many people now.

Of course, shortest code wins. If two code have same length, then the code with highest votes wins.

(Due: When there's more than 5 codes which has more than (or equal) +1 votes.)

\$\endgroup\$
5
  • \$\begingroup\$ To-day? Why, Christmas Day! \$\endgroup\$
    – Joey Adams
    Commented Jun 17, 2011 at 5:57
  • 3
    \$\begingroup\$ Optimistic solution written in Bash (6 chars):echo 4. \$\endgroup\$ Commented Jun 17, 2011 at 6:41
  • 1
    \$\begingroup\$ @trutheality No, I didn't mean that.. What I wanted is a code that prints/returns the day of week of a date someone typed, not just print the day of week of today. \$\endgroup\$
    – JiminP
    Commented Jun 17, 2011 at 6:55
  • \$\begingroup\$ Oh I know. That's what this one does. \$\endgroup\$ Commented Jun 17, 2011 at 16:45
  • \$\begingroup\$ It's right at least 14% of the time! \$\endgroup\$ Commented May 17, 2017 at 15:37

7 Answers 7

4
\$\begingroup\$

PHP - 101 97 103 125 characters

  • Sakamoto Algorithm
  • 0 = Sunday

Code

<?php fscanf(STDIN,"%4d%2d%2d",$y,$m,$d);@$a=a032503514624;$y-=$m<3;$z=$y+1;echo($y+$y/4%$z-$y/100%$z+$y/400%$z+$a[$m]+$d)%7;

Note

Unfortunately, due to PHP's dynamic, weak typing, the Sakamoto algorithm doesn't function properly without explicitly flooring each division operation.

\$\endgroup\$
8
  • \$\begingroup\$ Can you please test again? For some years it gives me different results (e.g. testcase 17760704 yields tuesday instead of wednesday). \$\endgroup\$
    – Howard
    Commented Jun 17, 2011 at 15:05
  • \$\begingroup\$ @Howard that's very odd; for 17760704, I get Wednesday. I do get other inconsistencies, though, which I cannot account for, e.g. 19040229 returns Tuesday. Not sure what could be causing this. I get the same results when I expand the algorithm back out to y+y/4-y/100+y/400. \$\endgroup\$
    – rintaun
    Commented Jun 17, 2011 at 18:38
  • \$\begingroup\$ I can see it happening with 497*y/400: y=4 in that case returns 4, instead of the correct 5 from y+y/4+y/100+y/400 (where only the first two terms come into play). That is what plagues my JavaScript answer. Is it possible that doubles are being created instead of ints? (My PHP is too weak to know.) \$\endgroup\$
    – DocMax
    Commented Jun 17, 2011 at 20:57
  • \$\begingroup\$ @DocMax: Leaving the expression expanded has the same result (497y/400 should be equivalent: y/100 is subtracted and y/400 added again regardless). I'm guessing that PHP is just chopping off everything after the decimal instead of rounding it. I tested this by rounding before the modulo. This fixes two of the anomalies, but 19040229 still returns the same result. Any other ideas? \$\endgroup\$
    – rintaun
    Commented Jun 18, 2011 at 6:24
  • \$\begingroup\$ @rintaun i don't think it is the rounding. They are fundamentally different. Take the example from above (y=4): 497*4/400=1988/400=4 but on the other hand 4+4/4-4/100+4/400=4+1-0+0=5. The terms /100 and /400 get too much weight in your calculation such that the 2000 can not be reached. \$\endgroup\$
    – Howard
    Commented Jun 18, 2011 at 7:08
2
\$\begingroup\$

Ruby, 95 92 characters

Plain straightforward ruby implementation with 0:Monday, ...

p ((y=(d=gets.to_i)/(k=100)/k-((m=d/k%k)<3?1:0))+y/4-y/k+y/400+"squsptrotqro"[-m].ord+d%k)%7
\$\endgroup\$
0
2
\$\begingroup\$

C - 129

main(y,m,d,s)
{
    scanf("%04d%02d%02d",&y,&m,&d);
    y-=s=86400;
    d+=y+"-addgbegcfadf"[m];
    m>2?y++:0;
    putchar(48+(d+y/4-y/100+y/400+s+s)%7);
}

This abuses how division rounds toward zero, at least on my system (Linux x86).

The magic constant, 86400, serves two purposes:

  • Subtract from the year to make it negative, without affecting the day of the week. This makes it so the divisions will round up instead of down.
  • Shift the day number so Monday will be 0.

It also happens to be the number of seconds in a day.

\$\endgroup\$
1
  • \$\begingroup\$ Use y+=m>2; instead of m>2?y++:0; and shave a few bytes off. \$\endgroup\$
    – Clearer
    Commented Oct 28, 2017 at 22:36
2
\$\begingroup\$

Javascript, 126 123 characters

Using Sakamoto's algorithm with 0 = Sunday:

prompt().replace(/(....)(..)(..)/,function(_,y,m,d){y-=m<3;alert((+d+y-~(y/4)+~(y/100)-~(y/400)+ +".621462403513"[+m])%7)})

I suspect the divisions can be collapsed, but right now I'm not seeing it.

Edit: Improved the divisions (no need to ~~ when you can just ~).

\$\endgroup\$
2
\$\begingroup\$

Python 2, 83 116 113 109 bytes

Implements Sakamoto's algorithm. Golfing suggestions welcome. Try it online!

Edit: I should have fixed this ages ago. -6 bytes from Jonathan Allan's suggestions +2 bytes to actually fixing the code.

def w(s):m=int(s[4:6]);y,d=int(s[:4])-(m<3),int(s[6:]);return(y+y/4-y/100+y/400+int('032503514624'[m-1])+d)%7
\$\endgroup\$
2
  • \$\begingroup\$ Input should be a single string. \$\endgroup\$
    – msh210
    Commented Dec 7, 2015 at 20:46
  • \$\begingroup\$ int('032503514624'[m-1]) saves 6 \$\endgroup\$ Commented Mar 7, 2017 at 14:44
1
\$\begingroup\$

C (126 bytes)

main(y,m,d){scanf("%4d%2d%2d",&y,&m,&d);y%=400;putchar(((5*y+3)/4-y/100+"-KGGJLHJFIKGI"[m]+(!(y%4)&&m>2&&y%100||!y)+d)%7+48);}

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Perl -- 110 bytes

Here is a solution to be run with perl -p source.pl OR perl -pe 'here-is-code'.

s/((..)(..))(..)(..)/(1+3*$1+$2-2*($1%4+$2%4)-(2<$4?$4+(1&$4&&4-(8&$4)):(2^$4)+(!($3%4)-!-$3+!($2%4)))+$5)%7/e

Simply copy-paste the test cases to stdin.

This seems to be the only code without variables, string constants and divisions.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.