Проаналізуйте будь-яку дату в Java


79

Я знаю, що це запитання задається досить багато, і, очевидно, ви не можете проаналізувати жодної довільної дати. Однак я виявляю, що бібліотека python-dateutil може аналізувати кожну дату, яку я кидаю на неї, і все це вимагає абсолютно нульових зусиль для з'ясування рядка формату дати. Час Joda завжди продається як чудовий аналізатор дат Java, але він все одно вимагає, щоб ви вирішили, в якому форматі ваша дата, перш ніж вибрати формат (або створити власний). Ви не можете просто зателефонувати DateFormatter.parse (mydate) і магічно повернути об'єкт Date назад.

Наприклад, дата "Ср, 04 квітня, 05:09:06 GMT-06: 00 2009" правильно розібрана з python-dateutil:

import dateutil.parser
print dateutil.parser.parse('Wed Mar 04 05:09:06 GMT-06:00 2009')

але наступний дзвінок часу Joda не працює:

    String date = "Wed Mar 04 05:09:06 GMT-06:00 2009";
    DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
    DateTime dt = fmt.parseDateTime(date);
    System.out.println(date);

І створення власного DateTimeFormatter перемагає мету, оскільки це, схоже, те саме, що використання SimpleDateFormatter з правильним рядком формату.

Чи існує подібний спосіб синтаксичного аналізу дати в Java, як python-dateutil? Мені не байдужі помилки, я просто хочу, щоб вони в основному були досконалими.

Відповіді:


107

Найкраще попросити допомогти регулярному виразу, щоб він відповідав шаблону формату дати та / або виконував грубий примус.

Кілька років тому я написав маленький безглуздий DateUtilклас, який зробив цю роботу. Ось витяг з релевантності:

private static final Map<String, String> DATE_FORMAT_REGEXPS = new HashMap<String, String>() {{
    put("^\\d{8}$", "yyyyMMdd");
    put("^\\d{1,2}-\\d{1,2}-\\d{4}$", "dd-MM-yyyy");
    put("^\\d{4}-\\d{1,2}-\\d{1,2}$", "yyyy-MM-dd");
    put("^\\d{1,2}/\\d{1,2}/\\d{4}$", "MM/dd/yyyy");
    put("^\\d{4}/\\d{1,2}/\\d{1,2}$", "yyyy/MM/dd");
    put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}$", "dd MMM yyyy");
    put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}$", "dd MMMM yyyy");
    put("^\\d{12}$", "yyyyMMddHHmm");
    put("^\\d{8}\\s\\d{4}$", "yyyyMMdd HHmm");
    put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}$", "dd-MM-yyyy HH:mm");
    put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy-MM-dd HH:mm");
    put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}$", "MM/dd/yyyy HH:mm");
    put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy/MM/dd HH:mm");
    put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMM yyyy HH:mm");
    put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMMM yyyy HH:mm");
    put("^\\d{14}$", "yyyyMMddHHmmss");
    put("^\\d{8}\\s\\d{6}$", "yyyyMMdd HHmmss");
    put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd-MM-yyyy HH:mm:ss");
    put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy-MM-dd HH:mm:ss");
    put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "MM/dd/yyyy HH:mm:ss");
    put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy/MM/dd HH:mm:ss");
    put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMM yyyy HH:mm:ss");
    put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMMM yyyy HH:mm:ss");
}};

/**
 * Determine SimpleDateFormat pattern matching with the given date string. Returns null if
 * format is unknown. You can simply extend DateUtil with more formats if needed.
 * @param dateString The date string to determine the SimpleDateFormat pattern for.
 * @return The matching SimpleDateFormat pattern, or null if format is unknown.
 * @see SimpleDateFormat
 */
public static String determineDateFormat(String dateString) {
    for (String regexp : DATE_FORMAT_REGEXPS.keySet()) {
        if (dateString.toLowerCase().matches(regexp)) {
            return DATE_FORMAT_REGEXPS.get(regexp);
        }
    }
    return null; // Unknown format.
}

(кашель, ініціалізація подвійних фігурних дужок, кашель, це просто для того, щоб все це вмістилося в довжину 100 символів;))

Ви можете легко розширити його самостійно за допомогою нових шаблонів регулярного виразу та формату дати.


3
Що ви робите з неоднозначними датами? Наприклад, що 03/04/2010означає - 3 квітня 2010 року або 4 березня 2010 року?
Jesper

3
Думаю, припустити те чи інше (що налаштовується)
Божо

3
@Jesper: /роздільник зазвичай використовується для позначення MM/dd/yyyy(в основному використовується в мовах США / Англії). -Сепаратор зазвичай використовуються для позначення dd-MM-yyyy( в основному використовується в європейських районах).
BalusC

3
@Jesper, так, ви повинні вирішити між місяцем або днем ​​із форматом, інакше ви ніколи нікуди не дійдете.
Макс

3
@kittylyst: Це правильно. Навіть більше, для цього не існує куленепробивного підходу :)
BalusC

52

Існує приємна бібліотека під назвою Natty, яка, на мою думку, відповідає вашим цілям:

Natty - це синтаксичний аналізатор дати, написаний на Java. Враховуючи вираз дати, natty застосовуватиме методи розпізнавання та перекладу стандартної мови, щоб створити список відповідних дат із необов’язковою інформацією про синтаксичний аналіз та синтаксис.

Ви також можете спробувати через Інтернет !


Дуже дякую! Це здається справді чудовим вибором.
Раджу Пенумаца,

Оце Так! Я дуже вражений можливістю цієї бібліотеки аналізувати будь-яку дату в будь-якому форматі. Потрібна невелика допомога щодо аналізу часу, однак я звернувся до цього у цій публікації на SoftwareRecs.SE: softwarerecs.stackexchange.com/questions/26556/…
Майкл Плаутц

