Goal
Write a program or function that returns the day of the week for a date, eg.
01/06/2020 -> Mon
However, it's unknown if the date is in the format mm/dd/yyyy or dd/mm/yyyy. If you can be certain of the day of the week, return it. If there is uncertainty, return an error.
02/07/2020 -> Err (Thu? Fri?)
Input
A date in the format #/#/####.
- The numbers can optionally have leading zeros.
- The year should be read verbatim, assume that 19 = 19 AD, not 19 = 2019 AD.
- The separators can be any non-digit character.
- Assume the input date is valid in at least one format (ie. not 13/13/2020).
- At a minimum, support dates from 2000 through 2030 (which are likely covered by any modern OS and language with a date library.)
Requirements
- If the first and second numbers:
- are the same (03/03/2020), the date is unambiguous.
- either is greater than 12 (13/09/2020, 09/13/2020), the date is unambiguous.
- are different and both 12 or less (09/11/2020), the date is ambiguous.
Output
- If the date is unambiguous, return the day of the week.
- If the date is ambiguous:
- If both versions of the date have the same day of the week, return the day of the week.
- If both versions of the date have a different day of the week, return an error.
- A day of the week should be returned as a string of the day's three-letter abbreviation ("Mon"), case-insensitive.
- An error should be returned as any other three-character string, not all whitespace, eg. "Err", "NaN", "???".
Test Cases
02/07/2020 -> Err
01/06/2020 -> Mon
05/05/2020 -> Tue
24/06/2020 -> Wed
05/09/2030 -> Thu
03/13/2020 -> Fri
29/02/2020 -> Sat
03/12/2000 -> Sun
Scoring
The code with the least byte count in a week wins the tick.
-
6\$\begingroup\$ Can we take the three parts of the date as three separate arguments? \$\endgroup\$RGS– RGS2020年03月19日 01:09:00 +00:00Commented Mar 19, 2020 at 1:09
-
2\$\begingroup\$ Suggested test case 3/2/2019. Since 3rd of feburary and 2nd of march are both a monday, does this count as ambiguous or unambiguous for the purposes of this challenge? \$\endgroup\$Darren H– Darren H2020年03月19日 15:12:50 +00:00Commented Mar 19, 2020 at 15:12
-
1\$\begingroup\$ @DarrenH the date is ambiguous, but Mon should still be outputted, according to output specification. \$\endgroup\$Surculose Sputum– Surculose Sputum2020年03月19日 16:13:39 +00:00Commented Mar 19, 2020 at 16:13
-
2\$\begingroup\$ @DarrenH I think you meant 3/2/2020. Those dates in 2019 fell on different days, but they're both Mondays in 2020. So Err should be the output for 3/2/2019, but Mon should be the output for 3/2/2020. \$\endgroup\$Mitchell Spector– Mitchell Spector2020年03月19日 18:22:19 +00:00Commented Mar 19, 2020 at 18:22
-
1\$\begingroup\$ @MitchellSpector yes I did mean 2020, years are flying by I forgot we are in another one already, comes with old age \$\endgroup\$Darren H– Darren H2020年03月20日 07:32:39 +00:00Commented Mar 20, 2020 at 7:32
13 Answers 13
Bash + GNU utilities, (削除) 97 (削除ここまで) (削除) 96 (削除ここまで) (削除) 93 (削除ここまで) (削除) 85 (削除ここまで) (削除) 80 (削除ここまで) (削除) 76 (削除ここまで) (削除) 73 (削除ここまで) (削除) 58 (削除ここまで) (削除) 56 (削除ここまで) 51 bytes
(${d=date +%a -d3ドル-}1ドル-2ドル;$d2ドル-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.)
-
\$\begingroup\$ small improvement
d=date/+%a/-d IFS=/\$\endgroup\$Nahuel Fouilleul– Nahuel Fouilleul2020年03月19日 12:50:20 +00:00Commented Mar 19, 2020 at 12:50 -
\$\begingroup\$ @NahuelFouilleul Very nice -- thank you! \$\endgroup\$Mitchell Spector– Mitchell Spector2020年03月19日 13:20:45 +00:00Commented Mar 19, 2020 at 13:20
-
\$\begingroup\$ 56 bytes \$\endgroup\$Nahuel Fouilleul– Nahuel Fouilleul2020年03月19日 17:25:29 +00:00Commented Mar 19, 2020 at 17:25
-
\$\begingroup\$ @NahuelFouilleul Thank you again. I didn't know that bash construct -- the man entry I have shows the
${var:=...}construct but not${var=...}, unless I'm just missing it. \$\endgroup\$Mitchell Spector– Mitchell Spector2020年03月19日 18:15:18 +00:00Commented Mar 19, 2020 at 18:15 -
\$\begingroup\$ seems undocumented the difference between two is when the parameter is set to null example \$\endgroup\$Nahuel Fouilleul– Nahuel Fouilleul2020年03月19日 19:42:19 +00:00Commented Mar 19, 2020 at 19:42
05AB1E, (削除) 88 (削除ここまで) (削除) 82 (削除ここまで) (削除) 79 (削除ここまで) (削除) 76 (削除ここまで) (削除) 79 (削除ここまで) (削除) 73 (削除ここまで) 72 bytes
#ÂÀ‚Dε`UÐ3‹12*+>2*T÷s3‹Xα¬Ésт%D4÷O.•A±1γβ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 2 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
2* # 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±1γβ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±1γβCüIÊä6’C• is "satsunmontuewedthufri".
-
\$\begingroup\$ "Look mah" would look less "mah" as "Looks meh" :D I would edit it in, but I'm not sure if the "mah" is the "Dutch version" of "meh" :) \$\endgroup\$RGS– RGS2020年03月19日 09:59:48 +00:00Commented Mar 19, 2020 at 9:59
-
1\$\begingroup\$ You can probably get rid of \$J\$ since we're only asked to support dates from 2000 through 2030. \$\endgroup\$Arnauld– Arnauld2020年03月19日 10:14:20 +00:00Commented Mar 19, 2020 at 10:14
-
\$\begingroup\$ @Arnauld Thanks, -6 bytes by hard-coding \$+\left\lfloor {\frac {J}{4}}\right\rfloor -2J\$ to \$-35\$. :) \$\endgroup\$Kevin Cruijssen– Kevin Cruijssen2020年03月19日 11:48:20 +00:00Commented Mar 19, 2020 at 11:48
-
1\$\begingroup\$ My bad, my previous comment was wrong, I deleted it.
#Ð2£TSǝ=>#ÂÀfor -4. Rotate the list of days back to what it was so you can delete the -36 (-35 is a noop mod 7). \$\endgroup\$Grimmy– Grimmy2020年03月19日 22:55:10 +00:00Commented Mar 19, 2020 at 22:55 -
1\$\begingroup\$
13*5÷=>2*T÷for -1. \$\endgroup\$Grimmy– Grimmy2020年03月20日 11:02:00 +00:00Commented Mar 20, 2020 at 11:02
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
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.
-
2\$\begingroup\$ You can save a byte by changing
'Errto123(or any other 3-digit number). \$\endgroup\$Kevin Cruijssen– Kevin Cruijssen2020年03月19日 12:51:34 +00:00Commented Mar 19, 2020 at 12:51 -
\$\begingroup\$ @Kevin Cruijssen Thanks, I always forget that. \$\endgroup\$Galen Ivanov– Galen Ivanov2020年03月19日 13:04:45 +00:00Commented Mar 19, 2020 at 13:04
-
2\$\begingroup\$ Another -1 thanks to @Arnauld, by using
.1instead of911, which will automatically become0.1. \$\endgroup\$Kevin Cruijssen– Kevin Cruijssen2020年03月19日 13:24:08 +00:00Commented Mar 19, 2020 at 13:24 -
\$\begingroup\$ @KevinCruijssen Of course! That's nice. \$\endgroup\$Galen Ivanov– Galen Ivanov2020年03月19日 13:31:21 +00:00Commented Mar 19, 2020 at 13:31
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.
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).
-
\$\begingroup\$ Save two bytes by replacing
d(@F)eq d(@G)withd@F,eq d@G,\$\endgroup\$Abigail– Abigail2020年03月19日 09:03:00 +00:00Commented Mar 19, 2020 at 9:03
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
-
\$\begingroup\$ Why pre-release? Python 3.8 is released. \$\endgroup\$Tyilo– Tyilo2020年03月19日 12:26:36 +00:00Commented Mar 19, 2020 at 12:26
-
\$\begingroup\$ @Tyilo Just because TIO calls it that. Hand edited to
Python. \$\endgroup\$Noodle9– Noodle92020年03月19日 12:36:19 +00:00Commented Mar 19, 2020 at 12:36 -
1\$\begingroup\$ You can save 2 bytes by changing
"Err"to-99(or any other negative 2-digit number). \$\endgroup\$Kevin Cruijssen– Kevin Cruijssen2020年03月19日 12:50:22 +00:00Commented Mar 19, 2020 at 12:50 -
1\$\begingroup\$ @KevinCruijssen
.1(rendered as0.1) would be even shorter. \$\endgroup\$Arnauld– Arnauld2020年03月19日 13:22:39 +00:00Commented Mar 19, 2020 at 13:22 -
\$\begingroup\$
except:0saves 3 bytes \$\endgroup\$Surculose Sputum– Surculose Sputum2020年03月19日 14:08:13 +00:00Commented Mar 19, 2020 at 14:08
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.
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.
-
\$\begingroup\$ This has no effect on bytecount now that flags don't count towards that, but why not
-pF/and forgo the quotes? \$\endgroup\$Value Ink– Value Ink2020年03月21日 01:12:35 +00:00Commented Mar 21, 2020 at 1:12 -
\$\begingroup\$ It could have been. I'm just so in the mindset of quoting certain things, I did it out of habit. \$\endgroup\$Xcali– Xcali2020年03月21日 04:54:59 +00:00Commented Mar 21, 2020 at 4:54
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.
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")
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
Explore related questions
See similar questions with these tags.