Короткая дата на английский Длинная дата

14

Преобразуйте формат короткой даты в английскую длинную дату как можно меньше байтов.

вход

Ввод будет в форме строки с форматом yyyy-mm-dd, с нулевым заполнением необязательно для всех значений. Вы можете предположить, что это синтаксически правильно, но не обязательно является действительной датой. Отрицательные значения года не должны поддерживаться.

Выход

Вы должны преобразовать дату в английский длинный формат даты (например 14th February 2017). Нулевое заполнение здесь не допускается.

Если дата недействительна (например 2011-02-29), то это должно быть каким-то образом распознано. Бросать исключение разрешено.

Больше примеров можно увидеть ниже.

Тестовые случаи

"1980-05-12" -> 12th May 1980
"2005-12-3"  -> 3rd December 2005
"150-4-21"   -> 21st April 150
"2011-2-29"  -> (error/invalid)
"1999-10-35" -> (error/invalid)
GarethPW
источник
5
Допускается ли заполнение нулями? aka 03rdвместо3rd
Value Ink
@ValueInk Если вы читаете мой предыдущий комментарий, игнорируйте его; Я неправильно понял вопрос. Заполнение нулями на выходе не допускается.
GarethPW
Должны ли мы рассматривать год для более чем 4 символов (например, 10987-01-01)?
mdahmoune
@mdahmoune Вам не нужно поддерживать это, если это не легче сделать.
GarethPW
Как насчет 2016-2-29?
Оливье Грегуар

Ответы:

5

PostgreSQL, 61 символ

prepare f(date)as select to_char($1,'fmDDth fmMonth fmYYYY');

Подготовленный оператор, принимает входные данные в качестве параметра.

Образец прогона:

Tuples only is on.
Output format is unaligned.
psql (9.6.3, server 9.4.8)
Type "help" for help.

psql=# prepare f(date)as select to_char($1,'fmDDth fmMonth fmYYYY');
PREPARE

psql=# execute f('1980-05-12');
12th May 1980

psql=# execute f('2005-12-3');
3rd December 2005

psql=# execute f('150-4-21');
21st April 150

psql=# execute f('2011-2-29');
ERROR:  date/time field value out of range: "2011-2-29"
LINE 1: execute f('2011-2-29');
                  ^
psql=# execute f('1999-10-35');
ERROR:  date/time field value out of range: "1999-10-35"
LINE 1: execute f('1999-10-35');
                  ^
HINT:  Perhaps you need a different "datestyle" setting.
manatwork
источник
Отлично, хотелось бы, чтобы MS-SQL распознал «й» стиль форматирования.
BradC
Учитывая, что вам удалось правильно выполнить задачу с наименьшим количеством байтов, я полагаю, ваше решение - победитель!
GarethPW
Вау. Благодарю. Это совершенно неожиданно. До сих пор не заметил отсутствия выделенных языков для игры в гольф. Тем не менее, принятие решения всего через один день немного скоро.
Манатворк
@ manatwork Мне интересно, может быть, это немного рано? Но я могу изменить это в случае необходимости.
GarethPW
7

Python 3.6, 137 129 байт

from datetime import*
def f(k):g=[*map(int,k.split('-'))];n=g[2];return f"{date(*g):%-d{'tsnrhtdd'[n%5*(n^15>4>n%10)::4]} %B %Y}"

Попробуйте онлайн!

Уриэль
источник
3
%-dэто версия без отступов, %dкоторую вы можете использовать при форматировании строки вместо {g[2]}. Кроме того, 12должно стать 12th, а не 12nd(числа от 10-19 не следуют тем же правилам, что и 1-9 и 20+)
Value Ink
1
+1. не знал о fструнах
Фелипе Нарди Батиста,
@ValueInk спасибо! также исправлена ​​проблема с порядковыми
Уриэль
5

JavaScript (ES6), 142 140 байт

Выходы NaNth Invalid Dateдля недействительных дат.

Код для порядковых чисел был адаптирован из этого ответа .

d=>`${s=(D=new Date(d)).getDate()+''}${[,'st','nd','rd'][s.match`1?.$`]||'th'} `+D.toLocaleDateString('en-GB',{month:'long',year:'numeric'})

