Подтвердить что ты не робот

Дополнительные компоненты в SimpleDateFormat

Я читаю строки даты, которые могут быть с или без настройки часового пояса: yyyyMMddHHmmssz или yyyyMMddHHmmss. Когда в строке отсутствует зона, я буду рассматривать ее как GMT. Я не вижу возможности создавать необязательные разделы в SimpleDateFormat, но, возможно, мне что-то не хватает. Есть ли способ сделать это с помощью SimpleDateFormat, или просто написать новый конкретный DateFormat, чтобы справиться с этим?

4b9b3361

Ответ 1

Я бы создал два SimpleDateFormat, один с часовым поясом и один без. Вы можете посмотреть длину строки, чтобы определить, какой из них использовать.


Похоже, вам нужен DateFormat, который делегирует два разных SDF.

DateFormat df = new DateFormat() {
    static final String FORMAT1 = "yyyyMMddHHmmss";
    static final String FORMAT2 = "yyyyMMddHHmmssz";
    final SimpleDateFormat sdf1 = new SimpleDateFormat(FORMAT1);
    final SimpleDateFormat sdf2 = new SimpleDateFormat(FORMAT2);
    @Override
    public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Date parse(String source, ParsePosition pos) {
        if (source.length() - pos.getIndex() == FORMAT1.length())
            return sdf1.parse(source, pos);
        return sdf2.parse(source, pos);
    }
};
System.out.println(df.parse("20110102030405"));
System.out.println(df.parse("20110102030405PST"));

Ответ 2

JSR-310 был доставлен с Java 8, который обеспечивает расширенную поддержку для разбора времени, где компоненты теперь могут быть необязательными. Вы не только можете сделать зону необязательной, но также можете сделать компонент времени дополнительным и вернуть правильный временной блок для данной строки.

Рассмотрим следующие тестовые примеры.

public class DateFormatTest {

    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
            "yyyy-MM-dd[[ ]['T']HH:mm[:ss][XXX]]");

    private TemporalAccessor parse(String v) {
        return formatter.parseBest(v,
                                   ZonedDateTime::from,
                                   LocalDateTime::from,
                                   LocalDate::from);
    }

    @Test public void testDateTime1() {
        assertEquals(LocalDateTime.of(2014, 9, 23, 14, 20, 59),
                     parse("2014-09-23T14:20:59"));
    }

    @Test public void testDateTime2() {
        assertEquals(LocalDateTime.of(2014, 9, 23, 14, 20),
                     parse("2014-09-23 14:20"));
    }

    @Test public void testDateOnly() {
        assertEquals(LocalDate.of(2014, 9, 23), parse("2014-09-23"));
    }

    @Test public void testZonedDateTime() {
        assertEquals(ZonedDateTime.of(2014, 9, 23, 14, 20, 59, 0,
                                      ZoneOffset.ofHoursMinutes(10, 30)),
                     parse("2014-09-23T14:20:59+10:30"));
    }

}

Здесь DateTimeFormatter паттерн "yyyy-MM-dd[[ ]['T']HH:mm[:ss][XXX]]" позволяет в квадратных скобках включать и выключать опции, которые также могут быть вложенными. Шаблоны также могут быть построены из DateTimeFormatterBuilder, который показан выше:

private final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
        .parseCaseInsensitive()
        .append(DateTimeFormatter.ISO_LOCAL_DATE)
        .optionalStart()
        .optionalStart()
        .appendLiteral(' ')
        .optionalEnd()
        .optionalStart()
        .appendLiteral('T')
        .optionalEnd()
        .appendOptional(DateTimeFormatter.ISO_TIME)
        .toFormatter();

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

Ответ 3

Я знаю, что это старый пост, но только для записи...

Класс Apache DateUtils может помочь вам в этом.

String[] acceptedFormats = {"dd/MM/yyyy","dd/MM/yyyy HH:mm","dd/MM/yyyy HH:mm:ss"};
Date date1 = DateUtils.parseDate("12/07/2012", acceptedFormats);
Date date2 = DateUtils.parseDate("12/07/2012 23:59:59", acceptedFormats);

Ссылка на библиотеку Apache Commons Lang в репозитории Maven, вы можете проверить, какая последняя версия:
http://mvnrepository.com/artifact/org.apache.commons/commons-lang3

Maven v3.4

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.4</version>
</dependency>

Gradle v3.4

'org.apache.commons:commons-lang3:3.4'

Ответ 4

Если вы можете использовать Joda Datetime, он поддерживает дополнительные части в форматировании, например, "yyyy-MM-dd [hh: mm: ss]"

