10
\$\begingroup\$

Given a date written in any (must handle all in the same program) of the following formats, parse it into a valid yyyy/mm/dd date.

17th May 2012
March 14th, 2016
20 February 2014
September 14, 2017
Sunday, June 8, 2015

Rules

  • Dates will sometimes be invalid, ie. incorrect day for the month or number of months in a year, you must handle both cases. Either by erroring out or returning a consistent falsey value, you choose. (They will however stick to the template formats above)
  • Padding for days and months less than 10 must be used to create a two digit output.
  • Month names will always be the full name, not shortened to their three character counterparts.
  • You can assume the year will always be within the 0000-9999 range.
  • Negative numbers need not be handled.
  • You can create a full program or function so output can be in any format, printed to console or returned from a function.
  • Input will always be a string, output must always be a string, if it makes it shorter to take it as a single argument in an array eg. ["17th May 2012"] you may do so and output can be the same ["2012/05/17"]
  • You can assume spelling in input will be correct.

BONUS: cos who here doesnt like a challenge ;)

If you can manage to also allow the input formats of The Fourteenth of March, 2016 or March the Fourteenth, 2016 you may take an extra 20 bytes off your code with any final byte counts less than 1 resulting in 1.

Here are the full written numbers for each of the days to avoid any confusion on spelling.

First, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Nineth, Tenth, Eleventh, Twelfth, Thirteenth, Fourteenth, Fifteenth, Sixteenth, Seventeenth, Eighteenth, Nineteenth, Twentieth, Twenty First, Twenty Second, Twenty Third, Twenty Fourth, Twenty Fifth, Twenty Sixth, Twenty Seventh, Twenty Eighth, Twenty Nineth, Thirtieth, Thirty First

Test Cases

INPUT                           | Output
17th May 2012                   | 2012/05/17
March 14th, 2016                | 2016/03/14
20 February 2014                | 2014/02/20
September 14, 2017              | 2017/09/14
Sunday, June 8, 2015            | 2015/06/08
1st January 1918                | 1918/01/01

The Fourteenth of March, 2016   | 2016/03/14
March the Fourteenth, 2016      | 2016/03/14
November the Seventeenth, 2019  | 2019/11/17
The Thirtieth of April, 2016    | 2016/04/30

30 February 2014                | Invalid
September 99, 2017              | Invalid
Sunday, June8, 2015             | Invalid

The Thirty First of April, 2016 | Invalid
\$\endgroup\$
  • 12
    \$\begingroup\$ 0/10, not freeform enough \$\endgroup\$ – Martin Ender Aug 25 '17 at 13:46
  • 10
    \$\begingroup\$ "cos who here doesnt like a challenge" - when they come in the form of a bonus, mostly all of us don't! And boo-urns to input validation. \$\endgroup\$ – Shaggy Aug 25 '17 at 13:50
  • 3
    \$\begingroup\$ @Mr.Xcoder I think the entire challenge is to support multiple input formats.. Things like 20 February 2014 are default supported in a lot of date-parsers, but 17th and Sunday, June 8, 2015 are a bit more difficult to parse (depending on the language). \$\endgroup\$ – Kevin Cruijssen Aug 25 '17 at 13:58
  • 1
    \$\begingroup\$ @Mr.Xcoder I never said it was a fun/boring, easy/hard, good/bad code-golf challenge. I'm simply stating that based on what I read in the challenge, the multiple input-format supporting seems to be the main goal of the challenge. I agree it will most likely be an if-else for the five input-formats, or some kind of regex to extract the year, month and day (which is currently my approach). \$\endgroup\$ – Kevin Cruijssen Aug 25 '17 at 14:04
  • 3
    \$\begingroup\$ Is the day of the week always correct? Would Thursday, August 25, 2017 be valid? I see submissions ignoring the DOW. \$\endgroup\$ – Benjamin Cuningham Aug 25 '17 at 16:19
4
\$\begingroup\$

Rails, 41, 37 35 bytes

->x{x.to_date.strftime('%Y/%m/%d')}

