Fecha corta en inglés Fecha larga

14

Convierta el formato de fecha corta a la fecha larga en inglés en la menor cantidad de bytes posible.

Entrada

La entrada tendrá la forma de una cadena con formato yyyy-mm-dd, con cero relleno opcional para todos los valores. Puede suponer que esto es sintácticamente correcto, pero no necesariamente una fecha válida. Los valores anuales negativos no necesitan ser compatibles.

Salida

Debe convertir la fecha al formato de fecha larga en inglés (por ejemplo 14th February 2017). Cero relleno aquí no está permitido.

Si la fecha no es válida (p 2011-02-29. Ej. ), Debe reconocerse de alguna manera. Lanzar una excepción está permitido.

Se pueden ver más ejemplos a continuación.

Casos de prueba

"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
fuente
55
¿Está permitido el relleno cero? aka en 03rdlugar de3rd
Value Ink
@ValueInk Si lees mi comentario anterior, ignóralo; No entendí la pregunta. El relleno cero en la salida no está permitido.
GarethPW
¿Deberíamos considerar un año en más de 4 caracteres (por ejemplo, 10987-01-01)?
mdahmoune
@mdahmoune No necesita admitir esto a menos que sea más fácil hacerlo.
GarethPW
¿Qué hay de 2016-2-29?
Olivier Grégoire

Respuestas:

5

PostgreSQL, 61 caracteres

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

Declaración preparada, toma la entrada como parámetro.

Ejecución de muestra:

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.
hombre trabajando
fuente
Agradable, deseo que MS-SQL reconozca el "th" estilo de formato.
BradC
Teniendo en cuenta que logró completar la tarea correctamente con la menor cantidad de bytes, ¡supongo que su solución es la ganadora!
GarethPW
Guau. Gracias. Eso es completamente inesperado. No noté hasta ahora la ausencia de idiomas dedicados al golf. Sin embargo, aceptar una solución después de solo un día es un poco pronto.
manatwork
@manatwork Me pregunté si podría ser un poco temprano. Pero puedo cambiarlo si es necesario de todos modos.
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}"

Pruébalo en línea!

Uriel
fuente
3
%-des la versión sin relleno %dque puede usar en su formato de cadena en lugar de {g[2]}. Además, 12debería ser 12th, no 12nd(los números del 10-19 no siguen las mismas reglas que el 1-9 y el 20+)
Value Ink
1
+1. no sabía sobre las fcuerdas
Felipe Nardi Batista
@ValueInk gracias! también, solucionó el problema de los ordinales
Uriel
5

JavaScript (ES6), 142 140 bytes

Salidas NaNth Invalid Datepara fechas no válidas.

El código para los números ordinales se adaptó de esta respuesta .

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

Darrylyeo
fuente
1
Da "1 de marzo de 2011" para el 29-22 de 2011 en Chrome. Esa puede ser una solución 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"))

Pruébalo en línea! (Configure el flujo de entrada y luego ejecútelo).

Gracias a las buenas sugerencias de los comentaristas a continuación.

Luke Sawczak
fuente
Puede guardar un byte eliminando el espacio entre int(x)y foren la lista de comp.
Christian Dean
@ ChristianDean Gracias, ¡listo!
Luke Sawczak
(('st','nd','rd')[b%10-1]if b<4 or 20<b<24 else'th')en lugar de su condicional actual para -3 bytes.
Value Ink
@ValueInk Lamentablemente, eso producirá el 31 °. Otra forma en que pensé en desglosar era 'th' si no 0 <b% 10 <4 o 10 <b <14 pero no guardó ningún byte.
Luke Sawczak
En ese caso, abusar de la coerción de tipo. (3<b<21)+(23<b<31)para -1 byte. Pruébalo en línea!
Value Ink
5

PHP, 87 bytes

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

Ejecutar como tubería con -Fo probarlo en línea . Siempre imprime un año de 4 dígitos; falla por años> 9999.

sin verificación de validez, 35 bytes:

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

Bash + coreutils, 115 78

  • 2 bytes guardados gracias 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"

Pruébalo en línea .

Trauma digital
fuente
1
Parece que el uso de cadenas en lugar de la matriz podría ayudar un poco: f=thstndrd; $d"${f:t/10-1?t%10<4?t%10*2:0:0:2} %B %Y".
manatwork
1
Por cierto, su revisión 1 inspiró un consejo de Bash . ;)
manatwork
@manatwork sí, es gracioso, consideré intentarlo pero no pensé que ayudaría. Gracias por el empujón.
Trauma digital
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 gracias a @The_Lone_Devil.

TheLethalCoder
fuente
¿No podría reemplazar el segundo t.Daycon dun ahorro de 4 bytes?
The_Lone_Devil
@The_Lone_Devil Por supuesto que podría agradecer, no sé cómo me perdí eso.
TheLethalCoder
4

mIRC versión 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
fuente
3

Rubí , 104 103 102 + 8 = 112 111 110 bytes

Utiliza -rdate -pbanderas 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"}

Pruébalo en línea!

