g | x | w | all
Bytes Lang Time Link
675Japt R241003T133202ZShaggy
nanGo241003T165201Zbigyihsu
nanPerl 5 pMDateManip241003T152836ZXcali
nanRuby151127T140236ZVasu Ada
052TSQL160707T114215Zt-clause
045PowerShell151125T203558ZAdmBorkB
035GNU coreutils151217T192338ZToby Spe
nanPHP151125T195435Zuser4594
nanSmalltalk Squeak and Pharo151126T003033ZEuan M
nanPike151126T074845ZeMBee
nanJapt151125T185833ZETHprodu
186TSQL 248 * 0.75 =151129T025606ZPieter G
044Vitsy151126T003416ZAddison
nanExcel151125T214031ZEuan M
092Perl151125T215641Zsteve
435JavaScript ES6151125T200336Znicael
nanPython151125T215637ZLuke
nanPyth151126T030927ZETHprodu
078Matlab151126T223948ZLuis Men
nanTIBASIC151126T174032Zlirtosia
nanJavaScript ES6151125T204435Zintrepid
nanTeaScript151125T224104Zintrepid
296TSQL151126T195120ZPeter Va
078Python151125T185941ZRod
059PHP 5.3+151126T030851ZIsmael M
041Befunge151126T090818ZLinus
nanVBA151126T084755Zuser3819
nanGolfscript151126T025955Zxsot
155Jython151125T214743Zuser4762
6448Excel151125T213315ZSeanC
038Mathematica151125T183937ZLegionMa

Japt -R, 10 9 - 25% = 7.5 6.75 bytes

Outputs as mmdd.

#p8aÐU8 e

Try it

#p8aÐU8 e     :Implicit input of integer U
#p8           :1128
   a          :Absolute difference with
    Ð         :  Create Date object with
     U        :    Year U, and
      8       :    0-based month 8
        e     :  Get 0-indexed day of the week (0=Sunday)

Go, 105 - 25% = 78.75 bytes

import(."time";."fmt")
func f(y int)string{return Sprintf("Nov %d",28-Date(y,9,1,0,0,0,0,UTC).Weekday())}

Attempt This Online!

Uses the September-1st trick to calculate the day of the week.

Perl 5 -pMDate::Manip, 32 + 14 = 46 bytes

$_=ParseDate("4th thu of nov$_")

Try it online!

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

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)

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.

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.

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

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!

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

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

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

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.

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.

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.

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

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.

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.

Matlab, 78 bytes

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

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.

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.

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.

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

Python, 83 81 78 bytes

from datetime import*
lambda a:'Nov '+`((10-datetime(a,11,1).weekday())%7)+22`

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

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%-

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.

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@-

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.

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)

Mathematica, 59 38 bytes

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