Excel, 212 bytes
=ABS(RIGHT(A1,2))&IF(ABS(ABS(RIGHT(A1,2))-12)<2,"th",SWITCH(RIGHT(A1,1),"1","st","2","nd","3","rd","th"))&TEXT(MID(A1,FIND("-",A1)+1,FIND("-",REPLACE(A1,1,FIND("-",A1),""))-1)*30," mmmm ")&LEFT(A1,FIND("-",A1)-1)
If you break it into chunks at every ampersand, you get these pieces:
ABS()
pulls the day number from the last two characters in the string. Since that may include a hyphen, ABS
converts it to positive.
IF((ABS-12)<2,"th",SWITCH())
adds the ordinal. The -12
bit is because 11, 12, and 13 don't follow the normal rule and they all get th
instead of st
, nd
, and rd
. This corrects for that.
- Note: The
SWITCH
function is only available in Excel 2016 and later. (Source) It's shorter than CHOOSE
in this case because it can return a value if no match is found whereas CHOOSE
requires numeric input and must have a corresponding return for each possible value.
TEXT(MID()*30," mmmm ")
extracts the month name. MID()
pulls out the month number as a string and multiplying by 30 returns a number. Excel sees that number as a date (1900-01-30, 1900-02-29, 1900-03-30, etc.) and TEXT()
formats it as a month name with a space on both ends. 28 and 29 would have also works but 30 looks "nicer".
LEFT()
extracts the year number.
Now, given all that, it would have been way easier if the test cases were all in a date range that Excel can handle as an actual date: 1900-01-01 to 9999-12-31. The big advantage is that the entire date is formatted at once. That solution is 133 bytes:
=TEXT(DATEVALUE(A1),"d""" & IF(ABS(ABS(RIGHT(A1,2))-12)<2,"th",SWITCH(RIGHT(A1,1),"1","st","2","nd","3","rd","th")) & """ mmmm yyyy")
The other big hurdle was having to include the ordinal. Without that, the solution is just 34 bytes:
=TEXT(DATEVALUE(A1),"d mmmm yyyy")
5Is zero-padding allowed? aka
03rd
instead of3rd
– Value Ink – 2017-06-29T02:00:09.307@ValueInk If you read my previous comment, ignore it; I misunderstood the question. Zero padding in the output is not allowed. – GarethPW – 2017-06-29T08:27:21.920
Should we consider a year on more than 4 characters (eg 10987-01-01)? – mdahmoune – 2017-06-29T09:39:28.337
@mdahmoune You don't need to support this unless it's easier to do so. – GarethPW – 2017-06-29T10:25:26.817
What about
2016-2-29
? – Olivier Grégoire – 2017-06-29T14:12:05.857@OlivierGrégoire That date would be valid. – GarethPW – 2017-06-29T14:13:16.313
You say we don't have to support negative year values, does that imply we have to support starting at year 1? (There was no year 0). Or just back to year 150, per your earliest example? Any max year? Up to 9999 ok? – BradC – 2017-06-29T14:39:10.723
And we shouldn't 0-pad the year either? So output "21 April 3" for
0003-04-03
? – BradC – 2017-06-29T14:46:41.373@BradC I would say don't zero pad the year and have it support the range, 0 - 9999. – GarethPW – 2017-06-29T19:01:59.127
Actually there is no year zero, 31st Dec 1 BC is followed immediately by 1st Jan 1 AD. Also, there are several existing answers that pad the year already, so might be better to leave that unspecified. – BradC – 2017-06-29T19:23:28.293
@BradC TIL! In that case, it's optional. But I think it's better to enforce a lack of padding as I did include it in the examples and state it in the output description. – GarethPW – 2017-06-29T23:27:12.393
Does it have to work independently of the language setting of the OS? (My german Windows would use german names unless I force powershell to do otherwise) – whatever – 2017-06-30T07:12:38.873
@whatever No. As long as the code produces the desired result on English machines, you're fine. – GarethPW – 2017-06-30T09:54:57.940