18
\$\begingroup\$

Australians love public holidays, and drinking. Yesterday, the 26th January, was Australia day, which is a public holiday. I was glad to not be at work yesterday, and eager to know the next time I get a public holiday! Unfortunately, I had a bit too much to drink, and I'm not able to work it out for myself.

Write a program that will take a date in Australian date/time notation (dd/mm) as input, and output the amount of days until the next public holiday. Because I'm a Queensland (QLD) resident, I'm only interested in public holidays that affect Queenslanders:

25/03 | Good Friday
26/03 | Easter Saturday
28/03 | Easter Monday
25/04 | Anzac Day
02/05 | Labour Day
03/10 | Queen's Birthday
25/12 | Christmas Day
26/12 | Boxing Day
27/12 | Christmas Day holiday

Note the following from the site:

Christmas Day holiday

An additional public holiday to be added when New Year's Day, Christmas Day or Boxing Day falls on a weekend.

Because Christmas day is on Sunday, there is an extra public holiday. Christmas day is still a public holiday.

Because I'm a morning person, you should include the current date as a day (as that's the most likely time I will check your program for the next public holiday). That is, if a public holiday's date is entered, your output should be 0; if the day before a public holiday is entered, your output will be 1.

I'm only interested in dates between now (the 27/01) until the end of the year. The final date you will need to account for is 31/12 where your output will be 1 (for New Year's day).

Standard loopholes are forbidden.

Input

  • Input will always be 5 characters: 4 letters, separated with a hyphen - or slash /
  • Input will only be a date between 27/01 and 31/12

Output

  • The number of days until the next public holiday in Queensland Australia, inclusive of the input date: should be a number between 0 and 153 (the longest gap)
  • No new lines or errors

Examples

01-05 = 1  
02-05 = 0  
03-05 = 153  
25/12 = 0
26-12 = 0
27/12 = 0
30/12 = 2
31-12 = 1

Hopefully this is clear and nothing is missed; however, this is my second question so I will appreciate any feedback and do my best to fix problems ASAP.

\$\endgroup\$
  • \$\begingroup\$ @insertusernamehere Thanks for the great suggestion! I've added the dates in to the question \$\endgroup\$ – Tas Jan 26 '16 at 21:56
  • \$\begingroup\$ @Tas are you sure those dates are correct? The ones in the examples don't match the quote and both of them don't match the website. \$\endgroup\$ – Adam Martin Jan 26 '16 at 22:47
  • \$\begingroup\$ @AdamMartin Thanks for pointing that out. I had incorrectly put in the December dates. The ones in the example are just any dates, not specific to public holidays. They are just examples of dates that could be entered, and what the output should be. The quoted ones should (and hopefully do) match the ones from the website. \$\endgroup\$ – Tas Jan 26 '16 at 22:50
  • \$\begingroup\$ You celebrate the Queen's birthday in October in Queensland? That is so weird, but seems correct from the link. \$\endgroup\$ – Level River St Jan 26 '16 at 22:52
  • \$\begingroup\$ Wow, you guys don't have any holidays from June straight through September? That's rough. \$\endgroup\$ – Joe Z. Jan 27 '16 at 19:21

10 Answers 10

2
\$\begingroup\$

Pyth, 98 84 62 67 Bytes

Update: Saved 14 Bytes by shortening the list of the day count for all 12 months for the day number calculation. Haven't found a good way to compress the other list tho, still trying!

Update2: Saved another 22 bytes by endcoding the list of the day-numbers as a base256 string.

J30KhJ=Yc:z"\W"dd=N+s<[KtJKJKJKKJKJK)tseYshY-hfgTNCMc"UVXt{ĕŨũŪů"1N

Try it online!

Same algorithm as in my Python answer. And no builtin to get the day of the year, so I had to do it myself. Creating those 2 lists for the day-of-the-year calculation and for the days of the holidays is quite costly...gonna have a look at it again and try to generate them in fewer bytes.

\$\endgroup\$
  • \$\begingroup\$ It doesn't seem to like input separated with a hyphen, but otherwise is great \$\endgroup\$ – Tas Feb 1 '16 at 2:05
  • \$\begingroup\$ @Tas Thanks for the hint, completly overread that part...Fixed it at the cost of 5 more bytes. Maybe you should add some hyphens to the testcases, since you want them to cover every possible input variation. \$\endgroup\$ – Denker Feb 1 '16 at 8:58
