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!