Revision 9aec6be5-d8e4-484b-bc15-0b0ad55baeb9 - Code Golf Stack Exchange
#[05AB1E](https://github.com/Adriandmen/05AB1E/wiki/Commands), <s>130</s> 128 [bytes](https://github.com/Adriandmen/05AB1E/wiki/Codepage)
žežfžg)V0[YÁćU`D3‹12*+>13*5÷Xт%D4÷Xт÷©4÷®·()ćsO7%2@+Y`©4Ö®тÖ®т4*%_**UD2Qi28円X+ë31s<7%É-}‹iY¬>0ëY1¾ǝDÅsD12‹i>1ë1円Dǝ¤>2}}ǝVYI'.¡Q#
I'm out of my mind..
For the golfing language 05AB1E it doesn't matter at all whether the input is with `.` or `-`. However, 05AB1E doesn't have any builtins for Date objects or calculations. The only builtin regarding dates it has is today's year/month/day/hours/minutes/seconds/microseconds.
So because of that, almost all of the code you see are manual calculations to go to the next day, and calculating the day of the week.
[Try it online](https://tio.run/##yy9OTMpM/f//6L7Uo/vSju5L1wwziI483HikPTTBxfhRw05DIy1tO0NjLdPD2yMuNqm6mIDpw9sPrQSyDq07tF1D80h7sb@5qpGDdmQCSHTaoXVABWDSREs1Xksr1MUoMDPGyCJC@/BqY8NiG3PVw526tUCzMyMPrbEzOLw60vDQvuNzXQ63FrsYGoHE7QwPr44xdDk@99ASO6Pa2uNzwyI91fUOLQxU/v/f0EzP0EDPyMDQAgA) or [Try it online with an emulated self-specified date of 'today'](https://tio.run/##JYzNSsNAFIX3fYohEtImJMxPpSoSXGTjomgXLQ0G7EimGmgTyEyELgoqSPEF3GVRcCORboMbFzOFrH0FXySm7V3c@93DOSfh9C5itXadCUBBSAUDUQzEAwPTJJ1TAcLQ7vftRTNAJCDjDFAO6EywNKYiemSAC5qKKL4/hNtsns0aCA9vMgWGSEK6MDpnWtBqaQTZCNsYolPNsOW6HsEbXz1vV8OJR/6evhE2LRcR81iV498X3evuryrlZ0NyI8t2Z7viVz0dX1j@ZKe@y01j2O@uqd@a5tDDgyjAJ2NLFQTx856u3uxl0x358suFqvCR/KlyT71yD@Gd7iJVBMircvnh4uWyykf@peHI9eCorgl0IHEwxPAf).
###Explanation:
*Wall of text incoming.*
In general, the code follows the following pseudo-code:
1 Date currentDate = today;
2 Integer counter = 0;
3 Start an infinite loop:
4* If(currentDate is NOT a Saturday and currentDate is NOT a Sunday):
5 Counter += 1;
6* currentDate += 1; // Set currentDate to the next day in line
7 If(currentDate == parsed input-string):
8 Stop the infinite loop, and output the counter
1) `Date currentDate = today;` is this part of the 05AB1E program:
<!-- language-all: lang-python -->
že # Push today's day
žf # Push today's month
žg # Push today's year
) # Wrap them into a single list
V # And store this list in variable `Y`
<!-- language-all: none -->
2) `Integer counter = 0;` and 3) `Start an infinite loop:` are straight-forward in the 05AB1E program:
<!-- language-all: lang-python -->
0 # Push 0 to the stack
[ # Start an infinite loop
<!-- language-all: none -->
4) `If(currentDate is NOT a Saturday and currentDate is NOT a Sunday):` is the first hard part with manual calculations. Since 05AB1E has no Date builtins, we'll have to calculate the *Day of the Week* manually.
The general formula to do this is:
$${\displaystyle h=\left(q+\left\lfloor {\frac {13(m+1)}{5}}\right\rfloor +K+\left\lfloor {\frac {K}{4}}\right\rfloor +\left\lfloor {\frac {J}{4}}\right\rfloor -2J\right){\bmod {7}},}$$
Where:
- \$q\$ is the day of the month
- \$m\$ is the month for March through December (`[3, 12]`), and the month + 12 for January and February (`1` becomes `13`; `2` becomes `14`)
- \$K\$ is the year of the century (\$year \bmod 100\$)
- \$J\$ is the 0-indexed century ( \$\left\lfloor {\frac {year}{100}}\right\rfloor\$)
Resulting in in the day of the week \$h\,ドル where 0 = Saturday, 1 = Sunday, ..., 6 = Friday.
[Source: Zeller's congruence](https://en.wikipedia.org/wiki/Zeller%27s_congruence)
We can see this in this part of the 05AB1E program:
<!-- language-all: lang-python -->
Y # Push variable `Y`
Á # Rotate it once towards the right ([dd, MM, yyyy] becomes [yyyy, dd, MM])
ć # Head extracted (pops the list, and pushes [dd, MM] and yyyy)
U # Pop and save the year in variable `X`
` # Push the day and month to the stack
D3‹ # Check if the month is below 3 (Jan. / Feb.),
# resulting in 1 or 0 for truthy/falsey respectively
12* # Multiply this by 12 (either 0 or 12)
+ # And add it to the month
# This first part was to make Jan. / Feb. 13 and 14
# Now we do the actual Zeller's formula:
> # Month + 1
13* # Multiplied by 13
5÷ # Integer-divided by 5
Xт% # Year modulo-100
D4÷ # Year modulo-100 integer-divided by 4
Xт÷©4÷ # Year integer-divided by 100, and then integer-divided by 4
®·( # Year integer-divided by 100, doubled, and then made negative
) # Wrap the entire stack into a list
ć # Extract the head (the counter variable that was also on the stack)
s # Swap so the calculated values above are as list at the top
O # Take the sum of this entire list
7% # And then take modulo-7 to complete the formula,
# resulting in 0 for Saturday, 1 for Sunday, and [2, 6] for [Monday, Friday]
2@ # Check if the day is greater than or equal to 2 (so a working day)
<!-- language-all: none -->
5) `Counter += 1;` is straight-forward again:
<!-- language-all: lang-python -->
# The >=2 check with `2@` results in either 1 for truthy and 0 for falsey
+ # So just adding it to the counter variable is enough
<!-- language-all: none -->
6) `currentDate += 1; // Set currentDate to the next day in line` is again more complex, because we have to do it manually. So this will be expanded to the following pseudo-code:
a Integer isLeapYear = currentDate.year % 4 == 0 &&
(currentDate.year % 100 == 0 & currentDate.year % 400 != 0);
b Integer daysInCurrentMonth = currentDate.month == 2 ?
c 28 + isLeapYear
d :
e 31 - (currentDate.month - 1) % 7 % 2;
f If(currentDate.day < daysInCurrentMonth):
g nextDate.day += 1;
h Else:
i nextDate.day = 1;
j If(currentDate.month < 12):
k nextDate.month += 1;
l Else:
m nextDate.month = 1;
n nextDate.year += 1;
Sources:
[Algorithm for determining if a year is a leap year.](http://www.dispersiondesign.com/articles/time/determining_leap_years)
[Algorithm for determining the number of days in a month.](http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month)
6a) `Integer isLeapYear = currentDate.year % 4 == 0 && (currentDate.year % 100 == 0 & currentDate.year % 400 != 0)` is done like this in the 05AB1E program:
<!-- language-all: lang-python -->
Y # Push variable `Y`
` # Push the days, month and year to the stack
© # Save the year in the register (without popping)
4Ö # Check if the year is divisible by 4
* # AND
®тÖ # Check if the year is divisible by 100
* # And
®т4*%_ # Check if the year modulo-400 is 0
U # Pop and save the result in variable `X`
<!-- language-all: none -->
6b) `currentDate.month == 2 ?` and 6c) `28 + isLeapYear` are done like this:
<!-- language-all: lang-python -->
D # Duplicate the month that is now the top of the stack
2Q # Check if it's equal to 2
i # And if it is:
\ # Remove the duplicated month from the top of the stack
28X+ # Add 28 and variable `X` (the isLeapYear) together
<!-- language-all: none -->
6d) `:` and 6e) `31 - (currentDate.month - 1) % 7 % 2;` are done like this:
<!-- language-all: lang-python -->
ë # Else:
31 # Push 31
s # Swap so the duplicated month is at the top of the stack again
< # Month - 1
7% # Modulo-7
É # Is odd (shortcut for %2)
- # Subtract it from the 31
} # Close the if-else
<!-- language-all: none -->
6f) `If(currentDate.day < daysInCurrentMonth):` is done like this:
<!-- language-all: lang-python -->
‹ # Check if the day that is still on the stack is smaller than the value calculated
i # And if it is:
<!-- language-all: none -->
6g) `nextDate.day += 1;` is done like this:
<!-- language-all: lang-python -->
Y # Push variable `Y`
¬ # Push its head, the days (without popping the list `Y`)
> # Day + 1
0 # Push index 0
# (This part is done after the if-else clauses to save bytes)
}} # Close the if-else clauses
ǝ # Insert the day + 1 at index 0 in the list `Y`
V # Pop and store the updated list in variable `Y` again
<!-- language-all: none -->
6h) `Else:` and 6i) `nextDate.day = 1;` are then done like this:
<!-- language-all: lang-python -->
ë # Else:
Y # Push variable `Y`
1 # Push a 1
¾ # Push index 0
ǝ # Insert 1 at index 0 (days part) in the list `Y`
<!-- language-all: none -->
6j) `If(currentDate.month < 12):`:
<!-- language-all: lang-python -->
D # Duplicate the list `Y`
Ås # Pop and push its middle (the month)
D12‹ # Check if the month is below 12
i # And if it is:
<!-- language-all: none -->
6k) `nextDate.month += 1;`:
<!-- language-all: lang-python -->
> # Month + 1
1 # Push index 1
# (This part is done after the if-else clauses to save bytes)
}} # Close the if-else clauses
ǝ # Insert the month + 1 at index 1 in the list `Y`
V # Pop and store the updated list in variable `Y` again
<!-- language-all: none -->
6l) `Else:`, 6m) `nextDate.month = 1;` and 6n) `nextDate.year += 1;` are then done like this:
<!-- language-all: lang-python -->
ë # Else:
\ # Delete the top item on the stack (the duplicated month)
1 # Push 1
D # Push index 1
ǝ # Insert 1 at index 1 (month part) in the list `Y`
¤ # Take the tail of `Y` (the year)
> # Year + 1
2 # Index 2
# (This part is done after the if-else clauses to save bytes)
}} # Close the if-else clauses
ǝ # Insert the year + 1 at index 2 in the list `Y`
V # Pop and store the updated list in variable `Y` again
<!-- language-all: none -->
And finally at 8) `If(currentDate == parsed input-string):` and 9) `Stop the infinite loop, and output the counter`:
<!-- language-all: lang-python -->
Y # Push variable `Y`
I # Push the input
'.¡ # Split it on dots
Q # Check if the two lists are equal
# # And if they are equal: stop the infinite loop
<!-- language-all: none -->