Мне нужно проанализировать даты формата "10 января 2010 года" на Java. Как я могу это сделать?
Как обрабатывать порядковые индикаторы st
, nd
, rd
или th
, завершающие номер дня
Мне нужно проанализировать даты формата "10 января 2010 года" на Java. Как я могу это сделать?
Как обрабатывать порядковые индикаторы st
, nd
, rd
или th
, завершающие номер дня
Это работает:
String s = "January 10th, 2010";
DateFormat dateFormat = new SimpleDateFormat("MMM dd yyyy");
System.out.println("" + dateFormat.parse(s.replaceAll("(?:st|nd|rd|th),", "")));
но вы должны убедиться, что используете правильный Locale
для правильного анализа имени месяца.
Я знаю, что вы можете включать общие тексты внутри шаблона SimpleDateFormat
. Однако в этом случае текст зависит от информации и фактически не имеет отношения к процессу синтаксического анализа.
На самом деле это самое простое решение, о котором я могу думать. Но я хотел бы, чтобы меня показали неправильно.
Вы можете избежать ловушек, выставленных в одном из комментариев, сделав что-то похожее на это:
String s = "January 10th, 2010";
DateFormat dateFormat = new SimpleDateFormat("MMM dd yyyy");
System.out.println("" + dateFormat.parse(s.replaceAll("(?<= \\d+)(?:st|nd|rd|th),(?= \\d+$)", "")));
Это позволит вам не соответствовать Jath,uary 10 2010
, например.
Вы можете установить nd
и т.д. как литералы в SimpleDateFormat. Вы можете определить четыре необходимых формата и попробовать их. Начиная с th
во-первых, потому что я предполагаю, что это произойдет чаще. Если он не работает с ParseException
, попробуйте следующий. Если все не удается, выкиньте ParseException. Код здесь - всего лишь концепция. В реальной жизни вы не можете генерировать новые форматы каждый раз и можете думать о безопасности потоков.
public static Date hoolaHoop(final String dateText) throws ParseException
{
ParseException pe=null;
String[] sss={"th","nd","rd","st"};
for (String special:sss)
{
SimpleDateFormat sdf=new SimpleDateFormat("MMMM d'"+special+",' yyyy");
try{
return sdf.parse(dateText);
}
catch (ParseException e)
{
// remember for throwing later
pe=e;
}
}
throw pe;
}
public static void main (String[] args) throws java.lang.Exception
{
String[] dateText={"January 10th, 2010","January 1st, 2010","January 2nd, 2010",""};
for (String dt:dateText) {System.out.println(hoolaHoop(dt))};
}
Вывод:
Sun Jan 10 00:00:00 GMT 2010
Пт Янв 01 00:00:00 GMT 2010
Сб Янв 02 00:00:00 GMT 2010
Исключение в потоке "main" java.text.ParseException: Непревзойденная дата: ""
"th","nd","rd","st"
, конечно, подходит только для языков с английским языком. Запомни. Во Франции, "re","nd"
и т.д., я думаю.
Это еще один простой способ, но нужно включить apache commons jar.
import org.apache.commons.lang.time.*;
String s = "January 10th, 2010";
String[] freakyFormat = {"MMM dd'st,' yyyy","MMM dd'nd,' yyyy","MMM dd'th,' yyyy","MMM dd'rd,' yyyy"};
DateUtils du = new DateUtils();
System.out.println("" + du.parseDate(s,freakyFormat));
Я хотел бы внести современный ответ. Вместо того, чтобы использовать класс SimpleDateFormat
использовавшийся сегодня в ответе с двумя голосами, вы должны использовать java.time, современный Java-интерфейс даты и времени. Он предлагает пару хороших решений.
Сначала мы определим форматер для разбора:
private static final DateTimeFormatter PARSING_FORMATTER = DateTimeFormatter.ofPattern(
"MMMM d['st']['nd']['rd']['th'], uuuu", Locale.ENGLISH);
Тогда мы используем это так:
String dateString = "January 10th, 2010";
LocalDate date = LocalDate.parse(dateString, PARSING_FORMATTER);
System.out.println("Parsed date: " + date);
Выход:
Дата разбора: 2010-01-10
Квадратные скобки []
в строке шаблона формата содержат необязательные части, а одинарные кавычки - текст. Таким образом, d['st']['nd']['rd']['th']
означает, что после дня месяца могут быть st
, nd
, rd
и/или th
.
Пара ограничений с подходом выше
10st
и даже 10stndrdth
.January 10stndrdth, 2010
форматирования работает для синтаксического анализа, вы не можете использовать его для форматирования (это даст 10 January 10stndrdth, 2010
).Если вы хотите лучше проверить порядковый индикатор или хотите отформатировать дату обратно в строку, вы можете построить свой форматер следующим образом:
private static final DateTimeFormatter FORMATTING_AND_PARSING_FORMATTER;
static {
Map<Long, String> ordinalNumbers = new HashMap<>(42);
ordinalNumbers.put(1L, "1st");
ordinalNumbers.put(2L, "2nd");
ordinalNumbers.put(3L, "3rd");
ordinalNumbers.put(21L, "21st");
ordinalNumbers.put(22L, "22nd");
ordinalNumbers.put(23L, "23rd");
ordinalNumbers.put(31L, "31st");
for (long d = 1; d <= 31; d++) {
ordinalNumbers.putIfAbsent(d, "" + d + "th");
}
FORMATTING_AND_PARSING_FORMATTER = new DateTimeFormatterBuilder()
.appendPattern("MMMM ")
.appendText(ChronoField.DAY_OF_MONTH, ordinalNumbers)
.appendPattern(", uuuu")
.toFormatter(Locale.ENGLISH);
}
Это проанализирует строку даты так же, как и выше. Давайте также попробуем это для форматирования:
System.out.println("Formatted back using the same formatter: "
+ date.format(FORMATTING_AND_PARSING_FORMATTER));
Отформатированный обратно, используя тот же форматер: 10 января 2010