17
\$\begingroup\$

inspired by this post.

Consider 2 coworkers who work Monday - Friday, every week of every year, with no breaks or national holidays.

On day n (day, month and year), worker 1 asks worker 2 their birthday, in the hopes of throwing an office party for the occasion.

However, worker 2 dislikes office parties, and would like to ensure the next office party be thrown as late as possible.

What is the day (day and month but not year) they should claim their birthday is such that the maximum time (in days) elapses between day n and the next instance of the day claimed being a working day?

Input:

The date of day n, including day, month, and year, in the format of your choice.
The format may include a time of day (always the same one though).
n > 31 December 1582.

Output:

A date including day and month, such that the next instance of that day being a working day (Mon-Fri) is as far in the future from day n as possible.

The format is also flexible, and the output may not represent the actual next birthday in question, as long as the day and month are correct (for example, outputting 31 January 1934 5:00AM is a valid output even if the next birthday happens on 31 January 2006)

Edge polishing:

Your algorithm must minimally work with the current Gregorian calendar, including leap years. Assume the Gregorian calendar will keep being the standard for infinite time in the future, and that the current pattern of leap years stays the same:

A year is a leap year if it is divisible by 4, except if it is divisible by 100, except if it is divisible by 400.

You may consider that day n is already passed, such that worker 1 would have to wait at least a full year before throwing a party (because they were unprepared). You may also do the opposite. Specify which one you chose in your submission.

Test cases:

March 1st, 1892 -> February 29th. (4,381 days)
February 28th, 2024 -> February 22nd. (1,090 days)
May 26th, 2025 -> May 23rd. (1,093 days)
February 25, 2100 -> February 29th. (1,464 days)

Scoring

This is .

Notes:

I checked the test cases manually in outlook, so I could be very wrong. Additional interesting or edge-case tests are welcome.

asked May 27 at 12:04
\$\endgroup\$
3
  • \$\begingroup\$ sandbox \$\endgroup\$ Commented May 27 at 12:06
  • 1
    \$\begingroup\$ While the test case answer itself seems to be accurate, 2028年05月23日 is a Tuesday, so the number of days is actually somewhat less than 1458. \$\endgroup\$ Commented May 29 at 17:15
  • \$\begingroup\$ @Nitrodon thanks, i was suspicious of it because it seemed quite high. \$\endgroup\$ Commented May 30 at 7:26

6 Answers 6

9
\$\begingroup\$

Bash with GNU date, 181 bytes

Requires an English locale (or indeed any locale where Saturday and Sunday but no others begin with S). Input is any date date (e.g. today)

a(){ date -f- +%F\ %a|sed /S/d\;q;}
(printf "1ドル+%dyear+307day\n" {0..12}|date -f- +%Y-02-29|a
for i in {1..7}day
do printf "1ドル-$i+%dyear\n" {1..3}|a
done)2>/dev/null|sort|sed '$!d'

The function a selects the first non-weekend date from its input stream. We apply this to the next three leap days and the next three of each day of the preceding week (which may include a leap day), and choose the latest from these.

Demo

Using examples from the question:

$ for i in '1 Mar 1892' '2/28/24' 'May 26, 2025' '2100-02-25'; do ./282005.sh "$i"; done
1904年02月29日 Mon
2027年02月22日 Mon
2028年05月23日 Tue
2104年02月29日 Fri
answered May 27 at 14:50
\$\endgroup\$
1
  • 1
    \$\begingroup\$ So on a quick browse through this table from Omniglot, it should work in locales with day names from West Germanic languages (English, Dutch, German, and close relatives), Baltic languages (Latvian and Lithuanian) possibly depending on capitalisation, and possibly Brythonic languages (Welsh, Cornish) depending on the abbreviations used. A few constructed languages also have suitable day names (Ido, Interlingua, Lingua Franca Nova...) but don’t afaics have any associated locales. \$\endgroup\$ Commented May 29 at 12:17
5
\$\begingroup\$

JavaScript (Node.js), 95 bytes