5
\$\begingroup\$

Visual Basic for Applications, 155 or 118 bytes

Version 1 - locale-independent, 155 bytes

Function h(d)
For i=0To 9
h=Array(0,1,3,31,38,192,275,276,277,282)(i)+42454-DateSerial(16,Val(Right(d,2)),Val(Left(d,2)))
If h>=0Goto 9
Next
9 End Function

Version 2 - locale-dependent, 118 bytes

Function h(d)
For i=0To 9
h=Array(0,1,3,31,38,192,275,276,277,282)(i)+42454-CDate(d)
If h>=0Goto 9
Next
9 End Function

Byte count is for final .BAS file, including linefeed characters. Edited outside standard VBA editor (as it imposes additional spaces and verbose forms of some keywords) - but imports and runs smoothly on any Office Application (to test type e.g. ? h("10/08") in immediate window, or in Excel use directly in a cell formula).

(EDITED) Initially I chose to use DateSerial to make the function locale-safe (version 1). As I live in Brazil and thus my system is configured to use "dd/mm/yy" format for dates (just like Australia) I could write a even smaller version by using CDate instead (version 2). CDate uses system locale information to convert text into date. I also assumed in this version that the code would only be run during 2016 (if year is omitted (-6 bytes) CDate assumes current year as per system clock).

The number 42454 on third line is the sum of 42450 that is the numeric representation of 01/01/2016 on VBA, and 84 that is the day-of-the-year for the first holiday. The Array contains day-of-the-year for each holiday (including 01/01/2017) offset by -84 as this takes some digits away. Using 16 instead of 2016 on DateSerial takes away two more bytes.

Creating an identical array nine times inside the iteration is "bad" code, but works and saves 3 more bytes (one for array name and one for equal sign outside loop, and one more to reference the array inside the loop).

The "missing" spaces between 0 and the following keyword on second and fourth lines are not necessary as they are reintroduced automatically by VBE when the module is imported. Used outdated but byte-cheap If <...> Goto <linenumber> to break from loop (both If <...> Then Exit For and If <...> Then Exit Function use more characters).

Also took advantage of the fact that the function name in VBA behaves as a local variable, and its value is automaticaly returned by the function in the end of execution.

\$\endgroup\$
  • \$\begingroup\$ Welcome to PPCG! Here we define a programming language by the interpreter, so it's perfectly acceptable to require a certain locale. \$\endgroup\$ – lirtosiast Jan 27 '16 at 17:37
  • \$\begingroup\$ Thanks! Edited to add a smaller locale-dependent version. \$\endgroup\$ – dnep Jan 27 '16 at 18:04
4
\$\begingroup\$

JavaScript (ES6), 131 128 bytes

d=>[56,57,59,87,94,248,331,332,333,338].map(n=>r=r||(q=1454e9+n*864e5-new Date(d[3]+d[4]+`/${d[0]+d[1]}/16`))>=0&&q/864e5,r=0)|r

Explanation

Uses the JavaScript built-in Date constructor to convert the input string into a number of milliseconds since epoch, then compares this with the number of milliseconds for each public holiday.