private DateTimeFormatter fmt = new DateTimeFormatterBuilder()
.append(DateTimeFormat.forPattern("yyyy-MM-dd"))                                            
.appendOptional(
    new DateTimeFormatterBuilder()
    .appendLiteral(' ')
    .append(DateTimeFormat.forPattern("HH:mm:ss"))
    .toParser()
.toFormatter();

Ответ 5

Я бы переместил список потенциальных объектов DateFormat, используя операцию try-catch, чтобы разбить цикл на первом успешном анализе.

Ответ 6

Вы можете создать два разных SimpleDateFormats типа

 public PWMDateTimeFormatter(String aPatternStr)
    {
        _formatter = DateTimeFormat.forPattern(aPatternStr);
    }



    public PWMDateTimeFormatter(String aPatternStr, TimeZone aZone)
    {
        _formatter =   DateTimeFormat.forPattern(aPatternStr).withZone(XXDateTime._getTimeZone(aZone));
    }

Ответ 7

Я решил подобную проблему некоторое время назад, расширив SimpleDateFormat. Ниже грубой реализации, чтобы показать идею моего решения. Он может быть не полностью полностью/оптимизирован.

public class MySimpleDateFormat extends SimpleDateFormat {
    private static final long serialVersionUID = 1L;
    private static String FORMAT = "        
    private static int FORMAT_LEN = "yyyyMMddHHmmss".length();
    private static String TZ_ID = "GMT";

    public MySimpleDateFormat() {
            this(TimeZone.getTimeZone(TZ_ID));
    }

    public MySimpleDateFormat(TimeZone tz) {
            super(FORMAT);
            setTimeZone(tz);
    }

    @Override
    public Date parse(String source, ParsePosition pos) {
            // TODO: args validation
            int offset = pos.getIndex() + FORMAT_LEN;
            Date result;
            if (offset < source.length()) {
                    // there maybe is a timezone
                    result = super.parse(source, pos);
                    if (result != null) {
                            return result;
                    }
                    if (pos.getErrorIndex() >= offset) {
                            // there isn't a TZ after all
                            String part0 = source.substring(0, offset);
                            String part1 = source.substring(offset);
                            ParsePosition anotherPos = new ParsePosition(pos.getIndex());
                            result = super.parse(part0 + TZ_ID + part1, anotherPos);
                            if(result == null) {
                                    pos.setErrorIndex(anotherPos.getErrorIndex());
                            } else {
                                    // check SimpleDateFormat#parse javadoc to implement correctly the pos updates
                                    pos.setErrorIndex(-1);
                                    pos.setIndex(offset);
                            }
                            return result;
                    }
                    // there something wrong with the first FORMAT_LEN chars
                    return null;
            }
            result = super.parse(source + TZ_ID, pos);
            if(result != null) {
                    pos.setIndex(pos.getIndex() - TZ_ID.length());
            }
            return result;
    }

    public static void main(String [] args) {
            ParsePosition pos = new ParsePosition(0);
            MySimpleDateFormat mySdf = new MySimpleDateFormat();
            System.out.println(mySdf.parse("20120622131415", pos) + " -- " + pos);
            pos = new ParsePosition(0);
            System.out.println(mySdf.parse("20120622131415GMT", pos) + " -- " + pos);
            pos = new ParsePosition(0);
            System.out.println(mySdf.parse("20120622131415xxx", pos) + " -- " + pos);
            pos = new ParsePosition(0);
            System.out.println(mySdf.parse("20120x22131415xxx", pos) + " -- " + pos);
    }
}

Суть в том, что вам нужно проверить строку ввода и "угадать" каким-то образом, что поле TZ отсутствует, добавьте его, если это так, а затем пусть SimpleDateFormat#parse(String, ParsePosition) сделает все остальное. Приведенная выше реализация не обновляет ParsePosition в соответствии с контрактом в javadoc в SimpleDateFormat#parse(String, ParsePosition)

У класса есть один по умолчанию ctor, так как разрешен только один формат.

Метод MySimpleDateFormat#parse(String, ParsePosition) вызывается SimpleDateFormat#parse(String), поэтому достаточно обойти оба случая.

Запуск main() - это выход (как ожидалось)

Fri Jun 22 14:14:15 BST 2012 -- java.text.ParsePosition[index=14,errorIndex=-1]
Fri Jun 22 14:14:15 BST 2012 -- java.text.ParsePosition[index=17,errorIndex=-1]
Fri Jun 22 14:14:15 BST 2012 -- java.text.ParsePosition[index=14,errorIndex=-1]
null -- java.text.ParsePosition[index=0,errorIndex=5]