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.
12 Answers 12
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.
-
\$\begingroup\$ What path is necessary for this to work? I have GnuWin date in my path, which is breaking it. \$\endgroup\$Peter Taylor– Peter Taylor2011年02月22日 12:31:20 +00:00Commented Feb 22, 2011 at 12:31
-
\$\begingroup\$ @Peter: It might clash with
date. Just remove GNUWin32 from the PATH and it should work. Or changedatetoGet-Date(that kind of fallback behavior only works when no command is found – to check, just usegcm date). Anyway, I don't consider that a particular problem with this script as GNUWin32 is not part of any standard Windows installation. \$\endgroup\$Joey– Joey2011年02月22日 16:46:18 +00:00Commented Feb 22, 2011 at 16:46 -
\$\begingroup\$ The reason I asked was because I already tried using
get-dateand got the error messageMethod 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\$Peter Taylor– Peter Taylor2011年02月22日 16:53:12 +00:00Commented 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-Datebut aSystem.DateTime. \$\endgroup\$Joey– Joey2011年02月22日 17:19:30 +00:00Commented 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+=1or+="1"or+='1'gives the error message about PSObject. And just+"1"works. \$\endgroup\$Peter Taylor– Peter Taylor2011年02月22日 17:29:21 +00:00Commented Feb 22, 2011 at 17:29
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].
-
4\$\begingroup\$ Fails the testcase for "0001年01月01日". Ruby's Date is too powerful, it takes Julian dates into account. \$\endgroup\$steenslag– steenslag2011年02月17日 23:16:46 +00:00Commented Feb 17, 2011 at 23:16
-
\$\begingroup\$ @Ventero What does def* do? Never seen it before. \$\endgroup\$steenslag– steenslag2011年02月17日 23:17:32 +00:00Commented 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 betweendefand the function name. \$\endgroup\$Ventero– Ventero2011年02月17日 23:22:01 +00:00Commented Feb 17, 2011 at 23:22 -
1\$\begingroup\$ Couldn't you just define a lambda instead? a=->{...} \$\endgroup\$Dogbert– Dogbert2011年02月17日 23:29:55 +00:00Commented Feb 17, 2011 at 23:29
-
1\$\begingroup\$ Also, there's no need of any space after strftime. \$\endgroup\$Dogbert– Dogbert2011年02月17日 23:42:40 +00:00Commented Feb 17, 2011 at 23:42
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')
-
\$\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\$mootinator– mootinator2011年02月17日 23:00:42 +00:00Commented Feb 17, 2011 at 23:00
-
\$\begingroup\$ I love seeing these T-SQL solutions. :) \$\endgroup\$Steve– Steve2011年02月18日 01:42:15 +00:00Commented Feb 18, 2011 at 1:42
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");};
-
\$\begingroup\$ You can save 8 bytes by using a normal function definition:
string f(string d){...}\$\endgroup\$Joey– Joey2011年02月18日 05:31:13 +00:00Commented Feb 18, 2011 at 5:31 -
\$\begingroup\$
.ToString("dddd")prints the date in the current locale, though, not in English. \$\endgroup\$Joey– Joey2011年02月18日 05:42:48 +00:00Commented 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\$Stephan Schinkel– Stephan Schinkel2014年08月27日 08:09:56 +00:00Commented 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\$Patrik Westerlund– Patrik Westerlund2014年08月29日 14:43:13 +00:00Commented Aug 29, 2014 at 14:43
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
-
\$\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\$Aman ZeeK Verma– Aman ZeeK Verma2011年02月18日 13:34:37 +00:00Commented 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\$Peter Taylor– Peter Taylor2011年02月18日 21:15:45 +00:00Commented 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\$Aman ZeeK Verma– Aman ZeeK Verma2011年02月18日 22:00:38 +00:00Commented Feb 18, 2011 at 22:00
-
1\$\begingroup\$ Rawness with bash is no excuse for using four-letter variable names :P \$\endgroup\$Peter Taylor– Peter Taylor2011年02月18日 22:25:33 +00:00Commented Feb 18, 2011 at 22:25
-
\$\begingroup\$ yes sir, I got your point. \$\endgroup\$Aman ZeeK Verma– Aman ZeeK Verma2011年02月18日 22:49:39 +00:00Commented Feb 18, 2011 at 22:49
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.
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
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.
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());
}
}
-
\$\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\$Michael Easter– Michael Easter2014年08月28日 23:57:38 +00:00Commented 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\$Michael Easter– Michael Easter2014年08月29日 03:18:39 +00:00Commented Aug 29, 2014 at 3:18
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"
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
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