1
це рука найкращої бібліотеки, я навіть спробував такі речі, як: "напередодні Різдва 2012", і він проаналізував це правильно
jjj

5
Це не вдається з "13/02/2002", я отримую 22 лютого, здається, не дуже міжнародним.
Рікардо Фрейтас

3
Так, дивно, що Натті не може працювати з форматами день-місяць-рік.
ConorD55

7

Те, що я бачив, - це клас утиліти Date, який містить кілька типових форматів дат. Отже, коли викликається DateUtil.parse (дата), він намагається проаналізувати дату з кожним форматом дати внутрішньо і видає виключення, лише якщо жоден із внутрішніх форматів не може проаналізувати її.

В основному це грубий підхід до вашої проблеми.


Я думаю, що це найбільш прямий та зрозумілий підхід. Оскільки рядок дат невідомого формату є амбіційним за задумом, вкладання занадто багато "розуму" у намагання розпізнати формат, ймовірно, призводить до більш "дивовижних" результатів.
Еріх Кіцмюллер

Так, але я думаю, що є кілька припущень, які ви можете зробити з урахуванням трохи початкової інформації (порядок дня / місяця / року в даті), щоб правильно проаналізувати більшість розумних дат без великої таблиці пошуку.
Макс.

Макс, це правда, і, швидше за все, існує обмежений набір форматів дат, які ви б шукали. Ви можете зробити дуже мало припущень щодо порядку дня та місяця, не написавши повномасштабного механізму розбору дати. Чи існує для цього конкретний випадок використання, оскільки це може допомогти людям спрямувати їх у правильному напрямку. Наприклад, більшість форматів дат з різних служб соціальних медіа вміщуються приблизно в 10 популярних форматах.
Роберт Діана,

Можливо, мене більше цікавить аспект юзабіліті. Msgstr "Розбір більшості дат, ніколи більше не маючи справу з рядком формату". Я думаю, що я справді просто хочу бачити в Java бібліотеку, як python-dateutil, що, я гадаю, означало б, що я повинен це зробити, якщо мені так хочеться!
Макс.

Я думаю, наші визначення понять юзабіліті теж різні. Клас дат, який я бачив, міг проаналізувати дати приблизно з 30 різних веб-служб. Використання класу дати було таким самим простим, як синтаксичний розбір (дата), тому мені як користувачеві утиліти не доводилося турбуватися про формати дат. Письменник утиліти зробив для мене тривожне.
Роберт Діана,

6

Ви можете спробувати dateparser .

Він може розпізнати будь-якого автоматично рядок та правильно та швидко аналізувати його на Date , Calendar , LocalDateTime , OffsetDateTime ( 1us~1.5us).

Це не базується на будь-якому natural language analyzerабо SimpleDateFormatабо regex.Pattern.

З його допомогою ви не повинні підготувати всі необхідні зразки , як yyyy-MM-dd'T'HH:mm:ss.SSSZі yyyy-MM-dd'T'HH:mm:ss.SSSZZ:

Date date = DateParserUtils.parseDate("2015-04-29T10:15:00.500+0000");
Calendar calendar = DateParserUtils.parseCalendar("2015-04-29T10:15:00.500Z");
LocalDateTime dateTime = DateParserUtils.parseDateTime("2015-04-29 10:15:00.500 +00:00");

Все працює нормально, будь ласка, насолоджуйтесь цим.


Щойно подивився, здається, він охоплює найрізноманітніші формати
Sankalp,

0

Я поняття не маю про цей синтаксичний аналіз, як це робити в python. У Java ми можемо робити так

SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yyyy");
  java.util.Date normalDate = null;
  java.sql.Date sqlDate = null;
  normalDate = sdf1.parse(date);
  sqlDate = new java.sql.Date(normalDate.getTime());
  System.out.println(sqlDate);

я думаю, що як у Java деякі заздалегідь визначені функції будуть у python. Ви можете слідувати цьому методу. Ці методи аналізують дату рядка на дату кв (дд-ММ-рррр);

import java.text.SimpleDateFormat;
import java.text.ParseException;
public class HelloWorld{
     public static void main(String []args){
        String date ="26-12-2019";
         SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yyyy");
        java.util.Date normalDate = null;
        java.sql.Date sqlDate = null;
        if( !date.isEmpty()) {
            try {
                normalDate = sdf1.parse(date);
                sqlDate = new java.sql.Date(normalDate.getTime());
                System.out.println(sqlDate);
            } catch (ParseException e) {
            }
        }
     }
} 

виконати це!


1
Будь ласка, не вчіть молодих користуватися давно застарілим і, як відомо, неприємним SimpleDateFormatзаняттям. Принаймні не як перший варіант. І не без будь-яких застережень. Сьогодні у нас набагато кращий java.timeсучасний API дати та часу Java та його DateTimeFormatter.
Оле В. В.

Якщо ми знаємо, як вирішити проблему, ми розглянемо останні оновлення. Тепер ми отримали рішення, спробуємо отримати набагато краще. У будь-якому разі, дякую за ваше оновлення!
Шашидхар Редді,

1
Існує друкарська помилка для мм, яка представляє хвилини. Ми повинні використовувати ММ, що представляє місяці.
Шашидхар Редді,

0
//download library:   org.ocpsoft.prettytime.nlp.PrettyTimeParser
String str = "2020.03.03";
Date date = new PrettyTimeParser().parseSyntax(str).get(0).getDates().get(0);
System.out.println(date)

1
Будь ласка, розміщуйте свою відповідь завжди в контексті, а не просто вставляйте код. Детальніше див. Тут .
gehbiszumeis
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.