Day of the week of the next Feb 29th

14

1

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.

Jonathan M Davis

Posted 2011-02-17T22:15:09.950

Reputation: 705

Answers

7

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.

Joey

Posted 2011-02-17T22:15:09.950

Reputation: 12 260

What path is necessary for this to work? I have GnuWin date in my path, which is breaking it. – Peter Taylor – 2011-02-22T12:31:20.727

@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. – Joey – 2011-02-22T16:46:18.780

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? – Peter Taylor – 2011-02-22T16:53:12.310

@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. – Joey – 2011-02-22T17:19:30.187

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. – Peter Taylor – 2011-02-22T17:29:21.423

It seems to be broken with PS1 and work with PS2, so maybe you should update the header. – Peter Taylor – 2011-02-24T14:11:40.513

Peter: Please just upgrade. PowerShell v2 was released 1½ years ago and is the current version. That scripts created recently won't necessarily run under old versions should be common sense. – Joey – 2011-02-24T16:21:12.097

5

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!

Ventero

Posted 2011-02-17T22:15:09.950

Reputation: 9 842

4Fails the testcase for "0001-01-01". Ruby's Date is too powerful, it takes Julian dates into account. – steenslag – 2011-02-17T23:16:46.200

@Ventero What does def* do? Never seen it before. – steenslag – 2011-02-17T23:17:32.813

@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. – Ventero – 2011-02-17T23:22:01.640

1Couldn't you just define a lambda instead? a=->{...} – Wile E. Coyote – 2011-02-17T23:29:55.407

@Dogbert: Good call, thanks. Still used to Ruby 1.8, where a lambda has to be defined with f=lambda{|a|...}, which is longer than def* a;...;end. – Ventero – 2011-02-17T23:35:24.250

1Also, there's no need of any space after strftime. – Wile E. Coyote – 2011-02-17T23:42:40.153

And 1 until can be written as 1until. – Wile E. Coyote – 2011-02-17T23:43:01.137

3

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')

mootinator

Posted 2011-02-17T22:15:09.950

Reputation: 1 171

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. – mootinator – 2011-02-17T23:00:42.950

I love seeing these T-SQL solutions. :) – Steve – 2011-02-18T01:42:15.403

3

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

Kris Ivanov

Posted 2011-02-17T22:15:09.950

Reputation: 131

You can save 8 bytes by using a normal function definition: string f(string d){...} – Joey – 2011-02-18T05:31:13.590

.ToString("dddd") prints the date in the current locale, though, not in English. – Joey – 2011-02-18T05:42:48.317

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();} – Stephan Schinkel – 2014-08-27T08:09:56.167

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+"");} – Patrik Westerlund – 2014-08-29T14:43:13.263

3

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

Aman ZeeK Verma

Posted 2011-02-17T22:15:09.950

Reputation: 609

might have to set TZ if no default timezone is set, I learnt http://www.cyberciti.biz/faq/howto-linux-unix-change-setup-timezone-tz-variable/ while doing this on some online IDE

– Aman ZeeK Verma – 2011-02-18T13:34:37.807

1Are 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` – Peter Taylor – 2011-02-18T21:15:45.130

:-) 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! – Aman ZeeK Verma – 2011-02-18T22:00:38.773

1Rawness with bash is no excuse for using four-letter variable names :P – Peter Taylor – 2011-02-18T22:25:33.883

yes sir, I got your point. – Aman ZeeK Verma – 2011-02-18T22:49:39.633

On second thoughts the hyphens in %m-%d and 02-29 can be removed. And so can d=; - that's from an earlier version which used $d, but this one doesn't. – Peter Taylor – 2011-02-18T22:59:46.583

Your old bytecount was 229 bytes. 229 -> 2 29 -> Feb 29? O_o – HyperNeutrino – 2017-04-09T03:18:11.943

2

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.

Peter Taylor

Posted 2011-02-17T22:15:09.950

Reputation: 41 901

2

DATE and some BASHy glue (90)

The function:

f(){ while :;do $(date -d$1+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

user19214

Posted 2011-02-17T22:15:09.950

Reputation:

1

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

Titus

Posted 2011-02-17T22:15:09.950

Reputation: 13 814

1

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.

Jonathan M Davis

Posted 2011-02-17T22:15:09.950

Reputation: 705

1

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());
    }
}

Michael Easter

Posted 2011-02-17T22:15:09.950

Reputation: 585

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. – Michael Easter – 2014-08-28T23:57:38.820

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 - http://en.wikipedia.org/wiki/Java_version_history#Java_SE_8_.28March_18.2C_2014.29 . 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.)

– Michael Easter – 2014-08-29T03:18:39.523

1

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"

draegtun

Posted 2011-02-17T22:15:09.950

Reputation: 1 592

0

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

Toby Speight

Posted 2011-02-17T22:15:09.950

Reputation: 5 058