It does this by storing the public holidays in an array as the number of days since a reference date. I chose 2016-01-29 for the reference date because the number of milliseconds since epoch can be condensed the shortest for this date. Any number of milliseconds between this day and the next works because the result is rounded down, and keeping the number in the middle avoids daylight saving effects (although the OP's timezone does not have daylight savings). The number of this day is 1453986000000 and rounding it up to 1454000000000 (adding a couple of hours) means it can be written as 1454e9.

d=>
  [56,57,59,87,94,248,331,332,333,338]             // list of day offsets from 01/29
  .map(n=>                                         // for each public holiday offset n
    r=r||                                          // if r is already set, do nothing
      (q=                                          // q = approximate difference in ms
        1454e9+n*864e5                             // time of public holiday
        -new Date(d[3]+d[4]+`/${d[0]+d[1]}/16`)    // time of input date
      )
      >=0&&                                        // if q >= 0
        q/864e5,                                   // r = q in days
    r=0                                            // r = result
  )
  |r                                               // floor and return r

Test

This solution is dependent on the user's timezone. This works in the OP's (and my) time zone (GMT +1000). If you want to test it in a different time zone, adding numberOfHoursDifferentFromGMT1000 * 60 * 60 * 1000 to the reference date number should work. (eg. GMT +0430 would be -5.5 * 60 * 60 * 1000 + 1454e9+n*864e5)

var solution = d=>[56,57,59,87,94,248,331,332,333,338].map(n=>r=r||(q=1454e9+n*864e5-new Date(d[3]+d[4]+`/${d[0]+d[1]}/16`))>=0&&q/864e5,r=0)|r
<input type="text" id="input" value="03/05" />
<button onclick="result.textContent=solution(input.value)">Go</button>
<pre id="result"></pre>

\$\endgroup\$
  • \$\begingroup\$ This gives always 0 when the date is seperated by a hyphen. I already asked the OP to adjust his testcases, since they are all with a slash as seperator. \$\endgroup\$ – Denker Feb 1 '16 at 12:16
  • \$\begingroup\$ @DenkerAffe Oh, I thought he meant we were free to choose a separator. Incidentally, making it separator-ambivalent saved me 3 bytes, so thanks! \$\endgroup\$ – user81655 Feb 1 '16 at 12:26
  • 1
    \$\begingroup\$ Seems like the rule separated with a hyphen - or slash / is a bit ambigious. For me it means that we have to deal with both, but I can definetly see your side. Guess the OP should clarify this. \$\endgroup\$ – Denker Feb 1 '16 at 12:33
3
\$\begingroup\$

T-SQL, 210, 206, 194 Bytes

(First post here, Hope this is ok, but please be nice :)

Input goes into @i, caters for both / and - as the separator. I'm in Australia, so my date format is the same as @Tas

DECLARE @i CHAR(5)='23-09';DECLARE @c INT=DATEPART(dy,CAST(REPLACE(@i,'-','/')+'/2016' AS DATE))-1;SELECT MIN(b)-@c FROM(VALUES(84),(85),(87),(115),(122),(276),(359),(360),(361))a(b)WHERE b>=@c;

Update varchar to char saves 3 bytes plus removed a space :)

Update 2 declare @c and assign without a select

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

T-SQL, 296 bytes

Created as a table valued function

create function d(@ char(5))returns table return select min(o)o from(select datediff(day,cast('2016'+right(@,2)+left(@,2)as date),cast(right('2016'+right('0'+cast(d as varchar(4)),4),8)as datetime)+1)o from(values(324),(325),(327),(424),(501),(1002),(1224),(1225),(1226),(1231))d(d))d where 0<=o

Used in the following manner

SELECT *
FROM (
    VALUES
        ('01/05') --= 1  
        ,('02/05') --= 0  
        ,('03/05') --= 153  
        ,('25/12') --= 0
        ,('26/12') --= 0
        ,('27/12') --= 0
        ,('30/12') --= 2
        ,('31/12') --= 1
    )testData(i)
    CROSS APPLY (
        SELECT * FROM d(t)
    ) out

i     o
----- -----------
01/05 1
02/05 0
03/05 153
25/12 0
26/12 0
27/12 0
30/12 2
31/12 1

(8 row(s) affected)

A brief explanation

create function d(@ char(5)) returns table  -- function definition
return 
select min(o)o -- minimum set value
from(
    select datediff( -- date difference
        day, -- day units
        cast('2016'+right(@,2)+left(@,2)as date), -- convert input parameter to date
        cast(right('2016'+right('0'+cast(d as varchar(4)),4),8)as datetime)+1 -- convert int values into datetimes and add a day
        )o 
    from(
        values(324),(325),(327),(424),(501),(1002),(1224),(1225),(1226),(1231) -- integers representing the day before public holidays
        )d(d)
    )d 
where 0<=o -- only for values >= 0
\$\endgroup\$
2
\$\begingroup\$

JavaScript (ES6), 134 bytes

x=>[0,1,3,31,38,192,275,276,277,282].find(z=>z>=(q=x[0]+x[1]- -[...'20212122121'].slice(0,x[3]+x[4]-1).reduce((a,b)=>+b+a+29,0)-85))-q

user81655 still has me beat by 3 bytes, but I can't find anywhere else to squeeze anything out of here. Works by calculating the number of days gone by instead of using Date, then comparing it to an array of holiday offsets.

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

Python 2, 204 185 165 166 Bytes

Update: Golfed it down by ~20 Bytes by calculating the day of the year by myself. No need for the long imports anymore :)