Tinta de valor
fuente
¿Me estoy perdiendo una razón por la que no usaste el operador ternario? d<4||d>20?"..stndrd"[d%10*2,2]:"th"
manatwork
@manatwork Un número como 26intentará acceder a los índices 12..13en la cadena de búsqueda, que está fuera de los límites, y por lo tanto regresa nil. Por lo tanto, usar el ternario lo hace d<4||d>20?"..stndrd"[d%10*2,2]||"th":"th", que es más largo por 2 bytes.
Value Ink
Ah, ya veo. Bueno, entonces genial truco @ValueInk.
manatwork
Casi lo olvido, un pequeño cambio: "th":th.
manatwork
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

Pruébalo en línea!

+30 bytes para

using System;

DateTime.Parse()

kakkarot
fuente
Puede revertir la comprobación ternaria para deshacerse del !byte -1. Y puede cambiarlo &&a &-3 bytes. Además, dado que usa s.Day7 veces, guarda algunos bytes para crear un valor temporal para él: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
@KevinCruijssen ¡Gracias!
kakkarot
También debe incluir using System;o calificar completamente el DateTimeobjeto.
TheLethalCoder
También DateTime.MinValuees 1-1-1así que no creo que necesites ese cheque. Lo que también haría que mi punto anterior sea irrelevante.
TheLethalCoder
1
Tomar una entrada como un DateTimeanálisis fuera del método no es aceptable, debe hacer todo el trabajo dentro del método. O agregue un método adicional para dividir el trabajo.
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)

Si lo rompes en pedazos en cada ampersand, obtienes estas piezas:

  • ABS()extrae el número del día de los últimos dos caracteres en la cadena. Como eso puede incluir un guión, lo ABSconvierte en positivo.
  • IF((ABS-12)<2,"th",SWITCH())agrega el ordinal. El -12bit es porque 11, 12 y 13 no siguen la regla normal y todos obtienen en thlugar dest , nd, y rd. Esto corrige por eso.
    • Nota: La SWITCHfunción solo está disponible en Excel 2016 y versiones posteriores. ( Fuente ) Es más corto que CHOOSEen este caso porque puede devolver un valor si no se encuentra una coincidencia, mientras que CHOOSErequiere una entrada numérica y debe tener un retorno correspondiente para cada valor posible.
  • TEXT(MID()*30," mmmm ")extrae el nombre del mes. MID()extrae el número del mes como una cadena y multiplicando por 30 devuelve un número. Excel ve ese número como una fecha (1900-01-30, 1900-02-29, 1900-03-30, etc.) y lo TEXT()formatea como un nombre de mes con un espacio en ambos extremos. 28 y 29 también habrían funcionado, pero 30 se ve "mejor".
  • LEFT() extrae el número del año.

Ahora, dado todo eso, habría sido mucho más fácil si todos los casos de prueba estuvieran en un rango de fechas que Excel puede manejar como una fecha real: 1900-01-01 a 9999-12-31. La gran ventaja es que toda la fecha está formateada a la vez. Esa solución es 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")

El otro gran obstáculo era tener que incluir el ordinal. Sin eso, la solución es solo 34 bytes :

=TEXT(DATEVALUE(A1),"d mmmm yyyy")
Tostadas de ingeniero
fuente
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)")}

Pruébalo en línea!

A. Pooja
fuente
8
Bienvenido al sitio! Aquí el objetivo es hacer que el código sea lo más breve posible, puedo ver que tiene nombres de variables largos y mucho espacio en blanco, puede acortarlos y eliminarlos para ahorrar muchos bytes. También generalmente incluimos un encabezado en la parte superior de la respuesta en forma de # Language, N bytes. Sería bueno si pudieras agregar uno también.
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')

La entrada es a través de la columna de texto i en la tabla preexistente t , según nuestros estándares IO .

Funciona para fechas del 1 de enero de 0001 al 31 de diciembre de 9999. El año se emite con al menos 3 dígitos (por ejemplo 150AD).

Las fechas inválidas resultarán en el siguiente error feo:

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

Diferentes configuraciones predeterminadas de idioma / cultura pueden cambiar este comportamiento. Si desea una salida de error un poco más elegante (NULL), agregue 4 bytes cambiando PARSE()aTRY_PARSE() .

Formato y explicación:

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

El DATEtipo de datos introducido en SQL 2008 permite un rango mucho más amplio queDATETIME , desde el 1 de enero de 0001 hasta el 31 de diciembre de 9999.

Algunas fechas muy tempranas se pueden analizar incorrectamente con la configuración de mi localidad de EE. UU. ("01-02-03" se convierte en "2 de enero de 2003"), por lo que agregué un par de ceros adicionales para que sepa que el primer valor es el año.

Después de eso, es solo una CASEdeclaración desordenada para agregar el sufijo ordinal al día. Molesto, el FORMATcomando SQL no tiene forma de hacerlo automáticamente.

BradC
fuente
1

q / kdb + 210 bytes, no competidores

Solución:

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

Ejemplos:

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

Explicación:

Este es un desafío horrible ya que no hay formato de fecha, así que tengo que crear meses desde cero (95 bytes) y generar el sufijo.

La solución no oculta se encuentra a continuación, básicamente divide la cadena de entrada y luego vuelve a unirlas después de agregar el sufijo y cambiar el mes.

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:

Las fechas en q solo se remontan a ~ 1709, por lo que no tengo una forma trivial de validar la fecha, por lo tanto, esta es una entrada no competitiva ... Lo mejor que puedo hacer es verificar si el día es> 31 o el mes es > 12 y devuelve 0.

callejero
fuente