Как преобразовать строку в дату, не зная формат?

У меня проблема. Я пытаюсь преобразовать некоторые строки в дату, и я не знаю формат даты прибытия.

Это может прийти как yyyy.mm.dd hh:mm:ss или же MM.dd.yy hh:mm:ss и так далее.

Как я могу преобразовать эти строки в дату? Я попробовал это:

DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
Date d = (Date)formatter.parse(someDate);

Но когда я распечатал someDate, он распечатался так: 2010-08-05 12:42:48.638 CEST, что означает yyyy.mm.dd hh:mm:ssОднако, когда я запустил приведенный выше код, объект даты теперь стал Sat Jan 31 00:42:48 CET 11 что странно по меньшей мере.

Любые идеи, как я могу правильно отформатировать строки на сегодняшний день?

11 ответов

Решение

Вы не можете!

Если у вас есть дата 2010-08-05 тогда это может быть либо 5 августа 2010 года, либо 8 мая 2010 года - вам нужно знать формат даты (или, по крайней мере, расставить приоритеты над одним форматом над кадром), чтобы отличать их друг от друга.

Я согласен с Крагеном, что в общем случае нет правильного решения. Однако, если выполняются следующие условия, вы можете использовать приведенное ниже решение:

  1. У вас есть набор всех возможных форматов

  2. Нет никакой двусмысленности между форматами; никакое выражение даты не может быть успешно проанализировано двумя из них.

Рассмотрим следующее решение, которое перебирает список возможных форматов. Это решение использует ThreadLocal для того, чтобы сделать анализ данных эффективным в многопоточной среде (помните, что SimpleDateFormat не потокобезопасен):

public class FlexibleDateParser {
    private List<ThreadLocal<SimpleDateFormat>> threadLocals = new  ArrayList<ThreadLocal<SimpleDateFormat>>();

    public FlexibleDateParser(List<String> formats, final TimeZone tz){
        threadLocals.clear();
        for (final String format : formats) {
            ThreadLocal<SimpleDateFormat> dateFormatTL = new ThreadLocal<SimpleDateFormat>() {
                protected SimpleDateFormat initialValue() {
                    SimpleDateFormat sdf = new SimpleDateFormat(format);
                    sdf.setTimeZone(tz); 
                    sdf.setLenient(false);
                    return sdf;
                }
            };
            threadLocals.add(dateFormatTL);
        }       
    }

    public Date parseDate(String dateStr) throws ParseException {
        for (ThreadLocal<SimpleDateFormat> tl : threadLocals) {
            SimpleDateFormat sdf = tl.get();
            try {
                return sdf.parse(dateStr);
            } catch (ParseException e) {
                // Ignore and try next date parser
            }
        }
        // All parsers failed
        return null;
    }       
}

Как отмечалось ранее, вам нужно как минимум иметь упорядоченный список кандидатов на шаблоны. Как только вы это сделаете, Apache DateUtils получит parseDate(String dateString, String[] patterns) метод, который позволяет вам легко проверить список шаблонов в строке даты и проанализировать его по первому подходящему:

public static Date parseDate(String str,
                         String[] parsePatterns)
                  throws ParseException

Разбирает строку, представляющую дату, пробуя различные парсеры.

Анализ будет пробовать каждый шаблон анализа по очереди. Анализ считается успешным, только если он анализирует всю входную строку. Если шаблоны разбора не совпадают, генерируется ParseException.

Парсер будет снисходительным к анализируемой дате.

Вот быстрое и грязное решение, основанное на американских форматах дат.

public Date parseDate(String strDate) throws Exception
{
    if (strDate != null && !strDate.isEmpty())
    {
        SimpleDateFormat[] formats =
                new SimpleDateFormat[] {new SimpleDateFormat("MM-dd-yyyy"), new SimpleDateFormat("yyyyMMdd"),
                        new SimpleDateFormat("MM/dd/yyyy")};

        Date parsedDate = null;

        for (int i = 0; i < formats.length; i++)
        {
            try
            {
                parsedDate = formats[i].parse(strDate);
                return parsedDate;
            }
            catch (ParseException e)
            {
                continue;
            }
        }
    }
    throw new Exception("Unknown date format: '" + strDate + "'");
}

Ваша проблема связана с интернационализацией. Как ответил Краген, вы не можете просто разобрать дату в неизвестном формате. Хотя вы можете сканировать все возможные локали и что-то анализировать, но вы не узнаете, правильно ли это было проанализировано.