Update 2: Another 20 Bytes down by realizing that I can treat new years just as day 367 and making some other small adjustments.

def f(d):d=[d[:2],d[3:]];y=sum([31,29,31,30,31,30,31,31,30,31,30,31][:int(d[1])-1])+int(d[0]);return filter(lambda n:n>=y,[85,86,88,116,123,277,360,361,362,367])[0]-y

Try it online!

Ungolfed:

def f(d):
    l=[85,86,88,116,123,277,360,361,362,367]
    d=[d[:2],d[3:]]
    y=sum([31,29,31,30,31,30,31,31,30,31,30,31][:int(d[1])-1])+int(d[0])
    f=filter(lambda n:n>=y,l)
    return f[0]-y

Works by storing the day-of-the-year number of the holidays in a list, filtering out the ones which are before the given date, taking the first element in that filtered list and subtracting the day-of-the-year, which got calculated from the input.

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

PHP, 116 bytes

Pretty straight forward approach. It stores the days of the year for the holidays and pops them as long as they are in the past. Finally the requested day of the year is subtracted.

for($x=[366,361,360,359,276,122,115,87,85,84];($a=date(z,strtotime($argv[1].'-2016')))>$t=array_pop($x););echo$t-$a;

Past all test cases. Runs from command line and accepts the input using a hyphen, like:

$ php holidays.php "12-05"
\$\endgroup\$
1
\$\begingroup\$

ruby 1.9.3, 155 153 bytes

After Christmas Day holiday, we need our super-special-day 366! Similar case as @DenkerAffe.

require'date'
c=(Date.strptime(ARGV[0],'%d/%m')-Date.parse('01/01')).to_i
print [84,85,87,115,122,276,359,360,361,366].map{|i|(i-c)}.select{|i|i>=0}.min

Usage:

$ ruby i_want_to_break_free.rb "03/05"
\$\endgroup\$
  • \$\begingroup\$ I don't know much about Ruby, but I think you can save 3 Bytes by removing whitespaces in line 1 and 3. Also you should specify which input method you are using, since it is not that obvious in your code. You might save some Bytes by defining a function, so you can take the input as argumen and use the return value as output. \$\endgroup\$ – Denker Jan 27 '16 at 13:51
  • \$\begingroup\$ @DenkerAffe Thank you very much! I've saved 2 bytes, but I think a function will increase the number. I've updated the answer with a usage example. \$\endgroup\$ – Tarod Jan 28 '16 at 12:03
0
\$\begingroup\$

05AB1E, 45 bytes

•9JRt€ª´Q®Ië•368вDI„-/S¡`•Σ₁t•ºS₂+s<£O+©@Ïн®-

It may not be 2016 anymore, but whatever.. ;) Still assumes the year is 2016 for the sake of being a leap year with 29 for February.

Try it online or verify all test cases.

Explanation:

•9JRt€ª´Q®Ië•  # Push compressed integer 10549819042671399072072399
  368в         # Converted to base-368 as list: [85,86,88,116,123,277,360,361,362,367]
      D        # Duplicate this list
I              # Take the input
 „-/S¡         # Split it on ["-","/"]
      `        # Push both integer separated to the stack
•Σ₁t•          # Push compressed integer 5354545
     º         # Mirror it without overlap: 53545455454535
      S        # Converted to a list of digits: [5,3,5,4,5,4,5,5,4,5,4,5,3,5]
       ₂+      # Add 26 to each: [31,29,31,30,31,30,31,31,30,31,30,31,29,31]
         s     # Swap to get the month-integer
          <    # Decrease it by 1
           £   # Only leave the first month-1 values from the integer-list
            O  # Sum that sublist
             + # And add it to the day-integer (so we now have the N'th day of the year)
©              # Save this in the register (without popping)
 @             # Do a >= check with each integer in the first (duplicated) list we created
  Ï            # Only leave the truthy values from the list
   н           # Then pop this sublist and only leave its first value
    ®-         # And subtract the integer we saved in the register (N'th day of the year)
               # (after which the result is output implicitly)

See this 05AB1E tip of mine (sections How to compress large integers? and How to compress integer lists?) to understand why •9JRt€ª´Q®Ië• is 10549819042671399072072399; •9JRt€ª´Q®Ië•368в is [85,86,88,116,123,277,360,361,362,367]; and •Σ₁t• is 5354545.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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