38
\$\begingroup\$

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>

\$\endgroup\$
  • 25
    \$\begingroup\$ For anyone who is wondering when Thanksgiving will be coming up this year: Thanksgiving will be tomorrow . \$\endgroup\$ – TheNumberOne Nov 25 '15 at 18:07
  • 6
    \$\begingroup\$ But I had thanksgiving on October 10th? I'm sorry to say your question is late. \$\endgroup\$ – JimmyJazzx Nov 25 '15 at 18:17
  • 5
    \$\begingroup\$ @Downgoat Use whichever byte counter you want. That's just the one I use personally and would recommend. \$\endgroup\$ – bkul Nov 25 '15 at 18:44
  • 22
    \$\begingroup\$ "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. ;-) \$\endgroup\$ – Adrian McCarthy Nov 25 '15 at 19:26
  • 7
    \$\begingroup\$ For an answer that does the bonus, should there be a year 0 between -1 and 1 or not? \$\endgroup\$ – feersum Nov 25 '15 at 21:07

28 Answers 28

40
\$\begingroup\$

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.

\$\endgroup\$
  • 5
    \$\begingroup\$ Holy crap. xD I never would've thought this would have worked like this, +1. \$\endgroup\$ – Addison Crump Nov 26 '15 at 17:42
  • \$\begingroup\$ Good idea to add 10000. Nice. \$\endgroup\$ – agtoever Nov 27 '15 at 6:16
  • \$\begingroup\$ I count 23 Letters. How can this be 15 Bytes ? \$\endgroup\$ – Stephan Schinkel Dec 18 '15 at 9:41
  • 1
    \$\begingroup\$ @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. \$\endgroup\$ – FryAmTheEggman Dec 18 '15 at 15:00
  • \$\begingroup\$ You tested it on a calculator? wow. \$\endgroup\$ – user54200 Jul 7 '16 at 11:45
23
\$\begingroup\$

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

\$\endgroup\$
  • 2
    \$\begingroup\$ This is funnier than Mathematica. \$\endgroup\$ – lirtosiast Nov 25 '15 at 21:31
  • 2
    \$\begingroup\$ @Tryth Even shorter: "4thuXI". You can even drop the space between the year "4thuXI2015". \$\endgroup\$ – Alexander O'Mara Nov 26 '15 at 1:04
  • 1
    \$\begingroup\$ Using the command line option -F, input could be shortened to "4thuXI$argn". \$\endgroup\$ – primo Nov 26 '15 at 8:01
  • 3
    \$\begingroup\$ Wait, this actually works? That's...that's just...I don't even... \$\endgroup\$ – ETHproductions Nov 26 '15 at 15:23
  • 1
    \$\begingroup\$ Works for me. While I'm at it, Md needs no quotes, with E_NOTICE disabled. \$\endgroup\$ – primo Nov 27 '15 at 6:20
18
\$\begingroup\$

Mathematica, 59 38 bytes

