When is Thanksgiving?

38

5

Background

Some holidays have fixed, easy-to-remember dates, like Oct 31, Dec 25, etc. Some, however, want to be troublesome. They're specified as things like "the first Monday in September" or "the fourth Thursday in November". How am I supposed to know when that is?

All I know is that Thanksgiving is fast approaching, so I need a program to help me figure out when it is. Some people even say that it's tomorrow, so your program needs to be as short as possible to make sure I can re-type it in time.

The Challenge

Create a program or function that, given an up-to-four-digit year (e.g. 2015 or 1984), outputs or returns the date of the United States' Thanksgiving in that year. Thanksgiving is defined as the fourth Thursday of November according to the Wikipedia page. (Hint: that page also includes some interesting information on the date pattern.)

Input: a decimal number with a maximum of four digits representing a year in the Common Era (C.E.). Examples: 987, 1984, 2101

Output: the date, including month and day, on which Thanksgiving falls, or would fall if it existed, in that year. This may be in any reasonable format; use your best judgment. Use the Gregorian Calendar in all cases, even if it was not in use at the time.

(Note: Make sure to handle leap years correctly!)

Test cases Input 1:

2015

Output 1:

Nov 26

Input 2:

1917

Output 2:

Nov 22

Scoring

Submissions will be scored in bytes. I recommend this website to keep track of your byte count, though you can use any counter you like.

Bonuses

-25% to your score if you handle B.C.E. dates as negative numbers (e.g. -480 would be the year of the battle of Thermopylae).

Negative test case input:

-480

Corresponding output:

Nov 25

This is , so the lowest score wins!

Edit: I am marking Thomas Kwa's TI-BASIC submission as accepted. Don't let this discourage you from submitting new entries!

Leaderboards

Here is a Stack Snippet to generate both a regular leaderboard and an overview of winners by language.

To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:

# Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

# Ruby, <s>104</s> <s>101</s> 96 bytes

If there you want to include multiple numbers in your header (e.g. because your score is the sum of two files or you want to list interpreter flag penalties separately), make sure that the actual score is the last number in the header:

# Perl, 43 + 2 (-p flag) = 45 bytes

You can also make the language name a link which will then show up in the leaderboard snippet:

# [><>](http://esolangs.org/wiki/Fish), 121 bytes

var QUESTION_ID=64785,OVERRIDE_USER=45162;function answersUrl(e){return"http://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"http://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?([\d.]+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

jqblz

Posted 2015-11-25T18:01:54.620

Reputation: 2 062

26For anyone who is wondering when Thanksgiving will be coming up this year: Thanksgiving will be tomorrow . – TheNumberOne – 2015-11-25T18:07:09.173

6But I had thanksgiving on October 10th? I'm sorry to say your question is late. – JimmyJazzx – 2015-11-25T18:17:28.967

2@JimmyJazzx brings up a good point. In Canada, Thanksgiving is on the second Monday in October. You should probably specify that you are talking about United States' Thanksgiving. – TheNumberOne – 2015-11-25T18:23:20.533

@TheNumberOne Thanks! I've edited the challenge to clarify. – jqblz – 2015-11-25T18:31:47.287

Must we use that byte counter or may we use that byte counter or may we use our own? Some languages like APL, have their own character set, which counts bytes differently – Downgoat – 2015-11-25T18:43:25.680

5@Downgoat Use whichever byte counter you want. That's just the one I use personally and would recommend. – jqblz – 2015-11-25T18:44:24.727

Do you want the de facto Thanksgiving dates or the dates that follow your rule? Since 1789 (in the US), Thanksgiving Day was observed on inconsistent days. From 1863 to 1940 it was on the last Thursday of November. Since then it has been celebrated on the 4th Thursday of that month. https://www.archives.gov/legislative/features/thanksgiving/

– DavidC – 2015-11-25T19:05:11.460

@DavidCarraher The legal holiday is on the fourth Thursday.

– LegionMammal978 – 2015-11-25T19:07:03.260

1@LegionMammal978, only since 1941 has Thanksgiving been taken to be on the 4th Thursday, even though it was officially celebrated before then. – DavidC – 2015-11-25T19:12:35.693

23"Some holidays have fixed, easy-to-remember dates, like Oct 31, Dec 25" Those examples are easy to remember because they're really the same day: octal 31 == decimal 25. ;-) – Adrian McCarthy – 2015-11-25T19:26:59.823

@Adrian McCarthy That wasn't unintentional. – jqblz – 2015-11-25T19:36:38.427

What should be the output for 0? – ETHproductions – 2015-11-25T19:47:01.537

@Eth I've got Nov 22 with my program, probably posting it soon. – nicael – 2015-11-25T19:52:24.690

@nicael I get that for 0 and 1. – LegionMammal978 – 2015-11-25T19:52:54.750

@AdrianMcCarthy Whenever I forget when Christmas is, I always use this method of converting octal 31 to decimal :) – user41805 – 2015-11-25T20:09:15.527

7For an answer that does the bonus, should there be a year 0 between -1 and 1 or not? – feersum – 2015-11-25T21:07:33.470

What about Easter? – Édouard – 2015-11-25T22:37:28.997

2

Most answers using buildin date functions apply the Julian calendar before 1582 and implement the date jump from Oct 4 to Oct 15 in 1582. The challenge states that we should "use the Gregorian calendar in all cases, even if it was not in use at the time.", so for example 1400 isn't a leap year. Also, the gap of 10 days in 1582 (or 11 days in 1752, which is more accurate for America) makes 1582 and 1752 good test cases. I'd say the correct answer for 1752 is Nov 23. 1582 would be Nov 25 and 1400 is Nov 27.

