web-dev-qa-db-ja.com

SimpleDateFormatのオプション部分

タイムゾーン調整の有無にかかわらず、日付文字列を読み取っています:yyyyMMddHHmmsszまたはyyyyMMddHHmmss。文字列にゾーンがない場合は、GMTとして扱います。 SimpleDateFormatにオプションセクションを作成する方法はありませんが、何か不足しています。 SimpleDateFormatでこれを行う方法はありますか、またはこれを処理するために新しい具象DateFormatを作成する必要がありますか?

42
traffichazard

2つのSimpleDateFormatを作成します。1つはタイムゾーンあり、もう1つはありません。文字列の長さを見て、どれを使用するかを決定できます。


2つの異なるSDFに委任するDateFormatが必要なようです。

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"));
21
Peter Lawrey

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();

これは、次のような式に変換されます。

yyyy-MM-dd[[' ']['T']HH:mm[':'ss[.SSS]]].

オプションの値はネストでき、開いている場合は最後に自動的に閉じられます。ただし、オプションの部分に排他的なORを提供する方法はないため、上記の形式は実際には次の値を非常に細かく解析します。

2018-03-08 T11:12

既存のフォーマッターを現在のフォーマットの一部として再利用できる、本当に素晴らしい機能に注意してください。

57
Brett Ryan

これは古い投稿ですが、参考までに...

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);

MavenリポジトリーのApache Commons Langライブラリへのリンク。最新バージョンを確認したい場合があります。
http://mvnrepository.com/artifact/org.Apache.commons/commons-lang

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'
32
tbraun

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();
3
hgh

try-catch操作を使用して、潜在的なDateFormatオブジェクトのリストをループし、最初に成功した解析でループを解除します。

2
Johan Sjöberg

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's 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フィールドが欠落していることを何らかの形で「推測」する必要があるということです。TZフィールドが欠落している場合は追加して、残りをSimpleDateFormat#parse(String, ParsePosition)に実行させます。上記の実装は、SimpleDateFormat#parse(String, ParsePosition)のjavadocの規約に従ってParsePositionを更新していません

使用できるフォーマットは1つだけなので、クラスにはデフォルトのctorが1つあります。

メソッド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]
_
0
smartrics

次のような2つの異なるSimpleDateFormatsを作成できます

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



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