16
\$\begingroup\$

Write a function which takes a date and returns the day of the week of the next February 29th after that date.

  • The input is a string in the ISO Extended format: YYYY-MM-DD (e.g. May 27th, 2010 would be "2010-05-27").

  • The output is a string which is the name of the day of the week (e.g. "Monday"). Capitalization doesn't matter, but do give the full name in English.

  • If the given date is February 29th, then return the day of the week of the next Feburary 29th.

  • Use the calculations for the Proleptic Gregorian Calendar (so, it uses the Gregorian leap year calculations for it's entire length). Don't worry about the Julian Calendar or when the switch from Julian to Gregorian occurred. Just assume Gregorian for everything.

  • The function should work for at least the range of "0001-01-01" - "2100-01-01".

  • Feel free to use whatever standard libraries your language of choice provides, but do not use 3rd party libraries unless you want to include that code as part of your solution.

  • Shortest code (fewest characters) wins.

Examples:

  • func("0001-01-01") -> "Sunday"
  • func("1899-12-03") -> "Monday"
  • func("1970-01-01") -> "Tuesday"
  • func("1999-07-06") -> "Tuesday"
  • func("2003-05-22") -> "Sunday"
  • func("2011-02-17") -> "Wednesday"
  • func("2100-01-01") -> "Friday"

(and no, you don't have to name the function func)

Hints:

  • Remember that years ending in 00 which aren't divisable by 400 aren't leap years.
  • January 1st, 0001 is a Monday.
asked Feb 17, 2011 at 22:15
\$\endgroup\$
0

12 Answers 12

7
\$\begingroup\$

Windows PowerShell, 65

Fairly straightforward.

filter f{for($d=date $_;($d+='1').day*$d.month-58){}$d.dayofweek}

As usual, we can shave off two bytes if we are willing to wait a long time till completion:

filter f{for($d=date $_;($d+=9).day*$d.month-58){}$d.dayofweek}

Test:

> '0001-01-01','1899-12-03','1970-01-01','1999-07-06','2003-05-22','2011-02-17','2100-01-01'|f
Sunday
Monday
Tuesday
Tuesday
Sunday
Wednesday
Friday

History:

  • 2011年02月18日 00:06 (65) First attempt.
answered Feb 17, 2011 at 23:07
\$\endgroup\$
7
  • \$\begingroup\$ What path is necessary for this to work? I have GnuWin date in my path, which is breaking it. \$\endgroup\$ Commented Feb 22, 2011 at 12:31
  • \$\begingroup\$ @Peter: It might clash with date. Just remove GNUWin32 from the PATH and it should work. Or change date to Get-Date (that kind of fallback behavior only works when no command is found – to check, just use gcm date). Anyway, I don't consider that a particular problem with this script as GNUWin32 is not part of any standard Windows installation. \$\endgroup\$ Commented Feb 22, 2011 at 16:46
  • \$\begingroup\$ The reason I asked was because I already tried using get-date and got the error message Method invocation failed because [System.Management.Automation.PSObject] doesn't contain a method named 'op_Addition'. Does it need PS2 or .Net 4 or something? \$\endgroup\$ Commented Feb 22, 2011 at 16:53
  • \$\begingroup\$ @Peter: I tried PowerShell v2. .NET 4 doesn't play into it since PowerShell is linked to the 2.0 runtime. In any case, there shouldn't be a PSObject coming back from Get-Date but a System.DateTime. \$\endgroup\$ Commented Feb 22, 2011 at 17:19
  • \$\begingroup\$ It's very weird. $d=Get-Date "0400年01月01日"; $d++ gives an error message about ++ not being defined on DateTime. The same replacing ++ with +=1 or +="1" or +='1' gives the error message about PSObject. And just +"1" works. \$\endgroup\$ Commented Feb 22, 2011 at 17:29
5
\$\begingroup\$

Ruby 1.9, 85 characters

f=->a{require"date";d=Date.parse(a,0,0)+1;d+=1until d.day*d.month==58;d.strftime"%A"}

Straightforward solution. Call the function with f[args].

  • Edit: (87 -> 97) Fixed the 0001年01月01日 testcase.
  • Edit 2: (97 -> 91) Date.parse allows specifying the date of the calendar reform as well.
  • Edit 3: (91 -> 87) Use a lambda instead of a function. Thanks Dogbert!
  • Edit 4: (87 -> 85) Remove unnecessary spaces. Thanks again, Dogbert!
answered Feb 17, 2011 at 22:53
\$\endgroup\$
7
  • 4
    \$\begingroup\$ Fails the testcase for "0001年01月01日". Ruby's Date is too powerful, it takes Julian dates into account. \$\endgroup\$ Commented Feb 17, 2011 at 23:16
  • \$\begingroup\$ @Ventero What does def* do? Never seen it before. \$\endgroup\$ Commented Feb 17, 2011 at 23:17
  • \$\begingroup\$ @steenslag: Thanks, fixed that testcase. def* just defines a function called *. That way I don't need a space between def and the function name. \$\endgroup\$ Commented Feb 17, 2011 at 23:22
  • 1
    \$\begingroup\$ Couldn't you just define a lambda instead? a=->{...} \$\endgroup\$ Commented Feb 17, 2011 at 23:29
  • 1
    \$\begingroup\$ Also, there's no need of any space after strftime. \$\endgroup\$ Commented Feb 17, 2011 at 23:42
3
\$\begingroup\$

T-SQL (削除) 166 (削除ここまで) 185 Characters

CREATE PROCEDURE f29 (@d DATETIME) AS
DECLARE @i int
SET @i=YEAR(DATEADD(M,-2,@d)+3)
SET @i=@i+(4-@i%4)
IF @i%100=0 AND @i%400<>0 SET @i=@i+4
SELECT DATENAME(W,CAST(@i AS CHAR)+'-2-29')

I was already messing with T-SQL date functions, so I figured why not...

Original solution was incorrect...

Here's what I actually have to do to get that strategy to work:

CREATE PROCEDURE f29 (@d DATE) AS
DECLARE @i int
SET @i = YEAR(@d)
BEGIN TRY 
SET @i=YEAR(DATEADD(D, 3, DATEADD(M,-2,@d)))
END TRY
BEGIN CATCH
END CATCH
SET @i=@i+(4-@i%4)
IF @i%100=0 AND @i%400<>0 SET @i=@i+4
SELECT DATENAME(W,CAST(@i AS CHAR)+'-2-29')
answered Feb 17, 2011 at 22:37
\$\endgroup\$
2
  • \$\begingroup\$ Given that I create an overflow subtracting 2 months from 0001年01月01日 even if I do use the correct datatype I give up on creating something short and valid for this question. O_o. Trixsy questionzes. \$\endgroup\$ Commented Feb 17, 2011 at 23:00
  • \$\begingroup\$ I love seeing these T-SQL solutions. :) \$\endgroup\$ Commented Feb 18, 2011 at 1:42
3
\$\begingroup\$

C#, 176

Func<String,String>f=(d)=>{DateTime n;for(n=DateTime.Parse(d).AddDays(307);!(DateTime.IsLeapYear(n.Year));n=n.AddYears(1)){}return new DateTime(n.Year,2,29).ToString("dddd");};
answered Feb 18, 2011 at 4:03
\$\endgroup\$
4
  • \$\begingroup\$ You can save 8 bytes by using a normal function definition: string f(string d){...} \$\endgroup\$ Commented Feb 18, 2011 at 5:31
  • \$\begingroup\$ .ToString("dddd") prints the date in the current locale, though, not in English. \$\endgroup\$ Commented Feb 18, 2011 at 5:42
  • \$\begingroup\$ 165 chars => string f(string d){var n=DateTime.Parse(d).AddDays(307);while(!(DateTime.IsLeapYear(n.Year)))n=n.AddYears(1);return(new DateTime(n.Year,2,29)).DayOfWeek.ToString();} \$\endgroup\$ Commented Aug 27, 2014 at 8:09
  • \$\begingroup\$ Further improved to 127 characters: string f(string d){var n=DateTime.Parse(d).AddDays(1);return DateTime.IsLeapYear(n.Year)&&n.DayOfYear==60?n.DayOfWeek+"":f(n+"");} \$\endgroup\$ Commented Aug 29, 2014 at 14:43
3
\$\begingroup\$

Bash, 96 bytes

Thanks Peter... Highly golfed version, 96 bytes:

i=1;until [ `date -d"1ドル $i days" +%m-%d` = "02-29" ];do((i++));done
date -d "${1} ${i} days" +%A

Old version, 229 bytes

#!/bin/bash
from=1ドル
i=1
while [ "$isFeb" = "" ] || [ "$is29" = "" ]
do
isFeb=`date -d "${from} ${i} days" | grep Feb`
is29=`date -d "${from} ${i} days" +%Y-%m-%d | grep "\-29"`
((i++))
done
((i--))
date -d "${from} ${i} days" +%A

SAMPLE I/O

:~/aman>./29Feb.sh 0001年01月01日
Sunday
:~/aman>./29Feb.sh 1899年12月03日
Monday
:~/aman>./29Feb.sh 1970年01月01日
Tuesday
answered Feb 18, 2011 at 13:21
\$\endgroup\$
7
  • \$\begingroup\$ might have to set TZ if no default timezone is set, I learnt cyberciti.biz/faq/… while doing this on some online IDE \$\endgroup\$ Commented Feb 18, 2011 at 13:34
  • 1
    \$\begingroup\$ Are you going to golf this? ;p You can replace everything before the final line with i=0;d=;until [ `date -d"1ドル $i days" +%m-%d` = "02-29" ];do((i++));done \$\endgroup\$ Commented Feb 18, 2011 at 21:15
  • \$\begingroup\$ :-) as we already discussed bout my raw-ness over bash!... For some reason, I don't fiddle much with bash.. they are so error prone!.. Thanks for ur suggestion.. updated! \$\endgroup\$ Commented Feb 18, 2011 at 22:00
  • 1
    \$\begingroup\$ Rawness with bash is no excuse for using four-letter variable names :P \$\endgroup\$ Commented Feb 18, 2011 at 22:25
  • \$\begingroup\$ yes sir, I got your point. \$\endgroup\$ Commented Feb 18, 2011 at 22:49
