Day of the week of an ambiguous date
Bash + GNU utilities, 97 96 93 85 80 76 73 58 56 51 bytes
(${d=date +%a -d$3-}$1-$2;$d$2-$1)|uniq|sed N\;cErr
Try it online!
5 bytes off thanks to user41805, who pointed out that I could use sed's c
command instead of the final s
command.
2 bytes off thanks to Nahuel Fouilleul. (Nahuel also helped by shaving 4 bytes off in an earlier version that has since been revamped.)
When I noticed that the separators can be any non-digit character, I changed the code to use spaces as the separator.
This is called with a command like
checkdate 01 06 2020
The output is on stdout. (There's spurious output on stderr.)
05AB1E, 88 82 79 76 79 73 72 bytes
#ÂÀ‚Dε`UÐ3‹12*+>₂*T÷s3‹Xα¬Ésт%D4÷O.•A±₁γβCüIÊä6’C•3ôsè}DËiнës€¨13‹WiтëθÏ
-3 bytes thanks to @Arnauld (initially -6, but +3 bytes again, since Jan./Feb. 2000 would still result in the previous century in the formula).
-7 bytes thanks to @Grimmy.
Input is space-separated; and outputs in lowercase with 100
as error.
Try it online or verify all test cases.
Explanation:
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 for the months March through December:
- \$q\$ is the \$day\$ of the month (
[1, 31]
) - \$m\$ is the 1-indexed \$month\$ (
[3, 12]
) - \$K\$ is the year of the century (\$year \bmod 100\$)
- \$J\$ is the 0-indexed century (\$\left\lfloor {\frac {year}{100}}\right\rfloor\$)
And for the months January and February:
- \$q\$ is the \$day\$ of the month (
[1, 31]
) - \$m\$ is the 1-indexed \$month + 12\$ (
[13, 14]
) - \$K\$ is the year of the century for the previous year (\$(year - 1) \bmod 100\$)
- \$J\$ is the 0-indexed century for the previous year (\$\left\lfloor {\frac {year-1}{100}}\right\rfloor\$)
Resulting in in the day of the week \$h\$, where 0 = Saturday, 1 = Sunday, ..., 6 = Friday.
Source: Zeller's congruence
Since the input is guaranteed to be in the year-range \$[2000, 2030]\$, we can modify \$+ \left\lfloor{\frac{J}{4}}\right\rfloor - 2J\$ to a hard-coded \$-35\$ to save some bytes. EXCEPT, when it's Jan./Feb. 2000, in which case it's the previous century. For those, we use ƒs
, to add 1 to our hard-coded replacement of \$J\$.
In addition, we won't need the \${\bmod {7}}\$, since we only use it to index into the string list, and 05AB1E uses modular indexing anyway.
We can save 2 more bytes, by just removing the \$-35\$ all together, since it's a multiple of 7 anyway.
And one more byte, by changing the \$\left\lfloor{\frac{13(m+1)}{5}}\right\rfloor\$ to \$\left\lfloor{\frac{26(m+1)}{10}}\right\rfloor\$, since 05AB1E has a single-byte builtin for 26
and 10
(which are ₂
and T
respectively).
Code explanation:
# # Split the (implicit) input-string by spaces
 # Bifurcate it (short for Duplicate & Reverse copy)
À # Rotate the reversed copy once towards the left
‚ # Pair the top two values together
# (we now have a pair [[day,month,year],[month,day,year]])
D # Duplicate it
ε # Map over both values in this pair:
Now comes Zeller's congruence into play:
` # Push the day, month, and year separated to the stack
U # Pop and save the year in variable `X`
Ð # Triplicate the month
3‹ # 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
> # Month + 1
₂* # Multiplied by 26
T÷ # Integer-divided by 10
s3‹ # Check if the month is below 3 again (resulting in 1 / 0)
Xα # Take the absolute difference with the year
¬ # Push the first digit (without popping the mYear itself)
É # Check if its odd (1 if 1; 0 if 2)
s # Swap to take the mYear again
т% # mYear modulo-100
D4÷ # mYear modulo-100, integer-divided by 4
O # Take the sum of all values on the stack
And then we'll continue:
.•A±₁γβCüIÊä6’C•
# Push compressed string "satsunmontuewedthufri"
3ô # Split it into parts of size 3: ["sat","sun","mon","tue","wed","thu","fri"]
s # Swap to get the earlier calculated number
è # And index it into this list (0-based and with wrap-around)
}D # After the map: duplicate the resulting two days
Ëi # If they are both the same:
н # Leave just one of them
ë # Else (they are different):
s # Swap, to take the initially duplicated pair of
# [[day,month,year],[month,day,year]]
۬ # Remove the year from each
13‹ # Check for the remaining values if they are smaller than 13
W # Push the flattened minimum (without popping)
i # If it's truthy (so day and month are both below 13):
т # Push 100 (as error value)
ë # Else:
θ # Pop and leave just the last one (either [0,1] or [1,0])
Ï # And only keep the day at the truthy index
# (after which the top of the stack is output implicitly)
See this 05AB1E tip of mine (section How to compress strings not part of the dictionary?) to understand why .•A±₁γβCüIÊä6’C•
is "satsunmontuewedthufri"
.
Perl 6, 124 121 bytes
{<Err Mon Tue Wed Thu Fri Sat Sun>[2>set($/=|grep ?*,map {try Date.new(|$_).day-of-week},m:g/\d+/[[2,1,0],[2,0,1]])&&$0]}
Try it online!