Data curta para a data longa em inglês

14

Converta o formato de data abreviada em data longa em inglês no menor número possível de bytes.

Entrada

A entrada será na forma de uma string com formato yyyy-mm-dd,, com preenchimento zero opcional para todos os valores. Você pode assumir que isso é sintaticamente correto, mas não necessariamente uma data válida. Os valores negativos do ano não precisam ser suportados.

Resultado

Você deve converter a data no formato de data longa em inglês (por exemplo 14th February 2017). O preenchimento zero aqui não é permitido.

Se a data for inválida (por exemplo 2011-02-29), isso deve ser reconhecido de alguma forma. Lançar uma exceção é permitido.

Mais exemplos podem ser vistos abaixo.

Casos de teste

"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
fonte
5
O preenchimento zero é permitido? aka em 03rdvez de3rd
Value Ink
@ ValueInk Se você leu meu comentário anterior, ignore-o; Eu entendi mal a pergunta. Não é permitido o preenchimento zero na saída.
GarethPW
Devemos considerar um ano com mais de 4 caracteres (por exemplo, 10987-01-01)?
Mdahmoune
@mdahmoune Você não precisa apoiar isso, a menos que seja mais fácil.
GarethPW
Que tal 2016-2-29?
Olivier Grégoire

Respostas:

5

PostgreSQL, 61 caracteres

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

Instrução preparada, recebe entrada como parâmetro.

Exemplo de execução:

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.
homem a trabalhar
fonte
Bom, gostaria que o MS-SQL reconhecesse o "th" estilo de formatação.
BradC
Considerando que você conseguiu concluir a tarefa corretamente com o menor número de bytes, suponho que sua solução seja a vencedora!
GarethPW
Uau. Obrigado. Isso é completamente inesperado. Não percebi até agora a ausência de idiomas dedicados ao golfe. No entanto, aceitar uma solução após apenas um dia é um pouco breve.
manatwork
@ manatwork Eu me perguntei se poderia ser um pouco cedo. Mas eu posso mudar, se necessário.
GarethPW
7

Python 3.6, 137 129 bytes

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}"

Experimente online!

Uriel
fonte
3
%-dé a versão sem preenchimento %dque você pode usar na formatação da string em vez de {g[2]}. Além disso, 12deve tornar-se 12th, não 12nd(os números de 10-19 não seguem as mesmas regras de 1-9 e 20+)
Valor de tinta
1
+1. não sabia sobre as fseqüências de caracteres
Felipe Nardi Batista
@ValueInk thanks! Além disso, corrigiu o problema ordinais
Uriel
5

JavaScript (ES6), 142 140 bytes

Saídas NaNth Invalid Datepara datas inválidas.

O código para números ordinais foi adaptado a partir desta resposta .

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

darrylyeo
fonte
1
Dá "1º de março de 2011" para 29/02/2011 no Chrome. Essa pode ser uma solução difícil.
Rick Hitchcock
5

Python 3.6 , 154 bytes

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

Experimente online! (Defina o fluxo de entrada e depois execute.)

Graças a boas sugestões dos comentaristas abaixo.

Luke Sawczak
fonte
Você pode salvar um byte removendo o espaço entre int(x)e forna lista comp.
Christian Dean
@ChristianDean Obrigado, pronto!
Luke Sawczak
(('st','nd','rd')[b%10-1]if b<4 or 20<b<24 else'th')em vez de seu condicional atual para -3 bytes.
Value Ink
@ValueInk Infelizmente, isso produzirá 31º. Outra maneira que pensei em dividi-lo foi 'th' se não 0 <b% 10 <4 ou 10 <b <14, mas não salvou bytes.
Luke Sawczak
Nesse caso, abuse do tipo de coerção. (3<b<21)+(23<b<31)para -1 byte. Experimente online!
Value Ink
5

PHP, 87 bytes

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

Execute como tubo -Fou teste-o online . Sempre imprime um ano de 4 dígitos; falha há anos> 9999.

sem verificação de validade, 35 bytes:

<?=date("jS F Y",strtotime($argn));
Titus
fonte
5

Bash + coreutils, 115 78

  • 2 bytes salvos graças a @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"

Experimente online .

Trauma Digital
fonte
1
Parece que o uso de cordas em vez de conjunto ajudaria um pouco: f=thstndrd; $d"${f:t/10-1?t%10<4?t%10*2:0:0:2} %B %Y".
manatwork
1
Aliás, sua revisão 1 inspirou uma dica do Bash . ;)
manatwork
@ manatwork sim - é engraçado - considerei tentar isso, mas não achei que ajudaria. Obrigado pela cutucada.
Digital Trauma
4

C #, 147 143 bytes

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");}

Guardado 4 bytes graças a @The_Lone_Devil.

TheLethalCoder
fonte
Você não poderia substituir o segundo t.Daycom duma economia de 4 byte?
The_Lone_Devil
@The_Lone_Devil É claro que eu poderia agradecer, não sei como eu perdi isso.
TheLethalCoder
4

mIRC versão 7.49 (197 bytes)

//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))
OS
fonte
3

Ruby , 104 103 102 + 8 = 112 111 110 bytes

Usa -rdate -psinalizadores de programa.

-1 byte de manatwork.

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

Experimente online!