x=>{for(e=new Set;e.size<366;t[0]!='S'&&e.add(t.slice(4,10)))x=new Date(+x+8e7),t=x+0;return x}

Try it online!

JavaScript (Node.js), 91 bytes, need stack expand

f=(x,e=new Set)=>e.size<366?f(x=new Date(+x+8e7),e,t=x+0,t[0]!='S'&&e.add(t.slice(4,10))):x

Try it online!

Find the last day which can't claim still not birthday as every possible day has been seen

answered May 27 at 12:48
\$\endgroup\$
3
\$\begingroup\$

05AB1E, 116 bytes

V[ ̄Ùg366Q#Y`т‰0Kθ4ÖUD<i28円X+ë<7%É31α}‹iY¬>0ëY13⁄4ǝDÅsD12‹i>1ë1円Dǝ¤>2}}ǝVY`UD3‹©12*+>2*T÷®Xα©т%D4÷®т÷©4÷®·(O7%2@iY ̈ˆ] ̄θ

Port of the approach used in @l4m2's JavaScript answer, so make sure to upvote that answer as well!

Input as a triplet \$[dd,MM,yyyy]\$; output as a pair \$[dd,MM]\$.

No TIO, since it'll time-out.
I've ran the test cases locally on my PC, and the first test case still doesn't have a result after 30 minutes (likely because of the 4k+ items in the global array that will be uniquified every iteration). The other three test cases take about 5 minutes each, so here is a screenshot of those:

enter image description here

Explanation:

V # Store the (implicit) input-triplet in variable `Y`
[ # Start an infinite list:
 ̄ # Push the global array (initially empty)
 Ù # Uniquify it
 g # Pop and push its length, to get the amount of distinct items
 366Q # If this length equal 366:
 # # Stop the infinite loop
 Y`т‰0Kθ4ÖUD<i28円X+ë<7%É31α}‹iY¬>0ëY13⁄4ǝDÅsD12‹i>1ë1円Dǝ¤>2}}ǝV
 # Change `Y` to the next day
 Y`UD3‹©12*+>2*T÷®Xα©т%D4÷®т÷©4÷®·(O7%2@
 # Check if date `Y` is a weekday
 i # Pop this check, and if it's truthy:
 Y # Push date `Y`
 ̈ # Remove its trailing year
 ˆ # Pop and add this [dd,MM]-pair to the global array
] # Close both the if-statement and infinite loop
 ̄θ # Push the last [dd,MM]-pair of the global array
 # (which is output implicitly as result)

Both going to the next day Y`т‰0Kθ4ÖUD<i28円X+ë<7%É31α}‹iY¬>0ëY13⁄4ǝDÅsD12‹i>1ë1円Dǝ¤>2}}ǝV and the weekday check Y`UD3‹©12*+>2*T÷®Xα©т%D4÷®т÷©4÷®·(O7%2@ are taken from my 05AB1E answer for The Work Day Countdown challenge. A full explanation of these code-snippets can be found in that answer.

answered May 28 at 14:19
\$\endgroup\$
3
\$\begingroup\$

R, 157 bytes

\(y,t,`[`=as.Date,`*`=\(a,b)paste(a,b)["%Y %e%m"],L={}){for(m in 0:1e4)for(x in 0:12+y)if(!is.na(j<-x*m)&j>y*t){L=c(L,j);if(format(j,"%u")<6)break};max(L)[]}

Attempt This Online!

A nice challenge indeed.

I have put the solution into a function that receives the date as 2 separate arguments: \$y = \$ year and \$t = \$ day+month, to have the freedom to operate on the years and the dates independently. Leading zero in the day number is ignored. For instance, f(1892,103) is the first case from the OP (01-03-1892).

The output is in the standard date format, even if it has cost me some bytes (without an explicit as.Date, the output would be the difference in days between that date and 1970年01月01日).

How it works.

There is one loop for the day+month combination, which simply produces every number between 0 and 10000. We actually need only some numbers in the range from 101 to 3112, but the invalid ones will be further rejected.

Now the second loop generates a sequence of 12 years (99 years would make the same bytecount, but the ATO server times out), starting from the input year, takes each day+month and concatenates with the year. If the generated date is valid and it is more recent than the input date, then we add it to the list. Afterwards, it checks whether that date was a working day, in which case it breaks the internal loop and switches to the next date; otherwise, it increments the year, keeping the same date.

The most recent date of the list \$L\$ is output.

answered Oct 21 at 8:47
\$\endgroup\$
2
\$\begingroup\$

Ruby, (削除) 137 (削除ここまで) (削除) 97 (削除ここまで) 75 bytes

Uses the -rdate flag to auto-import the Date library. Input is a Date object, output is a (削除) string (削除ここまで) Date object representing the day of the party. Assumes your coworker doesn't need to wait a year to throw a party. (削除) Assumes input date has a positive year (aka not BC/BCE) because the Gregorian calendar wasn't adopted until 1582. Takes a while to run for two reasons:

  1. In order to ensure 2/29 is accounted for, I check every date from the input to 3000 days in the future (since leap years are every 4 years but skipped every 100 years, I figure you should definitely get one within \365ドル*8=2920\$ days) because it saves bytes over a sane option like (D.new(4)..D.new(5)) that covers only 366 days.
  2. The shortest way to grab the latest future date is to simply check every year ever starting from year 1 and stopping at the first one whose relevant date is after the input and on a weekday. (d.year..) would be more sane but that's five whole bytes wasted. This is repeated for all 3000 dates checked. (削除ここまで)

-40 bytes (!!) by figuring out I can apply a block to uniq to make it remove duplicated valid dates, eliminating my insane weird method of looking at 3k dates just for their month and day just to make sure February 29 is included, then finding the first valid year for that date, even though most of the month/day combinations would be redundantly checked many, many times and running thousands of unnecessary calculations in the name of saving bytes.

-7 bytes by realizing I no longer need D=Date; as I am no longer creating fresh Date objects in my code.

-15 bytes by leveraging the flexible output requirements that state that any date can be returned as long as the month/date are correct.

->d{(d..d+9e3).select{(1..5)===_1.wday}.uniq{[_1.mon,_1.day]}.max_by{_1-d}}

Attempt This Online!

answered May 31 at 8:51
\$\endgroup\$
3
  • \$\begingroup\$ Note that the longest possible duration in the linked Puzzling question is 4381 days ≈ 12 years due to leap days not occuring on years that are multiples of 100. 5e3 should be enough \$\endgroup\$ Commented Jun 1 at 4:05
  • \$\begingroup\$ @emanresuA my solution was only grabbing 3000 dates because it would extract the month and day and use that in another loop that calculated year separately, which is why it was returning the correct answer even in that case. The only reason I did that was because I thought it would save bytes over disqualifying dates that already had a valid weekday in an earlier year. However, your comment inspired me to look into the Ruby spec and find a way to do just that in way fewer bytes. Thanks! \$\endgroup\$ Commented Jun 2 at 7:37
  • \$\begingroup\$ i hadn't seen how far you golfed this since the first submission, very nice golf! \$\endgroup\$ Commented Jun 13 at 14:10
1
\$\begingroup\$

Python3, 280 bytes

Assumes that worker 1 must wait at least 1 year before throwing a party.

import datetime as D
def f(n):
 d,v=D.date(*n),[]
 for i in range(1,13):
 for I in range(1,[[32,31][i in[4,6,9,11]],30][i==2]):
 Y=n[0]
 while 1:
 try:
 if(L:=D.date(Y:=Y+1,i,I)).weekday()<5:v+=[L];break
 except:1
 return(M:=max(v,key=lambda x:x-d)).month,M.day,M-d

Try it online!

answered May 28 at 15:39
\$\endgroup\$
2
  • 1
    \$\begingroup\$ 276 bytes \$\endgroup\$ Commented May 30 at 15:23
  • 1
    \$\begingroup\$ 265 bytes \$\endgroup\$ Commented Jun 6 at 20:27

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.