g | x | w | all
Bytes Lang Time Link
106JavaScript Node.js240903T062608Zl4m2
102Ruby apl200320T174711ZValue In
085Excel200320T165008ZChronoci
051Bash + GNU utilities200319T023900ZMitchell
07205AB1E200319T094847ZKevin Cr
158Python200319T100514ZNoodle9
108Perl 5 MTimeLocal=timegm_modern pF'/'200319T180056ZXcali
125Python 3.8 prerelease200319T150726ZSurculos
167Red200319T092447ZGalen Iv
125Perl 5 MPOSIX naF/200319T080617Zandytech
115PHP200319T123241ZGuillerm
131JavaScript ES6200319T095557ZArnauld
121Perl 6200319T011658ZJo King

JavaScript (Node.js), 106 bytes

s=>(x=[s,s.split`/`.reverse()].flatMap(a=>+(i=new Date(a))?(i+0).slice(0,3):[]))[1]&&x[1]!=x[0]?'Err':x[0]

Try it online!

More than 21 bytes less than Arnauld, aka. more than a week table save

Ruby -apl, 102 bytes

Takes input as a line of input from STDIN with space as the separator. Outputs false if the dates are ambiguous and the days of week don't match.

In order to make sure we get a correct date the first time, we sort the first two arguments to ensure the smaller of the two (which is guaranteed to be less than 13) is placed in the month field. We then reverse the arguments when comparing the days of week using wday (if it would be a valid date, done by checking b>12 first), and if they match (or the date was unambiguous) we use strftime("%a") to output the day of the week in a human-readable format.

-p takes each line of input and outputs the contents of $_ at the end of a program run.

-a is the autosplit flag, which will split the input on spaces and save into $F.

-l makes the input ignore newlines (they are included by default for Ruby's input function).

*x,y=$F.map &:to_i
d=Time.gm y,*x.sort!
a,b=x
$_=(b>12||d.wday==Time.gm(y,b,a).wday)&&d.strftime("%a")

Try it online!

Excel, 85 Bytes

=IF(OR(DAY(A1)>12,MOD(A1-DATE(YEAR(A1),DAY(A1),MONTH(A1)),7)=0),TEXT(A1,"ddd"),"???")

Input the date into cell A1

Since Excel will automatically evaluate the date into a valid form for us, we can first check if the Day is >12, since this would immediately make the date unambiguous.

We then work out if difference between the "dd/mm/yyyy" and "mm/dd/yyyy" versions is a multiple of 7, using MOD, since this would mean both Weekdays are the same

If either of these conditions are True, then the Weekday is unambiguous.

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:

And for the months January and February:

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".

Python, 180 164 158 bytes

Saved 3 bytes thanks to Kevin Cruijssen and Arnauld!!!
Saved 3 bytes thanks to Surculose Sputum!!!

lambda a,b,c:(x:=d(a,b,c))==(y:=d(b,a,c))and x or(x and y and.1)or y or x
from datetime import*
def d(a,b,y):
 try:return date(y,a,b).strftime("%a")
 except:0

Try it online!

Assumes we can input the date as three separate arguments and returns 0.1 if ambiguous.

Date as string

Python, 197 191 bytes

Ditto the above saves and thanks! :D

lambda s:(x:=d(s))==(y:=d(s,1))and x or(x and y and.1)or y or x
from datetime import*
def d(s,q=0):
 a,b,c=map(int,s.split('/'))
 if q:a,b=b,a
 try:return date(c,a,b).strftime("%a")
 except:0

Try it online!

Perl 5 -MTime::Local=timegm_modern -pF'/', 116 113 108 bytes

sub f{$_[1]--;eval{timegm_modern 0,0,9,@_}}$_=($a=f@g=@F)*($b=f@F[1,0,2])&$a-$b?Err:substr gmtime($a|$b),0,3

Try it online!

Tries converting both formats into an epoch time. The eval traps the error that would occur for an invalid date. Multiplies the two epoch times together to make sure that one is 0, and checks that they are not the same with substraction. Uses their bitwise or to convert back to an actual date, then picks off the day of the week from that.

Python 3.8 (pre-release), 125 bytes

Improvement over @Noodle9's answer

lambda a,b,y:[.1,w:=g(y,a,b)][w==g(y,b,a)]
g=lambda y,m,d:m<13and date(y,m,d).strftime("%a")or g(y,d,m)
from datetime import*

Try it online!

Input: 3 integers representing date, month, and year.
Output: A 3-character abbreviation of a weekday, or 0.1 if the weekday is ambiguous.

How:

Red, 186 169 167 bytes

-2 bytes thanks to Kevin Cruijssen

func[d][a: to-date t: load form split d"/"m: min a b:
to-date reduce[t/2 t/1 t/3]either any[a/10 = b/10 t/1 > 12
t/2 > 12][pick[:Mon:Tue:Wed:Thu:Fri:Sat:Sun]m/10][.1]]

Try it online!

Takes the input as string, because Red doesn't accept invalid dates in dd/mm/yyyy format. That's strange, because the coerce function to-date that takes a block of 3 values [dd mm yyyy] accepts any integer, including 0 and negative numbers and creates a "correct" date based on them.

Perl 5 -MPOSIX -naF/, 125 bytes

sub d{substr ctime(mktime 6,0,0,$_[0],$_[1]-1,$_[2]-1900),0,3}@G=@F[1,0,2];say$F[0]>12?d@F:$F[1]>12?d@G:d(@F)eq d(@G)?d@F:Err

Try it online!

Not completely happy with this one, but haven't thought up a better way (yet).

PHP, 115 bytes

foreach(['/','-']as$s){if($x=strtotime(strtr($argn,'/',$s)))$o=date('D',$x);if($p&&$o!=$p)die('err');$p=$o;}echo$p;

Try it online!

Uses the peculiarity that the builtin strtotime parses dates as either European or American format according to whether the delimiter is either slash or dash.

Takes input as a/b/Y and returns err for an error condition.

JavaScript (ES6),  136 132  131 bytes

Returns "Err" for an error.

s=>'SunMonTueWedThuFriSatErr'.match(/.../g)[[i,j]=([d,m,y]=s.split`/`).map(M=>new Date(y,M-1,M^m^d).getDay()),d<13?m>12|i==j?i:7:j]

Try it online!

Commented

s =>                           // s = input string
  'SunMonTueWedThuFriSatErr'   // lookup string
  .match(/.../g)[              // split it and pick the relevant entry:
    [i, j] =                   //   (i, j) = day-of-week indices
      ([d, m, y] = s.split`/`) //   split s into (d, m, y)
      .map(M =>                //   for each value M in there (ignoring y):
        new Date(              //     create a date:
          y,                   //       using y as the year
          M - 1,               //       using M - 1 as the 0-indexed month
          M ^ m ^ d            //       using the other value as the day
        )                      //     end of Date()
        .getDay()              //     get the day-of-week index (Sun:0, ..., Sat:6)
      ),                       //   end of map()
    d < 13 ?                   //   if d is a valid month:
      m > 12 | i == j ? i      //     non-ambiguous if m is an invalid month or i = j
                      : 7      //     ambiguous otherwise (-> "Err")
    :                          //   else:
      j                        //     use j
  ]                            // end of lookup

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!