Просто немного фона i18n:

Q: Можете ли вы сказать мне, какой день месяц и год относится к этой дате:

09/11/10?

A: Не зная языка, вы не можете. Это может быть что угодно. 11 сентября в США. 9 ноября в Великобритании. И так далее.

Этот ответ является копией моего ответа на другой вопрос, отмеченный как дубликат этого

Однажды у меня была задача написать код, который анализировал бы строку на дату, если формат даты не был заранее известен. Т.е. мне пришлось разобрать любой допустимый формат даты. Я написал проект, а после этого написал статью, в которой описал идею моей реализации. Вот ссылка на статью: Пакет Java 8 java.time: анализ любой строки на текущий момент. Общая идея состоит в том, чтобы записать все шаблоны, которые вы хотите поддерживать, в файл внешних свойств, прочитать их оттуда и попытаться проанализировать вашу строку по этим форматам один за другим, пока у вас не получится или не закончатся форматы. Обратите внимание, что порядок также будет важен, поскольку некоторые строки могут быть действительны для нескольких форматов (различия в США и Европе). Преимущество состоит в том, что вы можете продолжать добавлять / удалять форматы в файл, не меняя код. Так что такой проект можно было адаптировать под разных заказчиков.

Если это протокол; определить формат - возможно, ISO, который будет раздражать всех, кроме нас в Швеции...

Если это вход от пользователей; Пусть они устанавливают свою локаль. Если вы можете это сделать, покажите проанализированную дату в полном формате, чтобы пользователь мог ее проверить, например, 10 ноября 2009 г.

Я создал утилиту, которая пытается анализировать некоторые распространенные форматы даты. Он пытается проверить, какая цифра больше 12, или, если обе цифры меньше 12, он предпочитает определяемое пользователем логическое значение PreferMonthFirst, на основе которого он будет выбирать между MM/dd/yyyyили же dd/MM/yyyy

Он также принимает логическое значение Prefer24HourTime для времени синтаксического анализа.

Я не использовал список, не перебирал его, не пытался разобрать и поймать исключение, потому что исключения дороже. Поэтому, основываясь на разделителе и длине, я попытался найти формат даты.

Вы можете найти использование в тестовых примерах.

https://github.com/rajat-g/DateParser

Мое единственное предположение, что вы должны сначала собрать статистику, чтобы выяснить формат.

Если вам повезет, у вас будет дата типа "2010/08/13", которую можно однозначно проанализировать

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

String dateToConvert(String date) {
        String strDate = "";
        try {
            //create SimpleDateFormat object with source string date format
            DateFormat sdfSource = new SimpleDateFormat("yyyy-MM-dd");

            //parse the string into Date object
            Date d = sdfSource.parse(date);
            LocalLog.e(LOG_TAG, d.toString());
            //create SimpleDateFormat object with desired date format
            SimpleDateFormat sdfDestination = new SimpleDateFormat(AppConstants.UNIVERSAL_DATE_FORMAT);

            //parse the date into another format
            strDate = sdfDestination.format(d);

            LocalLog.e(LOG_TAG, strDate);

        } catch (ParseException pe) {
            System.out.println("Parse Exception : " + pe);
        }
        return strDate;
    }
public  String compareDate( PaymentTxnRequest request ) throws ParseException { 
        Date debitDate= request.getPaymentTxn().getCrValDt();
        Date now = new Date();
        String response="";
        SimpleDateFormat sdfDate = new SimpleDateFormat("dd/MM/yyyy");
        String strCurrDate = sdfDate.format(now);
        String strDebitDate = sdfDate.format(debitDate);
        System.out.println("Current Date: " + strCurrDate);
        Date currentDate =  new SimpleDateFormat("dd/MM/yyyy").parse(strCurrDate);
        Date txnDate =  new SimpleDateFormat("dd/MM/yyyy").parse(strDebitDate);
        System.out.println("C -> "+currentDate);
        System.out.println("C -> "+txnDate); 
         if (txnDate!=null){
         if (currentDate.equals(txnDate))
         {
             System.out.println("Valid Txn");
             response="valid";
         }
         if (currentDate.after(txnDate))
         {
            System.out.println("--> Not  Valid TXN Past");   
            response="notValid";
         }
        if (currentDateenter code here.before(txnDate)){
            System.out.println("Future Valid TXn");
             response="future";
        }
     }
        return response;
    }
Другие вопросы по тегам