DayRange[{#,11},{#,12},Thursday][[4]]&
\$\endgroup\$
  • \$\begingroup\$ +1 Clever. There is also WolframAlpha["Thanksgiving " <> #] & where the date is entered as a string. \$\endgroup\$ – DavidC Nov 25 '15 at 18:53
  • \$\begingroup\$ 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. \$\endgroup\$ – DavidC Nov 25 '15 at 19:09
  • \$\begingroup\$ @DavidCarraher However, the OP states that the program has to work for at least 0 - 9999 C.E. \$\endgroup\$ – LegionMammal978 Nov 25 '15 at 19:15
17
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ Nice solution, I love how short it is and that it's still more understandable than most of entries. \$\endgroup\$ – Marko Grešak Nov 28 '15 at 19:22
16
\$\begingroup\$

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

\$\endgroup\$
  • \$\begingroup\$ Very nicely done. \$\endgroup\$ – bkul Nov 25 '15 at 22:26
  • \$\begingroup\$ This is now longer than vanilla JavaScript. Thomas Kwa has a much shorter formula. \$\endgroup\$ – intrepidcoder Nov 26 '15 at 21:43
  • \$\begingroup\$ @intrepidcoder Thanks, fixed. \$\endgroup\$ – ETHproductions Nov 28 '15 at 16:07
12
\$\begingroup\$

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.

\$\endgroup\$
  • 2
    \$\begingroup\$ No. but you get a +1 from me ;) \$\endgroup\$ – Conor O'Brien Nov 26 '15 at 1:05
  • \$\begingroup\$ It says that Thanksgiving in 2016 is Nov 28. It is really Nov 24. \$\endgroup\$ – Hosch250 Nov 28 '15 at 17:08
  • \$\begingroup\$ @Hosch250 You're right - fixed now. \$\endgroup\$ – Addison Crump Nov 28 '15 at 17:56
10
\$\begingroup\$

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.

\$\endgroup\$
  • 1
    \$\begingroup\$ You can drop f=. \$\endgroup\$ – feersum Nov 25 '15 at 22:51
  • 1
    \$\begingroup\$ How does this work for negative dates? Does modulo in Python apply to negatives? :O \$\endgroup\$ – Addison Crump Nov 26 '15 at 0:58
  • \$\begingroup\$ It should be shorter to use string reps, "Nov "+`...` \$\endgroup\$ – xnor Nov 26 '15 at 1:00
  • \$\begingroup\$ @VoteToClose Python modulo gives an answer with the same sign as the second argument. Thus -40%7==2 but -40%-7==-5 \$\endgroup\$ – quintopia Nov 26 '15 at 5:39
  • \$\begingroup\$ @quintopia Wait, hold on, I'm an idiot. Okay. That makes sense. \$\endgroup\$ – Addison Crump Nov 26 '15 at 11:14
10
\$\begingroup\$

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

\$\endgroup\$
  • 2
    \$\begingroup\$ Pardon 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? \$\endgroup\$ – ETHproductions Nov 25 '15 at 22:05
  • \$\begingroup\$ BTW, it tells me the wrong year for any input from 0 to 99. Not sure if this matters, though. \$\endgroup\$ – ETHproductions Nov 25 '15 at 22:08
  • \$\begingroup\$ @Eth LOL of course you're right, didn't think that's always 4+18 :D \$\endgroup\$ – nicael Nov 25 '15 at 22:11
  • \$\begingroup\$ @Eth hm, 0 to 99 is indeed wrong, let my try fixing that. \$\endgroup\$ – nicael Nov 25 '15 at 22:13
  • \$\begingroup\$ @Eth It looks like 0-100 give the same date that 1900-2000, judging from the other answers. \$\endgroup\$ – nicael Nov 25 '15 at 22:18
8
\$\begingroup\$

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.
\$\endgroup\$
  • \$\begingroup\$ It's 35 chars, but 36 bytes due to ¡ :) \$\endgroup\$ – nicael Nov 25 '15 at 22:42
  • \$\begingroup\$ @nicael Not by this site, it's not ;) \$\endgroup\$ – ETHproductions Nov 25 '15 at 22:43
  • \$\begingroup\$ Hm, getting 36 with mothereff.in/…. \$\endgroup\$ – nicael Nov 25 '15 at 22:44
  • \$\begingroup\$ @Downgoat usually uses this format, in which all chars up to U+00FF are 1 byte. \$\endgroup\$ – ETHproductions Nov 25 '15 at 22:45
5
\$\begingroup\$

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.

\$\endgroup\$
5
\$\begingroup\$

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)
\$\endgroup\$
  • 1
    \$\begingroup\$ Just 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. \$\endgroup\$ – Kritixi Lithos Nov 25 '15 at 19:38
  • 2
    \$\begingroup\$ You could also try pandas. import pandas;print'Nov '+`((10-pandas.datetime(input(),11,1).weekday())%7)+22` \$\endgroup\$ – Willem Nov 25 '15 at 19:39
  • \$\begingroup\$ @ΚριτικσιΛίθος but then datetime.datetime would only be d.datetime, wouldn't it? \$\endgroup\$ – TanMath Nov 25 '15 at 19:50
  • 2
    \$\begingroup\$ from datetime import* is even a bit shorter, as you won't need the d. any more \$\endgroup\$ – FryAmTheEggman Nov 25 '15 at 22:00
5
\$\begingroup\$

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.

\$\endgroup\$
  • 1
    \$\begingroup\$ When I try this with dates prior to 1900 it doesn't work in my copy of excel. \$\endgroup\$ – Angelo Fuchs Nov 26 '15 at 8:37
  • \$\begingroup\$ The long version doesn't work as well. \$\endgroup\$ – Angelo Fuchs Nov 26 '15 at 8:49
  • \$\begingroup\$ For old dates, use Excel User's pre-1900 dates workbook. <exceluser.com/downloads/examples/post_900_123/index.html> \$\endgroup\$ – Euan M Nov 27 '15 at 0:42
  • \$\begingroup\$ 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. \$\endgroup\$ – lirtosiast Nov 27 '15 at 1:59
  • \$\begingroup\$ @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. \$\endgroup\$ – Euan M Nov 27 '15 at 2:16
4
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ "{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 \$\endgroup\$ – n0rd Nov 25 '15 at 22:51
  • \$\begingroup\$ @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! \$\endgroup\$ – AdmBorkBork Nov 27 '15 at 16:17
  • \$\begingroup\$ @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. \$\endgroup\$ – AdmBorkBork Dec 16 '15 at 14:52
  • 1
    \$\begingroup\$ @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. \$\endgroup\$ – AdmBorkBork Dec 16 '15 at 17:12
  • \$\begingroup\$ Right, right. Double padding it is. \$\endgroup\$ – TessellatingHeckler Dec 16 '15 at 17:15
3
\$\begingroup\$

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@-
\$\endgroup\$
3
\$\begingroup\$

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
\$\endgroup\$
  • \$\begingroup\$ Is this really the shortest way? \$\endgroup\$ – lirtosiast Nov 26 '15 at 21:05
  • \$\begingroup\$ 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? \$\endgroup\$ – Peter Vandivier Nov 26 '15 at 21:12
  • \$\begingroup\$ 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. \$\endgroup\$ – lirtosiast Nov 26 '15 at 21:18
  • \$\begingroup\$ not strictly a pure-math solution, but reading that answer definitely gave me some ideas to golf out 200 characters. Thanks!! \$\endgroup\$ – Peter Vandivier Nov 26 '15 at 21:42
  • \$\begingroup\$ (I meant (28 - ( x - 2 + floor(x / 4) - floor(x / 100) + floor(x / 400) ) % 7) Yeah, reading other answers tends to help a lot. \$\endgroup\$ – lirtosiast Nov 26 '15 at 21:45
3
\$\begingroup\$

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.

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

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)
\$\endgroup\$
  • \$\begingroup\$ 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. \$\endgroup\$ – Angelo Fuchs Nov 26 '15 at 8:33
  • 1
    \$\begingroup\$ Also, this doesn't work for dates prior to 1900 (at least in my copy of Excel) \$\endgroup\$ – Angelo Fuchs Nov 26 '15 at 8:35
  • \$\begingroup\$ 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. \$\endgroup\$ – user3819867 Nov 26 '15 at 8:41
  • \$\begingroup\$ @user3819867, that's how excel stores its dates. formatting is left up to the user \$\endgroup\$ – SeanC Nov 26 '15 at 15:42
  • \$\begingroup\$ @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). \$\endgroup\$ – SeanC Nov 27 '15 at 16:05