darrylyeo
источник
1
Дает "1 марта 2011" на 2011-2-29 в Chrome. Это может быть трудным решением.
Рик Хичкок
5

Python 3.6 , 154 байта

from datetime import*
s=[*map(int,input().split('-'))]
b=s[2]
print(date(*s).strftime(f"%-d{'th'if(3<b<21)+(23<b<31)else('st','nd','rd')[b%10-1]} %B %Y"))

Попробуйте онлайн! (Установите поток ввода и затем запустите.)

Благодаря хорошим предложениям от комментаторов ниже.

Люк Соучак
источник
Вы можете сохранить байт, удалив пробел между int(x)и forв вашем списке комп.
Кристиан Дин
@ChristianDean Спасибо, готово!
Люк Сончак
(('st','nd','rd')[b%10-1]if b<4 or 20<b<24 else'th')вместо вашего текущего условного для -3 байта.
Value Ink
@ValueInk К сожалению, это будет 31-е. Еще один способ разбить его - «th», если не 0 <b% 10 <4 или 10 <b <14, но это не спасло ни одного байта.
Люк Сончак
В таком случае злоупотребляйте типом принуждения. (3<b<21)+(23<b<31)за -1 байт. Попробуйте онлайн!
Чернила стоимости
5

PHP, 87 байт

<?=checkdate(($a=explode("-",$argn))[1],$a[2],$a[0])?date("jS F Y",strtotime($argn)):E;

Запустите как трубу с -Fили проверьте это онлайн . Всегда печатает четырехзначный год; терпит неудачу в течение многих лет> 9999.

нет проверки достоверности, 35 байт:

<?=date("jS F Y",strtotime($argn));
Titus
источник
5

Bash + coreutils, 115 78

  • 2 байта сохранены благодаря @manatwork.
d="date -d$1 +%-e"
t=`$d`
f=thstndrd
$d"${f:t/10-1?t%10<4?t%10*2:0:0:2} %B %Y"

Попробуйте онлайн .

Цифровая травма
источник
1
Кажется , что с помощью строки вместо массива будет немного помочь: f=thstndrd; $d"${f:t/10-1?t%10<4?t%10*2:0:0:2} %B %Y".
Манатворк
1
Кстати, ваша ревизия 1 вдохновила Bash tip . ;)
manatwork
@manatwork да - это смешно - я подумал попробовать, но не думал, что это поможет. Спасибо за толчок.
Цифровая травма
4

C #, 147 143 байта

s=>{var t=System.DateTime.Parse(s);int d=t.Day,o=d%10;return d+((d/10)%10==1?"th":o==1?"st":o==2?"nd":o==3?"rd":"th")+t.ToString(" MMMM yyy");}

Сохранено 4 байта благодаря @The_Lone_Devil.

TheLethalCoder
источник
Не могли бы вы не заменить второй t.Dayс dдля экономии 4 байта?
The_Lone_Devil
@The_Lone_Devil Конечно, спасибо, не знаю, как я это пропустил.
TheLethalCoder
4

версия mIRC 7.49 (197 байт)

//tokenize 45 2-2-2 | say $iif($3 isnum 1- $iif($2 = 2,$iif(4 // $1 && 25 \\ $1||16//$1,29,28),$iif($or($2,6) isin 615,30,31))&&$2 isnum1-12&&1//$1,$asctime($ctime($+($1,-,$2,-,$3)date), doo mmmm yyyy))
Операционные системы
источник
3

Рубин , 104 103 102 + 8 = 112 111 110 байт

Использует -rdate -pфлаги программы.

-1 байт от manatwork.

sub(/.*-(\d*)/){Date.parse($&).strftime"%-d#{d=eval$1;(d<4||d>20)&&"..stndrd"[d%10*2,2]||:th} %B %-Y"}

Попробуйте онлайн!

Значение чернил
источник
Я пропускаю причину, по которой вы не использовали троичный оператор? d<4||d>20?"..stndrd"[d%10*2,2]:"th"
Манатворк
@manatwork Число, подобное 26будет пытаться получить доступ к индексам 12..13в строке поиска, которая находится за пределами, и, следовательно, возвращает nil. Таким образом, использование троичного делает его d<4||d>20?"..stndrd"[d%10*2,2]||"th":"th"длиннее на 2 байта.
Стоимость чернил
Ах я вижу. Ну, тогда крутой трюк @ValueInk.
manatwork
Почти забыл, небольшое изменение: "th":th.
Манатворк
2