I don't know of an online interpreter for Rails, but here is a screenshot demonstrating this proc

enter image description here

\$\endgroup\$
  • \$\begingroup\$ Online interpreter link? \$\endgroup\$ – Jonathan Allan Aug 25 '17 at 18:50
  • 1
    \$\begingroup\$ Does this really work for all the formats in question? :o \$\endgroup\$ – totallyhuman Aug 25 '17 at 19:40
  • \$\begingroup\$ @totallyhuman Added a screenshot showing the results. Unfortunately I don't know of an online interpreter :( \$\endgroup\$ – Suever Aug 25 '17 at 20:09
8
\$\begingroup\$

PowerShell, 91 89 91 56 bytes

date("$args"-replace'th|rd|st|(\b.*)day|,')-f yyyy/MM/dd

Try it online!

Takes input as a string. Uses a -replace to get rid of junk, then uses the built-in Get-Date command with the -format flag to specify the required yyyy/MM/dd format. That string is left on the pipeline and output is implicit.

Saved two bytes thanks to Mr Xcoder. Saved a huge chunk thanks to TessellatingHeckler's regex golfing.

\$\endgroup\$
  • 1
    \$\begingroup\$ Hey look competitive PowerShell submission! \$\endgroup\$ – Erik the Outgolfer Aug 25 '17 at 14:05
  • \$\begingroup\$ @EriktheOutgolfer Date manipulation is one of PowerShell's strengths. \$\endgroup\$ – AdmBorkBork Aug 25 '17 at 14:06
  • 1
    \$\begingroup\$ Why do you have all those extraneous spaces? \$\endgroup\$ – Mr. Xcoder Aug 25 '17 at 14:06
  • \$\begingroup\$ @Mr.Xcoder "all those" = 2. lol. Thanks! \$\endgroup\$ – AdmBorkBork Aug 25 '17 at 14:10
  • \$\begingroup\$ You can remove the '' from the date format, for -2. If you want to stick to your approach, given the "spelling is correct" then you can replace 'th|rd|st|(\b.*)day|,' and it drops to 56 bytes and handles the same cases. Although I think it's wrong because if you drop the days, you can't pick up the Sunday, June8, 2015 | Invalid case because it was Monday. It should error, but it parses as valid. Not sure what the ruling on that will be, or if others are checking it. \$\endgroup\$ – TessellatingHeckler Aug 26 '17 at 9:28
4
\$\begingroup\$

PHP, 73 164+1 bytes

for(preg_match("#(\d+)[^\d]+(\d+)#",$d=$argn,$r);$m++<12;)strpos(_.$d,date(F,strtotime($r[2].-$m)))&&printf(checkdate($m,$r[1],$r[2])?"$r[2]/%02d/%02d":E,$m,$r[1]);

Run as pipe with -nR or try it online.

The date check was really expensive: I had to disassemble the date before using a builtin, then try and error on the month name.

\$\endgroup\$
  • \$\begingroup\$ I think you have to support all formats not just one of them? \$\endgroup\$ – Erik the Outgolfer Aug 25 '17 at 14:08
  • 1
    \$\begingroup\$ @EriktheOutgolfer it does. But not the bonus formats. \$\endgroup\$ – Titus Aug 25 '17 at 14:09
  • \$\begingroup\$ @EriktheOutgolfer This does handle all formats. \$\endgroup\$ – Mr. Xcoder Aug 25 '17 at 14:09
  • 1
    \$\begingroup\$ This fails for invalid dates. 30 February 2014 returns 2014/03/02. Either by erroring out or returning a consistent falsey value, you choose. (They will however stick to the template formats above) \$\endgroup\$ – Mr. Xcoder Aug 25 '17 at 14:10
  • \$\begingroup\$ @Mr.Xcoder That quite ruins it ... \$\endgroup\$ – Titus Aug 25 '17 at 14:12
3
\$\begingroup\$

Python 3 + parsedatetime library, 152 139 155 153 bytes

Saved 13 bytes thanks to Jonathan Allan

Added 16 bytes to handle dates with invalid length days

Saved 2 bytes by removing lambda assignment