2
\$\begingroup\$

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%-
\$\endgroup\$
2
\$\begingroup\$

Matlab, 78 bytes

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

Ruby, 60 58 57 * 0.75 = 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}"
}

Usage:

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

=> Nov 26
\$\endgroup\$
0
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ Spaces count; if they're unnecessary to the program you can remove them though. \$\endgroup\$ – lirtosiast Nov 26 '15 at 21:06
0
\$\begingroup\$

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

\$\endgroup\$
  • 1
    \$\begingroup\$ 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) \$\endgroup\$ – Dennis Nov 27 '15 at 16:36
0
\$\begingroup\$

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
\$\endgroup\$
0
\$\begingroup\$

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();}
\$\endgroup\$
  • \$\begingroup\$ This doesn't seem to take input. \$\endgroup\$ – Peter Taylor Nov 26 '15 at 8:16
  • \$\begingroup\$ 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. \$\endgroup\$ – eMBee Nov 26 '15 at 9:27
  • \$\begingroup\$ 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. \$\endgroup\$ – Peter Taylor Nov 26 '15 at 9:56
  • \$\begingroup\$ 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... \$\endgroup\$ – eMBee Nov 26 '15 at 16:34
  • \$\begingroup\$ The Smalltalk version is written as a program - specifically a Workspace expression. We were told we are given an input, rather than soliciting input. \$\endgroup\$ – Euan M Nov 27 '15 at 3:37
0
\$\begingroup\$

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!

\$\endgroup\$
  • \$\begingroup\$ As per the OP's clarification, the day number version is not valid. \$\endgroup\$ – Mego Nov 26 '15 at 4:23
  • \$\begingroup\$ 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 \$\endgroup\$ – Euan M Nov 26 '15 at 6:20
0
\$\begingroup\$

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.

\$\endgroup\$
  • 2
    \$\begingroup\$ This works unless Sean Connery runs it. \$\endgroup\$ – bkul Dec 17 '15 at 21:23
0
\$\begingroup\$

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)

\$\endgroup\$
-1
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ 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. \$\endgroup\$ – msh210 Nov 26 '15 at 7:53

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