C # (.NET Core) , 167 197 байт

s=>s.Equals(DateTime.MinValue)?"":s.Day+((s.Day%10==1&s.Day!=11)?"st":(s.Day%10==2&s.Day!=12)?"nd":(s.Day%10==3&s.Day!=13)?"rd":"th")+" "+s.ToString("MMMM")+" "+s.Year

Попробуйте онлайн!

+30 байт для

using System;

DateTime.Parse()

kakkarot
источник
Вы можете отменить троичную проверку, чтобы избавиться от !-1 байта. И вы можете изменить &&в &течение -3 байт. Кроме того, поскольку вы используете s.Day7 раз, он экономит несколько байтов, чтобы создать для него временное значение:s=>{var t=s.Day;return s.Equals(DateTime.MinValue)?"":t+((t%10==1&t!=11)?"st":(t%10==2&t!=12)?"nd":(t%10==3&t!=13)?"rd":"th")+" "+s.ToString("MMMM")+" "+s.Year;}
Кевин Круйссен,
@KevinCruijssen Спасибо!
Каккарот
Вы также должны включить using System;или полностью квалифицировать DateTimeобъект.
TheLethalCoder
Кроме того, DateTime.MinValueэто , 1-1-1так что я не думаю , что вам нужно это проверить. Что также сделало бы мой предыдущий пункт неуместным.
TheLethalCoder
1
Принимать входные данные как a DateTimeи анализ вне метода недопустимо, вы должны делать всю работу внутри метода. Или добавьте дополнительный метод для разделения работы.
TheLethalCoder
2

Excel, 212 байт

=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)

Если вы разбиваете его на куски на каждом амперсанде, вы получаете эти кусочки:

  • ABS()извлекает номер дня из двух последних символов в строке. Поскольку это может включать дефис, ABSпреобразует его в положительный.
  • IF((ABS-12)<2,"th",SWITCH())добавляет порядковый номер. -12Бит , потому что 11, 12 и 13 не следуют нормальному правилу , и все они получают thвместо st, ndи rd. Это исправляет для этого.
    • Примечание. SWITCHФункция доступна только в Excel 2016 и более поздних версиях. ( Источник ) Он короче, чем CHOOSEв этом случае, потому что он может возвращать значение, если совпадение не найдено, тогда как CHOOSEтребует числового ввода и должен иметь соответствующий возврат для каждого возможного значения.
  • TEXT(MID()*30," mmmm ")извлекает название месяца. MID()вытаскивает номер месяца в виде строки, а умножение на 30 возвращает число. Excel видит это число как дату (1900-01-30, 1900-02-29, 1900-03-30 и т. Д.) ИTEXT() форматирует его как название месяца с пробелом на обоих концах. 28 и 29 тоже бы работали, но 30 выглядит "лучше".
  • LEFT() извлекает номер года.

Теперь, учитывая все это, было бы намного проще, если бы все контрольные примеры были в диапазоне дат, который Excel может обработать как фактическую дату: с 1900-01-01 по 9999-12-31. Большим преимуществом является то, что вся дата форматируется сразу. Это решение составляет 133 байта :

=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")

Другим большим препятствием было включение порядкового номера. Без этого решение будет всего 34 байта :

=TEXT(DATEVALUE(A1),"d mmmm yyyy")
Инженер Тост
источник
1

Swift 3: 298 байт

let d=DateFormatter()
d.dateFormat="yyyy-MM-dd"
if let m=d.date(from:"1999-10-3"){let n=NumberFormatter()
n.numberStyle = .ordinal
let s=n.string(from:NSNumber(value:Calendar.current.component(.day, from:m)))
d.dateFormat="MMMM YYY"
print("\(s!) \(d.string(from:m))")}else{print("(error/invalid)")}

Попробуйте онлайн!

А. Пуджа
источник
8
Добро пожаловать на сайт! Здесь цель состоит в том, чтобы сделать код максимально коротким, я вижу, что у вас длинные имена переменных и много пробелов, вы можете сократить и удалить их, чтобы сэкономить много байтов. Мы также обычно добавляем заголовок вверху ответа в форме # Language, N bytes. Было бы хорошо, если бы вы могли добавить еще один.
TheLethalCoder
1