lambda s:re.search(f'(^| ){str(h(s)[0].tm_mday)[:2]}[^\d]',s)and time.strftime('%Y/%m/%d',h(s)[0])
import parsedatetime as p,time,re
h=p.Calendar().parse

Try it online!

Does not support bonus dates

\$\endgroup\$
  • \$\begingroup\$ Welcome to PPCG! Nice first post. Save 13 bytes by using: the falsey return value of None; import ...as; & a lambda by reusing parse as h. \$\endgroup\$ – Jonathan Allan Aug 25 '17 at 21:03
1
\$\begingroup\$

Python 2, 193 bytes

lambda s:time.strftime('%Y/%m/%d',time.strptime(re.sub(r'^(\w+) (\d+)',r'\2 \1',re.sub('^ ','',re.sub('(th|rd|st)|(Sun|Mon|Tues|Wednes|Thurs|Fri|Satur)day|,','',s))),'%d %B %Y'))
import re,time

Try it online!

;-; pls halp

\$\endgroup\$
1
\$\begingroup\$

Java (OpenJDK 8), 190 + 26 = 216 bytes

import java.time.format.*;

s->DateTimeFormatter.ofPattern("uuuu/MM/dd").format(DateTimeFormatter.ofPattern("[EEEE, ][d ]MMMM [d, ]uuuu").withResolverStyle(ResolverStyle.STRICT).parse(s.replaceAll("(\\d)[a-z].","$1")))

Try it online!

Important note: it was shorter to also validate the day of week instead of ditching it, so that validation is included!

I haven't tried with SimpleDateFormat beyond the obvious cases which all accepted dates like 30 February. So I had to ditch it and I used Java 8's DateTimeFormatter.

Explanation

"[EEEE, ][d ]MMMM [d, ]uuuu"

This format means :

  • optional day-of-week followed by comma and space [EEEE, ] (happens in Sunday, ...),
  • followed by optional day with space [d ],
  • followed by month in full letters MMMM and space,
  • followed by optional day with comma and space [d, ],
  • followed by year of era uuuu to let the parser know we're in Gregorian era.

Code:

import java.time.format.*;                                     // Required for DateTimeFormatter, *and* ResolverStyle

s->DateTimeFormatter.ofPattern("uuuu/MM/dd")                   // Output format
  .format(
    DateTimeFormatter.ofPattern("[EEEE, ][d ]MMMM [d, ]uuuu")  // Input format
      .withResolverStyle(ResolverStyle.STRICT)                 // Invalidates xxxx-02-30 instead of transforming it into xxxx-02-28
      .parse(
        s.replaceAll("(\\d)[a-z].","$1")                       // remove st, nd, rd, th
       )
    )

Credits

  • 2 bytes in the regex thanks to Neil.
\$\endgroup\$
  • 1
    \$\begingroup\$ Do you need the brackets in the replaceAll pattern? \$\endgroup\$ – Neil Aug 26 '17 at 11:51
  • \$\begingroup\$ Looks like I don't. Thanks, @Neil! \$\endgroup\$ – Olivier Grégoire Aug 26 '17 at 11:55
1
\$\begingroup\$

JavaScript (ES6), 124 122 bytes

f=
s=>(d=new Date(s.replace(/.[dht]\b/,'')+' +0')).getDate()==s.match(/\d\d?/)&&d.toISOString().replace(/-(..)(T.*)?/g,'/$1')
<input oninput=o.textContent=f(this.value)><pre id=o>

\$\endgroup\$
  • \$\begingroup\$ Returns 2015/06/07 for June 8, 2015 (UTC issue? I'm in UTC+2) and false for either Sunday, June 8, 2015 or Monday, June 8, 2015. \$\endgroup\$ – Olivier Grégoire Aug 26 '17 at 12:35
  • 1
    \$\begingroup\$ @OlivierGrégoire Thanks for pointing those out. I'd only tried dates in UK outside Summer Time so they were already using UTC, and I'd not tried weekdays ending in nday. \$\endgroup\$ – Neil Aug 26 '17 at 13:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.