Синхронизация дат формата "10 января 2010 года" на Java? (с порядковыми показателями, st | nd | rd | th)

Мне нужно проанализировать даты формата "10 января 2010 года" на Java. Как я могу это сделать?

Как обрабатывать порядковые индикаторы st, nd, rd или th, завершающие номер дня

Ответ 1

Это работает:

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, например.

Ответ 2

Вы можете установить 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" и т.д., я думаю.

Ответ 3

Это еще один простой способ, но нужно включить 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));

Ответ 4

Я хотел бы внести современный ответ. Вместо того, чтобы использовать класс 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.

Более твердый раствор

Пара ограничений с подходом выше

  1. Он принимает любой порядковый индикатор, например 10st и даже 10stndrdth.
  2. Хотя 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

связи