Value Ink
fonte
Estou faltando uma razão pela qual você não usou o operador ternário? d<4||d>20?"..stndrd"[d%10*2,2]:"th"
manatwork
@manatwork Um número como 26tentará acessar índices 12..13na string de pesquisa, que está fora dos limites e, portanto, retorna nil. Assim, o uso do ternário faz com d<4||d>20?"..stndrd"[d%10*2,2]||"th":"th"que seja mais longo em 2 bytes.
Value Ink
Ah entendo. Bem, então truque legal @ValueInk.
manatwork
Quase esqueci, uma pequena mudança: "th":th.
precisa saber é
2

C # (.NET Core) , 167 197 bytes

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

Experimente online!

+30 bytes para

using System;

DateTime.Parse()

kakkarot
fonte
Você pode reverter a verificação ternária para se livrar do !by-byte de -1. E você pode alterar &&para &para -3 bytes. Além disso, uma vez que você usa s.Day7 vezes, ele salva alguns bytes para criar um valor temporário para ele: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;}
Kevin Cruijssen 29/17
@KevinCruijssen Thanks!
kakkarot
Você também precisa incluir using System;ou qualificar totalmente o DateTimeobjeto.
TheLethalCoder
Também DateTime.MinValueé 1-1-1assim que eu acho que você não precisa dessa verificação. O que também tornaria irrelevante meu argumento anterior.
TheLethalCoder
1
Tomando como entrada DateTimee analisando fora do método não é aceitável, você deve fazer todo o trabalho dentro do método. Ou adicione um método extra para dividir o trabalho.
TheLethalCoder
2

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)

Se você o dividir em pedaços a cada e comercial, você obtém as seguintes peças:

  • ABS()extrai o número do dia dos dois últimos caracteres da sequência. Como isso pode incluir um hífen, ABSconverte-o em positivo.
  • IF((ABS-12)<2,"th",SWITCH())adiciona o ordinal. O -12bit é porque 11, 12 e 13 não seguem a regra normal e todos eles recebem them vez de st, nde rd. Isso corrige isso.
    • Nota: A SWITCHfunção está disponível apenas no Excel 2016 e posterior. ( Origem ) É mais curto do que CHOOSEneste caso, pois pode retornar um valor se nenhuma correspondência for encontrada, enquanto CHOOSErequer entrada numérica e deve ter um retorno correspondente para cada valor possível.
  • TEXT(MID()*30," mmmm ")extrai o nome do mês. MID()extrai o número do mês como uma sequência e multiplicar por 30 retorna um número. O Excel vê esse número como uma data (1900-01-30, 1900-02-29, 1900-03-30, etc.) e o TEXT()formata como um nome de mês com um espaço nas duas extremidades. 28 e 29 também teriam obras, mas 30 parecem "mais agradáveis".
  • LEFT() extrai o número do ano.

Agora, considerando tudo isso, teria sido muito mais fácil se os casos de teste estivessem todos em um período que o Excel possa manipular como uma data real: 1900-01-01 a 9999-12-31. A grande vantagem é que a data inteira é formatada de uma só vez. Essa solução é 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")

O outro grande obstáculo foi ter que incluir o ordinal. Sem isso, a solução é de apenas 34 bytes :

=TEXT(DATEVALUE(A1),"d mmmm yyyy")
Engenheiro Toast
fonte
1

Swift 3: 298 bytes

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

Experimente online!

A. Pooja
fonte
8
Bem vindo ao site! Aqui, o objetivo é tornar o código o mais curto possível, posso ver que você tem nomes longos de variáveis ​​e muito espaço em branco, é possível encurtar e removê-los para economizar muitos bytes. Também geralmente incluímos um cabeçalho na parte superior da resposta na forma de # Language, N bytes. Seria bom se você pudesse adicionar um também.
TheLethalCoder
1

T-SQL, 194 bytes

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

A entrada é via coluna de texto i na tabela preexistente t , de acordo com nossos padrões de IO .

Funciona para datas de 1º de janeiro de 0001 a 31 de dezembro de 9999. O ano é produzido com pelo menos 3 dígitos (por exemplo 150AD).

Datas inválidas resultarão no seguinte erro feio:

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

Configurações de idioma / cultura padrão diferentes podem alterar esse comportamento. Se você deseja uma saída de erro ligeiramente mais elegante (NULL), adicione 4 bytes alterando PARSE()para TRY_PARSE().

Formato e explicação:

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

O DATEtipo de dados introduzido no SQL 2008 permite um intervalo muito maior do que DATETIMEentre 1º de janeiro de 0001 e 31 de dezembro de 9999.

Algumas datas muito precoces podem ser analisadas incorretamente nas minhas configurações de localidade nos EUA ("01-02-03" se torna "2 de janeiro de 2003"); portanto, eu antecipei alguns zeros extras para saber que o primeiro valor é o ano.

Depois disso, é apenas uma CASEdeclaração confusa para adicionar o sufixo ordinal ao dia. Irritantemente, o FORMATcomando SQL não tem como fazer isso automaticamente.

BradC
fonte
1

q / kdb + 210 bytes, não concorrente

Solução:

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)};

Exemplos:

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

Explicação:

Este é um desafio horrível, pois não há formatação de data, portanto, tenho que criar meses do zero (95 bytes) e gerar o sufixo.

A solução ungolfed está abaixo, basicamente divida a string de entrada e depois se junte novamente após adicionarmos o sufixo e mudar o mês.

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)
  };

Notas:

As datas em q só retornam a ~ 1709, portanto, não tenho uma maneira trivial de validar a data, portanto essa é uma entrada não concorrente ... O melhor que posso fazer é verificar se o dia é> 31 ou mês é > 12 e retorne 0.

rua
fonte