| Bytes | Lang | Time | Link |
|---|---|---|---|
| 106 | JavaScript Node.js | 240903T062608Z | l4m2 |
| 102 | Ruby apl | 200320T174711Z | Value In |
| 085 | Excel | 200320T165008Z | Chronoci |
| 051 | Bash + GNU utilities | 200319T023900Z | Mitchell |
| 072 | 05AB1E | 200319T094847Z | Kevin Cr |
| 158 | Python | 200319T100514Z | Noodle9 |
| 108 | Perl 5 MTimeLocal=timegm_modern pF'/' | 200319T180056Z | Xcali |
| 125 | Python 3.8 prerelease | 200319T150726Z | Surculos |
| 167 | Red | 200319T092447Z | Galen Iv |
| 125 | Perl 5 MPOSIX naF/ | 200319T080617Z | andytech |
| 115 | PHP | 200319T123241Z | Guillerm |
| 131 | JavaScript ES6 | 200319T095557Z | Arnauld |
| 121 | Perl 6 | 200319T011658Z | Jo 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]
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")
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
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".
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
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
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
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*
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:
date(y,m,d).strftime("%a")returns the abbreviated weekday of the dated/m/y.g(y,m,d)returns the weekday if the date is valid, otherwise returns the weekday of the date where month and day are swapped. Thus ifg(y,a,b)andg(y,b,a)are the same, then the weekday is not ambiguous. Otherwise, the weekday is ambiguous.[.1,w:=g(y,a,b)][w==g(y,b,a)]evaluates tog(y,a,b)if the weekday is not ambiguous, or0.1if the weekday is ambiguous.
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]]
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
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;
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]
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]}