2
\$\begingroup\$

Perl, no date library: (削除) 160 159 (削除ここまで) 155

sub f{($y,$m)=split/-/,@_[0],2;$y++if($m>'02-28');$y=($y+3)%400>>2;$y+=$y&&!($y%25);@r=(Tues,Wednes,Thurs,Fri,Satur,Sun,Mon);@r[(5*$y-($y/25&3))%7]."day";}

The real benefit of these date libraries is pushing off the length of the names of the days to someone else.

On the other hand, I think this is the only solution so far which works regardless of locale.

answered Feb 18, 2011 at 20:59
\$\endgroup\$
2
\$\begingroup\$

DATE and some BASHy glue (90)

The function:

f(){ while :;do $(date -d1ドル+1day +'set - %F %A 1%m%d');((3ドル==10229))&&break;done;echo 2ドル;}

Testing:

$ for x in 0001年01月01日 1899年12月03日 1970年01月01日 1999年07月06日 2003年05月22日 2011年02月17日 2100年01月01日 ; do f $x ; done
Sunday
Monday
Tuesday
Tuesday
Sunday
Wednesday
Friday
answered Aug 29, 2014 at 16:25
\$\endgroup\$
1
\$\begingroup\$

D: 175 Characters

S f(S)(S s){auto d=Date.fromISOExtString(s)+days(1);while(d.month!=Month.feb||d.day!=29)d+=days(1);return["Sun","Mon","Tues","Wednes","Thurs","Fri","Sat"][d.dayOfWeek]~"day";}