T-SQL, 194 байта

DECLARE @ DATE;SELECT @=PARSE('00'+i AS DATE)FROM t;PRINT DATENAME(d,@)+CASE WHEN DAY(@)IN(1,21,31)THEN'st'WHEN DAY(@)IN(2,22)THEN'nd'WHEN DAY(@)IN(3,23)THEN'rd'ELSE'th'END+FORMAT(@,' MMMM yyy')

Ввод осуществляется с текстовой колонке I в уже существующей таблице т , в соответствии с нашими стандартами IO .

Работает для дат с 1 января 0001 по 31 декабря 9999 года. Год выводится не менее чем из 3 цифр (на пример 150AD).

Неверные даты приведут к следующей ужасной ошибке:

Error converting string value 'foo' into data type date using culture ''.

Различные настройки языка / культуры по умолчанию могут изменить это поведение. Если вы хотите немного более изящный вывод ошибок (NULL), добавьте 4 байта, изменив PARSE()на TRY_PARSE().

Формат и объяснение:

DECLARE @ DATE;
SELECT @=PARSE('00'+i AS DATE)FROM t;
PRINT DATENAME(d,@) + 
    CASE WHEN DAY(@) IN (1,21,31) THEN 'st'
         WHEN DAY(@) IN (2,22)    THEN 'nd'
         WHEN DAY(@) IN (3,23)    THEN 'rd'
         ELSE 'th' END
    + FORMAT(@, ' MMMM yyy')

Тип DATEданных, введенный в SQL 2008, предоставляет гораздо более широкий диапазон, чем DATETIMEс 1 января 0001 года по 31 декабря 9999 года.

Некоторые очень ранние даты могут быть неправильно проанализированы в моих настройках местности в США («01-02-03» становится «2 января 2003»), поэтому я заранее добавил пару дополнительных нулей, чтобы он знал, что первое значение - это год.

После этого это просто грязное CASEзаявление, чтобы добавить порядковый суффикс в день. Досадно, что FORMATкоманда SQL не может сделать это автоматически.

BradC
источник
1

q / kdb + 210 байт, не конкурирует

Решение:

f:{a:"I"$"-"vs x;if[(12<a 1)|31<d:a 2;:0];" "sv(raze($)d,$[d in 1 21 31;`st;d in 2 22;`nd;d in 3 23;`rd;`th];$:[``January`February`March`April`May`June`July`August`September`October`November`December]a 1;($)a 0)};

Примеры:

q)f "2017-08-03"
"3rd August 2017"
q)f "1980-05-12"
"12th May 1980"
q)f "2005-12-3"
"3rd December 2005"
q)f "150-4-21" 
"21st April 150"
q)f "2011-2-29"       / yes it's wrong :(
"29th February 2011"
q)f "1999-10-35"
0

Объяснение:

Это ужасная проблема, так как нет форматирования даты, поэтому мне нужно создавать месяцы с нуля (95 байт), а также генерировать суффикс.

Раскрытое решение ниже, в основном разбивает входную строку и затем объединяет ее после того, как мы добавили суффикс и переключили месяц.

f:{
   // split input on "-", cast to integers, save as variable a
   a:"I"$ "-" vs x;
   // if a[1] (month) > 12 or a[2] (day) > 31 return 0; note: save day in variable d for later
   if[(12<a 1) | 31<d:a 2;
     :0];
   // joins the list on " " (like " ".join(...) in python)
   " " sv (
           // the day with suffix
           raze string d,$[d in 1 21 31;`st;d in 2 22;`nd;d in 3 23;`rd;`th];
           // index into a of months, start with 0 as null, to mimic 1-indexing
           string[``January`February`March`April`May`June`July`August`September`October`November`December]a 1;
           // the year cast back to a string (removes any leading zeroes)
           string a 0)
  };

Примечания:

Даты в q возвращаются только к ~ 1709, поэтому у меня нет тривиального способа проверки даты, поэтому это неконкурентная запись ... Лучшее, что я могу сделать, это проверить, является ли день> 31 или месяц > 12 и вернуть 0.

streetster
источник