– agtoever – 2015-11-25T22:55:49.537

Is outputting the day of the year (e.g. 329 for Nov 26 in a common year) acceptable? – Mego – 2015-11-26T04:15:20.777

@Mego No, you need the month and day. – jqblz – 2015-11-26T04:20:23.650

Are we allowed to output in the format MMDD (e.g. 1126 for Nov. 26)? – lirtosiast – 2015-11-26T21:21:25.360

@ThomasKwa Sure, you can do MMDD. – jqblz – 2015-11-26T22:06:58.777

Answers

40

TI-BASIC, 15 bytes * 0.75 = 11.25

Tested on my TI-84+ calculator

1129-dayOfWk(Ans+ᴇ4,9,1

Thankgiving is November 29, minus the day of the week of September 1st, where 1 is Sunday and 7 is Saturday. This outputs in the format MMDD.

Test cases: 2015 -> 1126, 1917 -> 1122, -480 -> 1125 have been verified. TI-BASIC seems to use the Gregorian calendar for all dates.

TI-BASIC doesn't support negative years, but this gets the bonus because we add 10000 to the input. Because the Gregorian calendar has a period of 400 years, this doesn't change the day of the week.

lirtosiast

Posted 2015-11-25T18:01:54.620

Reputation: 20 331

5Holy crap. xD I never would've thought this would have worked like this, +1. – Addison Crump – 2015-11-26T17:42:16.440

Good idea to add 10000. Nice. – agtoever – 2015-11-27T06:16:50.150

I count 23 Letters. How can this be 15 Bytes ? – Stephan Schinkel – 2015-12-18T09:41:08.237

1@StephanSchinkel TI-BASIC uses tokens that are stored in the calculators memory, I believe all the characters here are 1 byte except for the tokens dayofWK( and Ans which are 2 and 1 bytes each. – FryAmTheEggman – 2015-12-18T15:00:09.463

You tested it on a calculator? wow. – None – 2016-07-07T11:45:06.610

23

PHP, 65 48 42 41 36 (+2 for -F) = 38 bytes

<?date(Md,strtotime("4thuXI$argn"));

Takes input as the first command line argument. Runs with warnings, which are acceptable by our rules. Prints NovDD, where DD is the day of Thanksgiving.

No online link because ideone doesn't support command line args and I don't know of an online interpreter that does.

Thanks to Alexander O'Mara for teaching me a new trick, and primo for a significant reduction

Mego

Posted 2015-11-25T18:01:54.620

Reputation: 32 998

2This is funnier than Mathematica. – lirtosiast – 2015-11-25T21:31:39.520

You can shorten the string to "fourth thu ofXI" – Tryth – 2015-11-26T00:59:38.190

2@Tryth Even shorter: "4thuXI". You can even drop the space between the year "4thuXI2015". – Alexander O'Mara – 2015-11-26T01:04:04.107

1Using the command line option -F, input could be shortened to "4thuXI$argn". – primo – 2015-11-26T08:01:24.333

3Wait, this actually works? That's...that's just...I don't even... – ETHproductions – 2015-11-26T15:23:01.893

Input could also be piped in directly, e.g. echo 2015 | php -F thanks.php. – primo – 2015-11-27T00:33:06.623

1Works for me. While I'm at it, Md needs no quotes, with E_NOTICE disabled. – primo – 2015-11-27T06:20:24.417

18

Mathematica, 59 38 bytes

DayRange[{#,11},{#,12},Thursday][[4]]&

LegionMammal978

Posted 2015-11-25T18:01:54.620

Reputation: 15 731

+1 Clever. There is also WolframAlpha["Thanksgiving " <> #] & where the date is entered as a string. – DavidC – 2015-11-25T18:53:01.467

WolframAlpha returns the US Thanksgiving day dates from 1863 (when Lincoln declared it to be on the LAST Thursday of November) onward. The US has observed Thanksgiving since 1789. – DavidC – 2015-11-25T19:09:26.930

@DavidCarraher However, the OP states that the program has to work for at least 0 - 9999 C.E. – LegionMammal978 – 2015-11-25T19:15:29.210

17

JavaScript (ES6), 42 40 31 bytes - 25% = 23.25

a=>28.11-new Date(a,8).getDay()

Since the date "may be in any reasonable format", this function uses DD.MM. I wrote a TeaScript answer with a different technique, but this formula was shorter.

Explanation

As the months are zero based, new Date(a,10) returns a Date object representing November 1 of the specified year.

Since getDay() returns a number representing the day of week from 0..6 we want to map from

Su Mo Tu We Th Fr Sa 
0  1  2  3  4  5  6  
to to to to to to to
4  3  2  1  0  6  5  

then add 22. It turns out that (11 - new Date(a,10).getDay()) % 7 will do the trick. As @Thomas Kwa pointed out, this is the same as 28-new Date(a,8).getDay() which is 28 minus the day of the week of September 1.

intrepidcoder

Posted 2015-11-25T18:01:54.620

Reputation: 2 575

Nice solution, I love how short it is and that it's still more understandable than most of entries. – Marko Grešak – 2015-11-28T19:22:22.703

16

Japt, 43 37 36 35 29 bytes - 25% = 21.75

Japt is a shortened version of JavaScript.

`{28.11-?w D?e(U,8).getDay()}

Hahaha, I found a really cheaty trick: the interpreter ignores any brackets inside strings (used to insert code) while decompressing them, so we can compress the entire source code to save a byte >:D

The two ?s should be the Unicode unprintables U+0098 and U+0085, respectively. Try it online!

After decompression, the code evaluates to this:

"{28.11-new Date(U,8).getDay()}"

Which evaluates to:

""+(28.11-new Date(U,8).getDay())+""

Which gives the proper output.

Uses intrepidcoder's technique, outputting in format dd.mm. Properly supports negative years. Suggestions welcome!

Edit: As of Dec 2, you can now use this 11-byte code (scoring 8.25 points):

28.11-ÐU8 e

(I so wish I had implemented this sooner!)

ETHproductions

Posted 2015-11-25T18:01:54.620

Reputation: 47 880

Very nicely done. – jqblz – 2015-11-25T22:26:30.640

This is now longer than vanilla JavaScript. Thomas Kwa has a much shorter formula.

– intrepidcoder – 2015-11-26T21:43:06.340

@intrepidcoder Thanks, fixed. – ETHproductions – 2015-11-28T16:07:35.803

12

Vitsy, 44 bytes

I'm calculating with pure mathemagics!

Golfed:

Ve2*V2-V41m+Vaa*Dv1m-Vv4*1m+7M-baa*/+N
/D1M-

Ungolfed (moved the method call to the first line to make it readable):

Ve2*V2-V4/D1M-+Vaa*Dv/D1M--Vv4*/D1M-+7M-baa*/+N

V         Save the input as a final variable.
 e2*      Push 28 to the stack.
    V         Push the input to the stack.
     2-         Subtract two.
       V4/D1M-        Get floor(input/4).
              +        Add it to the total.
               Vaa*Dv/D1M-          Get floor(input/100), and save 100 as a temp
                                    variable in the process.
                          -          Subtract it from the total.
                           Vv4*/D1M-         Get floor(input/400).
                                    +         Add it to the total.
                                     7M       Modulo by seven.
                                       -       Subtract the result from 28.
                                        baa*/+      Add .11
                                              N      Output as number.

There's probably a better algorithm for this (and this is likely horridly golfed), but for those wondering, my algorithm comes from here.

Try it online!

Do I get bonus points for calculating it and having exactly 42 bytes? Dreams ruined.

Thanks to @Hosch250 for pointing out I was doing it wrong. :D Fixed.

Addison Crump

Posted 2015-11-25T18:01:54.620

Reputation: 10 763

2No. but you get a +1 from me ;) – Conor O'Brien – 2015-11-26T01:05:50.590

It says that Thanksgiving in 2016 is Nov 28. It is really Nov 24. – None – 2015-11-28T17:08:34.017

@Hosch250 You're right - fixed now. – Addison Crump – 2015-11-28T17:56:13.460

10

JavaScript (ES6), 43.5

Actual byte count is 58. The bonus of -25% applies => 58 * 0.75 = 43.5

s=>new Date(s,10,(a=new Date(s,10).getDay())<5?26-a:a+21)

Pretty straight and silly way as it could be, without any tricky workarounds or calculations.

De-golf (ES5) + demo:

function t(s) {
    a = new Date(s, 10).getDay();
    alert(new Date(s,10,a<=4?26-a:a+21))
}

t(prompt())

Note, that entering year 0-100 produces 1900-2000 year. Though, it looks like 0-100 years give the same date as do 1900-2000, judging from the other answers.


Replacing a+18 with 22, because it's called only in "else", and "else" occurs only if a is neither greater nor less than 4, i.e. exactly 4.


Replacing a<4?26-a:a>4?a+21:22 with a<=4?26-a:a+21

nicael

Posted 2015-11-25T18:01:54.620

Reputation: 4 585

2Pardon me if I'm wrong, but isn't a<4?x:a>4?y:a+18 equivalent to a<4?x:a>4?y:22? – ETHproductions – 2015-11-25T22:05:25.120

BTW, it tells me the wrong year for any input from 0 to 99. Not sure if this matters, though. – ETHproductions – 2015-11-25T22:08:59.363

@Eth LOL of course you're right, didn't think that's always 4+18 :D – nicael – 2015-11-25T22:11:04.590

@Eth hm, 0 to 99 is indeed wrong, let my try fixing that. – nicael – 2015-11-25T22:13:45.130

@Eth It looks like 0-100 give the same date that 1900-2000, judging from the other answers. – nicael – 2015-11-25T22:18:04.923

One more byte: x<=4 == x<5 ;) – ETHproductions – 2015-11-27T17:57:41.967

Also, you don't need to alert; returning is enough. This means you can cut off another sizable chunk: s=>new Date(s,10,(a=new Date(s,10).getDay())<5?26-a:a+21) – ETHproductions – 2015-11-27T18:00:54.053

@Eth That's just insane - so obvious, but I couldn't golf this anymore... :D – nicael – 2015-11-27T18:26:52.340

can you drop the new keyword? – royhowie – 2015-11-27T20:46:37.647

@roy No, won't work. – nicael – 2015-11-27T22:19:27.090

10

Python, 38 * 0.75 = 28.5 bytes

lambda x:28.11-(x-2+x/4-x/100+x/400)%7

This works with negative years in the manner specified in the question, although it's come to my attention that there is no year 0 in the Gregorian calendar so this behavior is a bit suspect.

Luke

Posted 2015-11-25T18:01:54.620

Reputation: 5 091

1You can drop f=. – feersum – 2015-11-25T22:51:04.630

1How does this work for negative dates? Does modulo in Python apply to negatives? :O – Addison Crump – 2015-11-26T00:58:38.417

It should be shorter to use string reps, "Nov "+`...` – xnor – 2015-11-26T01:00:16.650

@VoteToClose Python modulo gives an answer with the same sign as the second argument. Thus -40%7==2 but -40%-7==-5 – quintopia – 2015-11-26T05:39:31.583

@quintopia Wait, hold on, I'm an idiot. Okay. That makes sense. – Addison Crump – 2015-11-26T11:14:34.663

I think you can change "Nov "+ at the beginning to +"-11" at the end to save a byte. – Sherlock9 – 2015-11-26T18:01:16.753

user5535756 would like to know how this works (but doesn't have enough rep to comment). – Martin Ender – 2015-11-27T09:11:59.360

This appears to be using the Doomsday Rule for calculation - is that correct?

– Mego – 2015-11-27T11:06:17.683

It uses Sakamoto's algorithm.

– xsot – 2015-11-27T12:09:24.140

You could save a few bytes by outputting in dd.mm format: lambda x:28.11-(x-2+... – ETHproductions – 2015-11-27T15:26:28.450

Yes, this is just a very specific case of Sakamoto's algorithm. @ETHproductions thanks for the further golfing tip! Didn't think this could get any shorter. – Luke – 2015-11-27T18:27:34.217

8

TeaScript, 35 33 24 bytes - 25% = 18

28.11-new D(x,8).getDay¡

This is the same method as my JavaScript answer, which uses Thomas Kwa's clever formula.

Alternate Version with Explanation

r(22,28)F(#/h/t(new D(x,10,l)))+'-11'

r(22,28)    // Range from 22 to 28
        F(    // filter, keeps elements for which the function returns true. 
          #    // Expands to function(l,i,a,b)
           /h/    // RegExp literal, Thursday is the only day with an 'h'.
              t(    // test, true if date string contains an 'h'.
                new D(x,10,l) // Create date object
                             ))+'-11' // Append '-11' for the month.

intrepidcoder

Posted 2015-11-25T18:01:54.620

Reputation: 2 575

It's 35 chars, but 36 bytes due to ¡ :) – nicael – 2015-11-25T22:42:22.620

@nicael Not by this site, it's not ;)

– ETHproductions – 2015-11-25T22:43:10.863

@Downgoat usually uses this format, in which all chars up to U+00FF are 1 byte.

– ETHproductions – 2015-11-25T22:45:52.583

5

Python, 83 81 78 bytes

from datetime import*
lambda a:'Nov '+`((10-datetime(a,11,1).weekday())%7)+22`
  • -2 bytes: added a name to import (thanks @Κριτικσι Λίθος)
  • -1 bytes: changed to *import** (thanks @FryAmTheEggman)
  • -2 bytes: changed to repr to convert the day (thanks @willem)

Rod

Posted 2015-11-25T18:01:54.620

Reputation: 17 588

1Just a suggestion to reduce byte count: use import datetime as d and then you can use d for every time you use datetime in your program. – user41805 – 2015-11-25T19:38:01.417

2You could also try pandas. import pandas;print'Nov '+\((10-pandas.datetime(input(),11,1).weekday())%7)+22`` – Willem – 2015-11-25T19:39:57.637

@ΚριτικσιΛίθος but then datetime.datetime would only be d.datetime, wouldn't it? – TanMath – 2015-11-25T19:50:52.473

2from datetime import* is even a bit shorter, as you won't need the d. any more – FryAmTheEggman – 2015-11-25T22:00:08.503

5

Excel, 367 154 53 - 25% = 39.75 bytes

Assumes the year is held in cell A1, in Date format. Returns the integer number of the day in November on which Thanksgiving is held.

This only accounts for normal leap years. It does not account for the fact that the years, 2100, 2200, 2300 are not leap years.

This is only designed to work for 1621 onwards - i.e. since Thanksgiving began being held. (Although it will certainly work all the way back to 0 A.D.).

=IF(MOD(YEAR(A1),4)=0,IF(WEEKDAY(305+DATEVALUE(("01/01/"&TEXT(A1,"yyyy"))))<6,1127-WEEKDAY(305+DATEVALUE(("01/01/"&TEXT(A1,"yyyy")))),1134-WEEKDAY(305+DATEVALUE(("01/01/"&TEXT(A1,"yyyy"))))),IF(WEEKDAY(304+DATEVALUE(("01/01/"&TEXT(A1,"yyyy"))))<6,1127-WEEKDAY(304+DATEVALUE(("01/01/"&TEXT(A1,"yyyy")))),1134-WEEKDAY(304+DATEVALUE(("01/01/"&TEXT(A1,"yyyy"))))))

Pretty-printed:

=IF(MOD(YEAR(A1),4)=0,
    IF(WEEKDAY(305+DATEVALUE(("01/01/"&TEXT(A1,"yyyy"))))<6,
       1127-WEEKDAY(305+DATEVALUE(("01/01/"&TEXT(A1,"yyyy")))),
       1134-WEEKDAY(305+DATEVALUE(("01/01/"&TEXT(A1,"yyyy"))))),
    IF(WEEKDAY(304+DATEVALUE(("01/01/"&TEXT(A1,"yyyy"))))<6,
       1127-WEEKDAY(304+DATEVALUE(("01/01/"&TEXT(A1,"yyyy")))),
       1134-WEEKDAY(304+DATEVALUE(("01/01/"&TEXT(A1,"yyyy"))))))

Gah! Instead of calculating based on the 1st of Jan, then doing lots of leap year calculations to cope with the 29th of Feb, I should have based the calculations on the 1st of Nov. n.b. This now deals correctly with the years 2100, 2200 and 2300, but makes the implementation dependent on your Excel installation's default date format. This version is designed for dd/mm/yyyy:

 =IF(WEEKDAY(DATEVALUE(("01/11/"&TEXT(C1,"yyyy"))))<6,  
     1127-WEEKDAY(DATEVALUE(("01/11/"&TEXT(C1,"yyyy")))),
     1134-WEEKDAY(DATEVALUE(("01/11/"&TEXT(C1,"yyyy")))))

And now that I've done the pfaffing about to get a terse calculation in Smalltalk, backporting it to Excel results in:

=DATE(A1,11,MOD(12-WEEKDAY(DATE(9600+A1,11,1)),7)+22)

(with the year still in A1, but as an integer). This works even for years 2100, 2200 and 2300, for all dates from 7700BC/E onwards, using Thomas Kwa's date repetition trick.

Euan M

Posted 2015-11-25T18:01:54.620

Reputation: 151

1When I try this with dates prior to 1900 it doesn't work in my copy of excel. – Angelo Fuchs – 2015-11-26T08:37:43.520

The long version doesn't work as well. – Angelo Fuchs – 2015-11-26T08:49:55.880

For old dates, use Excel User's pre-1900 dates workbook. http://exceluser.com/downloads/examples/post_900_123/index.html

– Euan M – 2015-11-27T00:42:37.247

Questions about whether this works aside, I don't think you're allowed to output just the date in November; you need a "Nov" or "11" in some form. – lirtosiast – 2015-11-27T01:59:00.603

@Thomas Kwa The date input is the year, in date format. The calculation then works out the day of the week that the 1st of November falls on, then calculates what date the fourth Thursday in November is. – Euan M – 2015-11-27T02:16:46.103

Yes, the spec allows for numeric Novs. I'll amend to suit – Euan M – 2015-11-27T02:27:24.440

5

Jython, 141 155 Bytes

Uses the Java Calendar and Scanner classes with Python syntax.

import java.util.Scanner as s;import java.util.Calendar as c
d=c.getInstance();k=s(System.in);d.set(c.YEAR,k.nextInt());d.set(c.DAY_OF_WEEK,c.THURSDAY)

Edit: Minor syntax issues, added 14 bytes.

Also see my Brainfuck version.

user47629

Posted 2015-11-25T18:01:54.620

Reputation:

4

PowerShell, 67 64 84 72 58 45 Bytes

param($a)'Nov';28-(date "00$a/9/1").DayOfWeek

We take our input integer as $a, and immediately output Nov and a newline. Then we take $a and prepend it with zeroes and September 1st with 00$a/9/1 before generating a new date and determining what DayOfWeek that is. If September 1st is on a Sunday (.DayOfWeek equal to 0), Thanksgiving is on the 28th. If September 1st is on a Monday (.DayOfWeek equal to 1), Thanksgiving is on the 27th. And so on. Thus, we subtract that day of the week from 28 to output our answer.

Prepending with double-zeroes accounts for single- and double-digit years without interrupting parsing for three- or four-digit years. Doesn't work for negative numbers, as .NET datetime doesn't support years less than 0.

Thanks to TessellatingHeckler and Toby Speight for assistance on this.

AdmBorkBork

Posted 2015-11-25T18:01:54.620

Reputation: 41 581

"{0:D3}/Nov/$_" -f $a is shorter than "$($a.ToString("000"))/Nov/$_" and produces the same result, and I think you could use 11 instead of Nov – n0rd – 2015-11-25T22:51:22.073

@n0rd Ah, excellent! Yes -- the Nov was a remnant from when I was trying to get it to recognize two-digit years properly, so I'll swap that out, too. Thanks! – AdmBorkBork – 2015-11-27T16:17:26.213

@TessellatingHeckler That approach works, but you need to prepend two zeroes to account for one-digit years, else you're back into the 20XX that I ran into. Thanks for the assist - I updated the answer. – AdmBorkBork – 2015-12-16T14:52:39.673

1

@TessellatingHeckler On my system, at least, inputting single-digit years into date "11/1/$a" results in Thursday, November 1, 2007 12:00:00 AM. This is because we're not editing the Calendar.TwoDigitYearMax property, so when it parses, we're stuck with double-zero-padding to get around that.

– AdmBorkBork – 2015-12-16T17:12:00.593

Right, right. Double padding it is. – TessellatingHeckler – 2015-12-16T17:15:57.530

I think you can shorten this a little further by picking a different sample date for day-of-week. See my answer where I use a date in early May. And then you could use 28-... or 29-... depending on whether PowerShell reports 0-based or 1-based weekdays.

– Toby Speight – 2015-12-18T09:29:55.323

Thanks @TobySpeight - That golfed a bunch! I selected September 1st because that lined up the dates nicely that I could just subtract them. – AdmBorkBork – 2015-12-18T14:51:54.303

3

Golfscript, 25 * 0.75 = 18.75 bytes

~.4/.25/.4/2---+7%28\--11

This uses Sakamoto's formula for the day of the week. Since there are people doing this, the output is in the form of dd-mm. My previous submission can be found below:

~.4/.25/.4/2---+7%"Nov "28@-

xsot

Posted 2015-11-25T18:01:54.620

Reputation: 5 069

3

Pyth, 30 28 * 75% = 21 bytes

I'm 100% positive that this could be made shorter, but hey, it's my first Pyth program! \o/

-28.11%+-+-Q2/Q4/Q100/Q400 7

Test suite

Outputs the date in dd.mm format.

Please suggest ways to golf this if you can! I'd like to learn more about Pyth.

ETHproductions

Posted 2015-11-25T18:01:54.620

Reputation: 47 880

3

TSQL, 478 296 bytes

Just for funsies. Typically, you'd have a date dimensional table to make this a simple select * from dimDate where [Year] = @Year and [Holiday] = 'Thanksgiving' but in the absence of that...

create function a(@y varchar(4))returns date as begin declare @d date,@i int=0 set @d=convert(date,@y+'-11-01')declare @t table(d date,c int identity)while @i<28 begin if datepart(weekday,@d)=5 insert @t(d) select @d select @d=dateadd(d,1,@d),@i+=1 end select @d=d from @t where c=4 return @d end

Ungolfed:

if exists(select * from sys.objects where name='a' and [type]='FN') drop function a
go

create function a(@y varchar(4))returns date
-- select dbo.a('2015')
as
begin
    declare @d date,@i int=0;

    set @d=convert(date,@y+'-11-01'); -- cannot golf out dashes for dates <year 1000

    declare @t table(d date,c int identity)

    while @i<28 
    begin -- populate "November" array
        if datepart(weekday,@d)=5 insert @t(d) select @d -- assumes @@datefirst = 7
        select @d=dateadd(d,1,@d),@i+=1
    end;

    select @d=d from @t where c=4 

    return @d

end

Peter Vandivier

Posted 2015-11-25T18:01:54.620

Reputation: 211

Is this really the shortest way? – lirtosiast – 2015-11-26T21:05:50.960

I suppose I may not be familiar with the rules of code golf, but the answer creates a DB object that accepts input and returns output to match the OP. There's some obvious shortening that could occur if input can be accepted into inline code but this was my thought for the function object type. I'm sure it could be golfed further in either case. @ThomasKwa, do you have suggestions? – Peter Vandivier – 2015-11-26T21:12:39.767

Well, it seems to me you could use the pure-math formula that this answer mentions and not bother with all that date object stuff.

– lirtosiast – 2015-11-26T21:18:07.367

not strictly a pure-math solution, but reading that answer definitely gave me some ideas to golf out 200 characters. Thanks!! – Peter Vandivier – 2015-11-26T21:42:49.883

(I meant (28 - ( x - 2 + floor(x / 4) - floor(x / 100) + floor(x / 400) ) % 7) Yeah, reading other answers tends to help a lot. – lirtosiast – 2015-11-26T21:45:10.190

TBH, i don't fully understand why that formula works ( not pegged to a Datepart of Day = Thursday? ). – Peter Vandivier – 2015-11-26T22:05:41.730

This is a shorter way for the first line: if object_id('a','FN') is not null drop function a – Pieter Geerkens – 2015-11-29T01:53:42.983

Got this down to 215 bytes using a mini-tally table and an ITVF - see my answer below – Pieter Geerkens – 2015-11-29T02:57:09.020

Now down to 186 bytes, handling negative years at least as far back as 5480 BCE. – Pieter Geerkens – 2015-11-29T07:10:59.623

@lirtosiast there is a shorter way now – t-clausen.dk – 2016-07-07T11:55:13.203

2

Excel, 64 48

Year in A1

=DATE(A1,11,CHOOSE(WEEKDAY(DATE(A1,11,1)),26,25,24,23,22,28,27))

=DATE(A1,11,MOD(12-WEEKDAY(DATE(A1,11,1)),7)+22)

SeanC

Posted 2015-11-25T18:01:54.620

Reputation: 1 117

As Excel is localization based and I would like to test this. Please explain what the functions do, so I can select the proper one in my language. – Angelo Fuchs – 2015-11-26T08:33:39.733

1Also, this doesn't work for dates prior to 1900 (at least in my copy of Excel) – Angelo Fuchs – 2015-11-26T08:35:52.347

Also, you saved 13 bytes on TEXT from =TEXT(DATE(A1,11,MOD(12-WEEKDAY(DATE(A1,11,1)),7)+22),"d mmm") - the output is an integer without formatting. – user3819867 – 2015-11-26T08:41:25.857

@user3819867, that's how excel stores its dates. formatting is left up to the user – SeanC – 2015-11-26T15:42:43.100

@AngeloFuchs, DATE returns a date value, using year, month, day as parameters. MOD returns the remainder part of a division (modulus). WEEKDAY returns the day of week of a date (1=Sunday, 2=Monday, etc). – SeanC – 2015-11-27T16:05:52.453

2

Befunge, 41 Bytes

47*&:::4"d"*/\"d"/-\4/++5+7%-" voN",,,,.@

Run on this interpreter.

Explanation: A common year is 365 = 1 mod 7 days, so the year plus every 4th year, minus every 100th (d in ascii) year, plus every 400th years accounts for any leap days (including the present year). The result of :::4"d"*/\"d"/-\4/++ can then be thought of as March 5th, the first day after February to fall on the same day as the first day of the year in common years. After that we calibrate to the pattern with 5+7%- subtracting a number of days of the week from the 28th(the 47*stored earlier) of November. Then print.

A version correcting for B.C. years is currently longer than the bonus provides for, at 59 -25% = 44.25 bytes:

47*&v
!`0:<+*"(F"_v#
" voN",,,,.@>:::4"d"*/\"d"/-\4/++5+7%-

Linus

Posted 2015-11-25T18:01:54.620

Reputation: 1 948

2

Matlab, 78 bytes

x=find(all(bsxfun(@eq,datestr(datenum(input(''),11,1:30),'ddd'),'Thu')'));x(4)

Luis Mendo

Posted 2015-11-25T18:01:54.620

Reputation: 87 464

2

Ruby, 60 58 42.75 39 * 0.75 = 29.25 bytes

->y{puts"Nov #{28-Time.new(y,9).wday}"}

42.75 bytes

->y{puts"Nov #{[5,4,3,2,1,7,6][Time.new(y,11).wday]+21}"}

58 bytes

->y{puts "Nov #{[5,4,3,2,1,7,6][Time.new(y,11).wday]+21}"}

60 bytes

->y{puts "Nov #{[5,4,3,2,1,7,6][Time.new(y,11,1).wday]+21}"}

Ungolfed:

-> y {
  puts "Nov #{[5,4,3,2,1,7,6][Time.new(y,11).wday]+21}"
}

Tests:

->y{puts"Nov #{28-Time.new(y,9).wday}"}.(2015)
Nov 26

->y{puts"Nov #{28-Time.new(y,9).wday}"}.(1917)
Nov 22

->y{puts"Nov #{28-Time.new(y,9).wday}"}.(-480)
Nov 25

Vasu Adari

Posted 2015-11-25T18:01:54.620

Reputation: 941

Or using the September 1st trick: "Nov #{28-Time.new(y,9).wday}" 30 bytes – Josh – 2019-12-17T23:18:46.133

@josh That's brilliant. Updating it :) – Vasu Adari – 2019-12-25T18:45:25.333

0

Smalltalk - Squeak and Pharo, 69 57 54 49 35 34 33 32 - 25% = 24 bytes

"Nov nn": 69B 49B (-25% = 36.75B)
11nn (anInt): 57 54 32B (-25% = 24B)

"Year supplied as an integer value, y  
 Result provided as an integer value, 11nn
 Methods Date>>y:m:d: and >>w created as duplicates 
 of year:month:day and weekdayIndex"
(12-(Date y:y m:11d:1)w)\\7+1122

Previous versions

11nn output

"Year supplied as an integer value, y  
 Result provided as an integer value, 11nn"
(12-(Date year:y month:11 day:1) weekdayIndex)\\7+1122

"Year supplied as an integer value, y  
 Result provided as an integer value, d, prefixed by 11"
d:=(12-(Date year:y month:11 day:1) weekdayIndex)\\7+1122

Nov nn output

"Year supplied as an integer value, y  Result provided as a string, 'Nov nn'"
'Nov ', (12-(Date year:y month:11 day:1) weekdayIndex\\7+22) asString

nn output

"Year supplied as an integer value, y  Result provided as an integer value, d"
d:=(12-(Date year:y month:11 day:1) weekdayIndex)\\7+22

n.b. This is provided as a program - specifically a Workspace expression - rather than as a method.

It assumes the input is a given (as per the phrasing "given an up-to-four-digit year".
Including the declaring and giving of the input would add another 11 characters:

|y|y:=2015.(12-(Date y:y m:11 d:1)w)\\7+1122

Thanks to eMBee, for showing how to remove yet one more char - the whitespace immediately before the 'w'.

Actually, that inspired me to try removing the whitespace before 'd:' :
|y|y:=2015.(12-(Date y:y m:11d:1)w)\\7+1122
Which worked!

Euan M

Posted 2015-11-25T18:01:54.620

Reputation: 151

As per the OP's clarification, the day number version is not valid. – Mego – 2015-11-26T04:23:06.180

I agree - but given there are quite a few day-number-only versions in the list, I put it there for an apples-to-apples comparison – Euan M – 2015-11-26T06:20:55.093

0

PHP 5.3+, 59 bytes

This uses PHP builtin function strtotime to parse the date.

<?=gmdate('M d',strtotime("Next Thursday $_GET[Y]-11-19"));

This expects the value to be passed over GET parameter Y OR over php-cli Y=<year>.

It tries to find the next thursday after 19th November.
So far, with the tests I've made, it works fine.

Be aware that 2-digit years may be interpreted differently.

I use gmdate to avoid timezone issues, but it works equally well using date (at least, where I live).

Ismael Miguel

Posted 2015-11-25T18:01:54.620

Reputation: 6 797

1

An answer is not an appropriate place to discuss the validity of an input method. I've created this option in the default I/O meta post (so the community can vote on it) and moved your conversation to chat. (CC @Mego)

– Dennis – 2015-11-27T16:36:41.713

0

Pike, 87 91 107 77 76 bytes - 25% = 57

string t(int y){return"Nov "+((11-Calendar.Day(y,11,1)->week_day())%7+22);}

leaving the old one in for comparison because i find it more clever in terms of taking advantage of the Calendar module, but the above is way shorter.

string t(int y){object n=Calendar.Month(y,11);return"Nov "+(n->weeks()->day(4)&n->days())[3]->month_day();}

eMBee

Posted 2015-11-25T18:01:54.620

Reputation: 101

This doesn't seem to take input. – Peter Taylor – 2015-11-26T08:16:55.110

yeah, i got confused by that, some solutions have a function definition and some just use a variable that is not defined anywhere, so i am not sure what i am supposed to use for the bytecount. – eMBee – 2015-11-26T09:27:06.517

I think that the answers which you see as using "a variable that is not defined anywhere" are actually using a very short function declaration syntax. There are one or two answers which use esolangs which have special treatment for stdin and so favour writing full programs. I don't know Pike, but if you can remove whitespace and abbreviate the function name to get string t(int y){object n=Calendar.Month(y,11);return"Nov "+(n->weeks()->day(4)&n->days())[3]->month_day();} then I count that as 107 bytes. – Peter Taylor – 2015-11-26T09:56:06.037

i thought i had seen a few that weren't function declarations, but they appear to have been fixed, only the smalltalk example doesn't fit. if that is valid i could shave another 17 bytes of this one... – eMBee – 2015-11-26T16:34:34.553

The Smalltalk version is written as a program - specifically a Workspace expression. We were told we are given an input, rather than soliciting input. – Euan M – 2015-11-27T03:37:05.047

@euan-m: well, it also says we are supposed to create a program or function. in smalltalk i would make it a block: [:y|...] that defines y as the input variable and only adds 5 bytes; btw: you can remove one more space from ) w -> )w ... – eMBee – 2015-11-27T04:18:48.847

@euan-m: you also need to re-add the 'Nov ', ... asString – eMBee – 2015-11-27T04:23:06.600

It's a program - a short one. the Nov doesn't need re-added - it's now output as an integer 11nn. The rules got explicitly amended to allow the winning entry. – Euan M – 2015-11-27T04:53:41.523

oh,. that's why you add 1122, sorry, now i get it. – eMBee – 2015-11-27T06:50:09.370

returning an integer is, uhm, interesting. no complaints, it fits the task. but if i wanted something actually practical i'd have gone for {11.(...)}, just slightly longer... – eMBee – 2015-11-27T06:57:20.310

@eMBee I know nothing about Pike... but I'd think the space after the return is unnecessary. – csga5000 – 2015-12-03T05:29:56.487

good catch, indeed it works without. thanks! – eMBee – 2015-12-04T19:21:55.063

0

VBA, 124 bytes * 75% = 93 bytes

Function e(y)
For i = 1 To 7
x = DateSerial(y, 11, 21) + i
If Weekday(x) = 5 Then e = Format(x, "mmm dd")
Next
End Function

I'm not sure if spaces count, it's somewhere between 102 and 124, feel free to edit.
If integers are a valid output I'm more than happy to remove the Format part.

user3819867

Posted 2015-11-25T18:01:54.620

Reputation: 439

Spaces count; if they're unnecessary to the program you can remove them though. – lirtosiast – 2015-11-26T21:06:27.910

0

T-SQL 215 bytes 248 * 0.75 = 186 bytes

create function t(@y int)returns table return select x=convert(char(6),x,107)from(select x=cast(dateadd(dd,d,dateadd(yy,@y-2015+7600,'20151101'))as date)from(select d=21+n from(values(0),(1),(2),(3),(4),(5),(6))b(n))c)t where datepart(weekday,x)=5

Ungolfed

set nocount on;
if object_id('dbo.t') is not null drop function dbo.t
go

create function t(@y int)returns table return
    select x=convert(char(6),x,107)
    from (
        select
        x=cast(dateadd(dd,d,dateadd(yy,@y-2015+7600,'20151101'))as date)
        from (
            select d=21+n 
            from (values (0),(1),(2),(3),(4),(5),(6))b(n)
        )c
    )t
    where datepart(weekday,x)=5
go

which with this test scaffold

select 
  [ 2015 = Nov. 26]=(select x from dbo.t( 2015))
 ,[ 1917 = Nov. 22]=(select x from dbo.t( 1917))
 ,[ 1752 = Nov. 23]=(select x from dbo.t( 1752))
 ,[ 1582 = Nov. 25]=(select x from dbo.t( 1582))
 ,[ 1400 = Nov. 27]=(select x from dbo.t( 1400))
 ,[ -480 = Nov. 25]=(select x from dbo.t( -480))
 ,[-5480 = Nov. 28]=(select x from dbo.t(-5480))
;
go

yields as desired

2015 = Nov. 26  1917 = Nov. 22  1752 = Nov. 23  1582 = Nov. 25  1400 = Nov. 27  -480 = Nov. 25 -5480 = Nov. 28
--------------- --------------- --------------- --------------- --------------- --------------- ---------------
Nov 26          Nov 22          Nov 23          Nov 25          Nov 27          Nov 25          Nov 28

Pieter Geerkens

Posted 2015-11-25T18:01:54.620

Reputation: 131

0

GNU coreutils, 35 bytes

seq -f$1-11-2%g 2 8|date -f-|grep h

Simply searches the week 22-28 November for a Thursday. Run it in a C or POSIX locale, as I depend on Thursday being the only day abbreviation to contain a 'h'.


Here's a cleverer answer, albeit a byte longer:

echo Nov $((28-`date -d$1-5-5 +%w`))

We retrieve the day-of-week number of a date in a fairly arbitrary week between March and September (so day and month are one digit each, and we're not affected by a possible leap day). We pick the day so that it's a Sunday (%w==0) when Thanksgiving is on the 28th. We can then subtract that value from 28 to obtain the appropriate Thursday.

Toby Speight

Posted 2015-11-25T18:01:54.620

Reputation: 5 058

2This works unless Sean Connery runs it. – jqblz – 2015-12-17T21:23:26.283

0

TSQL, 53 52 bytes

DECLARE @ CHAR(4)=2000

PRINT DATEADD(d,59,DATEDIFF(d,1,DATEADD(m,9,@))/7*7)

Fiddle

Calculating monday before or on september 2nd and adding 59 days

(better than calculating monday before or on october 4th and adding 31 days because october is the 10th month thus saving 1 byte)

t-clausen.dk

Posted 2015-11-25T18:01:54.620

Reputation: 2 874

-1

Perl, 92 bytes

use Time::Local;(localtime(timelocal 0,0,0,$_,10,$ARGV[0]))[6]==4?print "Nov $_":0for 22..28

EDIT : Fixed output format, fixed bug e.g. 2012 result, used shorter "for" format. Thanks to msh210.

steve

Posted 2015-11-25T18:01:54.620

Reputation: 2 276

This is shorter and does the same thing: use Time::Local;(localtime(timelocal 0,0,0,$_,10,$ARGV[0]))[6]==4?print:0for 22..29. But both scripts suffer from the same flaws: (1) they don't print a representation of the month as required; (2) they have the wrong output for e.g. 2012. – msh210 – 2015-11-26T07:53:25.317