More Legibly:

S f(S)(S s)
{
 auto d = Date.fromISOExtString(s) + days(1);
 while(d.month != Month.feb || d.day != 29)
 d += days(1);
 return ["Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Sat"][d.dayOfWeek] ~ "day";
}

It's very easy to write in D, but it's definitely not going to win any code golfing contests. Still, outside of code golfing, I'd much rather have it easy to write and understand but long than have it terse but hard to write and understand.

answered Feb 22, 2011 at 6:55
\$\endgroup\$
1
\$\begingroup\$

Java 8 - 252 chars

Golfed:

import java.time.*;
import java.time.format.*;
public class S{public static void main(String[] a){LocalDate d=LocalDate.parse(a[0]).plusDays(1);while(d.getMonthValue()!=2||d.getDayOfMonth()!=29){d=d.plusDays(1);}System.out.println(d.getDayOfWeek());}}

Ungolfed:

import java.time.*;
import java.time.format.*;
public class S {
 public static void main(String[] a) {
 LocalDate d = LocalDate.parse(a[0]).plusDays(1);
 while(d.getMonthValue()!=2 || d.getDayOfMonth()!=29) {
 d = d.plusDays(1);
 }
 System.out.println(d.getDayOfWeek());
 }
}
answered Aug 28, 2014 at 20:51
\$\endgroup\$
2
  • \$\begingroup\$ I accept downvotes but can you please explain "the necro is real" and link to a FAQ that explains your point? I didn't think I would win but thought it would be interesting to see how Java could do, esp. given Java 8. I view code golf as having "weight classes" versus an outright competition. Java will rarely beat Ruby or Python outright. \$\endgroup\$ Commented Aug 28, 2014 at 23:57
  • \$\begingroup\$ The OP prohibits 3rd party libraries, so in 2011, a Java solution would be unpleasant, as one couldn't use the popular Joda Time library. However, Java 8 (released in March 2014) contains the JSR-310 DateTime library - en.wikipedia.org/wiki/… . My reason is that I thought Java 8 would be fun, and potentially interesting to some readers. (If you aren't one of those, fine: there is still a reason for the post.) \$\endgroup\$ Commented Aug 29, 2014 at 3:18
