parse - simpledateformat java
Analizar el indicador ordinal de una fecha(st, nd, rd, th) en una cadena de fecha y hora (4)
Debería estar utilizando RuleBasedNumberFormat . Funciona perfectamente y es respetuoso con el Locale.
SimpleDateFormat
javadoc SimpleDateFormat
, pero no puedo encontrar una manera de analizar el indicador ordinal en un formato de fecha como este:
Feb 13th 2015 9:00AM
Intenté "MMM dd yyyy hh:mma"
, pero ¿los días tienen que estar en número para que sean correctos?
¿Es posible analizar la fecha "13" utilizando un SimpleDateFormat
sin tener que truncar la cadena?
En caso de que alguien lo encuentre útil: DateTimeFormatter constructor. Este formateador le permite formatear y analizar fechas en el Reino Unido con sufijos ordinales (por ejemplo, "1 de enero de 2017"):
public class UkDateFormatterBuilder
{
/**
* The UK date formatter that formats a date without an offset, such as ''14th September 2020'' or ''1st January 2017''.
* @return an immutable formatter which uses the {@link ResolverStyle#SMART SMART} resolver style. It has no override chronology or zone.
*/
public DateTimeFormatter build()
{
return new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.parseLenient()
.appendText(DAY_OF_MONTH, dayOfMonthMapping())
.appendLiteral('' '')
.appendText(MONTH_OF_YEAR, monthOfYearMapping())
.appendLiteral('' '')
.appendValue(YEAR, 4)
.toFormatter(Locale.UK);
}
private Map<Long, String> monthOfYearMapping()
{
Map<Long, String> monthOfYearMapping = new HashMap<>();
monthOfYearMapping.put(1L, "January");
monthOfYearMapping.put(2L, "February");
monthOfYearMapping.put(3L, "March");
monthOfYearMapping.put(4L, "April");
monthOfYearMapping.put(5L, "May");
monthOfYearMapping.put(6L, "June");
monthOfYearMapping.put(7L, "July");
monthOfYearMapping.put(8L, "August");
monthOfYearMapping.put(9L, "September");
monthOfYearMapping.put(10L, "October");
monthOfYearMapping.put(11L, "November");
monthOfYearMapping.put(12L, "December");
return monthOfYearMapping;
}
private Map<Long, String> dayOfMonthMapping()
{
Map<Long, String> suffixes = new HashMap<>();
for (int day=1; day<=31; day++)
{
suffixes.put((long)day, String.format("%s%s", (long) day, dayOfMonthSuffix(day)));
}
return suffixes;
}
private String dayOfMonthSuffix(final int day)
{
Preconditions.checkArgument(day >= 1 && day <= 31, "Illegal day of month: " + day);
if (day >= 11 && day <= 13)
{
return "th";
}
switch (day % 10)
{
case 1: return "st";
case 2: return "nd";
case 3: return "rd";
default: return "th";
}
}
}
Más un fragmento de la clase de prueba:
public class UkDateFormatterBuilderTest
{
DateTimeFormatter formatter = new UkDateFormatterBuilder().build();
@Test
public void shouldFormat1stJanuaryDate()
{
final LocalDate date = LocalDate.of(2017, 1, 1);
final String formattedDate = date.format(formatter);
Assert.assertEquals("1st January 2017", formattedDate);
}
@Test
public void shouldParse1stJanuaryDate()
{
final String formattedDate = "1st January 2017";
final LocalDate parsedDate = LocalDate.parse(formattedDate, formatter);
Assert.assertEquals(LocalDate.of(2017, 1, 1), parsedDate);
}
}
PD. Usé la solución de Greg Mattes para los sufijos ordinales de aquí: ¿Cómo formateas el día del mes para decir "11 °", "21 °" o "23 °" en Java? (indicador ordinal)
Respuesta de Java 8 (y Java 6 y 7) (porque cuando se hizo esta pregunta en 2015, el reemplazo de SimpleDateFormat
ya estaba fuera):
DateTimeFormatter parseFormatter = DateTimeFormatter
.ofPattern("MMM d[''st''][''nd''][''rd''][''th''] uuuu h:mma", Locale.ENGLISH);
LocalDateTime dateTime = LocalDateTime.parse(dateTimeString, parseFormatter);
Con la fecha de muestra de la pregunta esta yiedls:
2015-02-13T09:00
En el patrón de formato []
denota partes opcionales y ''''
denota partes literales. Entonces el patrón dice que el número puede ser seguido por st
, nd
, rd
o th
.
Para usar esto en Java 6 o 7 necesitas ThreeTen Backport . O para Android ThreeTenABP .
Dado que esos sufijos son especiales para el inglés, y otros idiomas / locales tienen otros usos para escribir fechas y horas (además, no usan AM / PM), creo que a menos que tenga otros requisitos, debe intentar implementar esto para Fechas y horarios en inglés solamente. También debe dar una configuración regional de habla inglesa explícitamente para que funcione independientemente de la configuración regional de su computadora o JVM.
He tratado de combinar las mejores partes de las respuestas de Hugo y myself en una pregunta duplicada. Bajo esa pregunta duplicada todavía hay más java 8 respuestas. Una limitación del código anterior es que no tiene una validación muy estricta: se saldrá con Feb 13rd
y hasta Feb 13stndrdth
.
SimpleDateFormat de Java no es compatible con un sufijo ordinal, pero el sufijo ordinal es solo una golosina, es redundante y puede eliminarse fácilmente para permitir un análisis sencillo:
Date date = new SimpleDateFormat("MMM dd yyyy hh:mma")
.parse(str.replaceAll("(?<=//d)(st|nd|rd|th)", ""));
El reemplazo de expresiones regulares es tan simple porque esas secuencias no aparecerán en ninguna otra parte en una fecha válida.
Para manejar cualquier idioma que agregue cualquier longitud de caracteres indicadores ordinales de cualquier idioma como sufijo:
Date date = new SimpleDateFormat("MMM dd yyyy hh:mma")
.parse(str.replaceAll("(?<=//d)(?=//D* //d+ )//p{L}+", ""));
Algunos idiomas, por ejemplo, el mandarín, anteponen su indicador ordinal, pero eso también podría manejarse usando una alternancia, dejada como un ejercicio para el lector :)