1
\$\begingroup\$

Rebol - 78

f: func[d][system/locale/days/(until[d: d + 1 d/day * d/month = 58]d/weekday)]

Ungolfed:

f: func [d] [
 system/locale/days/(
 until [
 d: d + 1
 d/day * d/month = 58
 ]
 d/weekday
 )
]

Example usage (in Rebol console):

>> f 0001年01月01日
== "Sunday"
>> f 2100年01月01日
== "Friday"
answered Aug 29, 2014 at 13:45
\$\endgroup\$
1
\$\begingroup\$

PHP, 104 bytes

function f($s){for($y=$s-(substr($s,5)<"02-29");!date(L,$t=strtotime(++$y."-2-1")););return date(l,$t);}

breakdown

for($y=$s-; // "-" casts the string to int; decrement year if ...
 (substr($s,5)<"02-29") // ... date is before feb 29
 !date(L, // loop while incremented $y is no leap year
 $t=strtotime(++$y."-2-1") // $t=feb 01 in that year (same weekday, but shorter)
 );
);
return date(l,$t); // return weekday name of that timestamp

date(L): 1 for leap year, 0 else
date(l): full textual representation of the day of the week

answered Mar 1, 2017 at 22:07
\$\endgroup\$
0
\$\begingroup\$

GNU coreutils, 71 bytes

f(){
seq -f "1ドル %gday" 2921|date -f- +'%m%d%A'|sed 's/0229//;t;d;:;q'
}

Linear search of the next 8 years (worst case, given 2096年02月29日). Sed finds and outputs the first line that matches 29th of February.

I was surprised to find that t;d;:;q was shorter than testing to see whether the digits remained (/[01]/d;q), despite being twice as many commands.

Tests

I added an extra line of tests for difficult corner cases:

for i in 0001年01月01日.Sunday 1899年12月03日.Monday \
 1970年01月01日.Tuesday 1999年07月06日.Tuesday \
 2003年05月22日.Sunday 2011年02月17日.Wednesday 2100年01月01日.Friday \
 2000年02月28日.Tuesday 2000年02月29日.Sunday 2000年03月01日.Sunday 2096年02月29日.Friday
do
 printf "%s => %s (expected %s)\n" ${i%.*} $(f ${i%.*}) ${i#*.}
done
answered Dec 17, 2015 at 19:05
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.