2016-08-29 13 views
7

私の問題は次のとおりです。私はファイルを読んでいて、それにはCSV行がたくさん含まれています。各行には、22-mar-2010またはそれに類する形式の日付が含まれています。つまり、形式はdd-MMM-yyyyです。これをISO形式に変換して、2010-03-22にします。Javaの英語の日付形式の解析

私が持っているコードは次のようになります。

private String convertDate(String date) { 
    DateTimeFormatter oldFormat = DateTimeFormatter.ofPattern("dd-MMM-yyyy", new Locale("en")); 
    LocalDate parsedDate = LocalDate.parse(date, oldFormat); 

    DateTimeFormatter newFormat = DateTimeFormatter.ISO_DATE; 
    String newDate = parsedDate.format(newFormat); 
    return newDate; 
    } 

入力は、このようなものになります。上記のようにロケールを含めるかwithLocale(Locale.ENGLISH)を使用する場合は

sdfdsfslk 28-mar-2007 dfdsljs 
sdfdsfslk 20-apr-2014 dfdsljs 
sdfdsfslk 13-oct-2005 dfdsljs 
sdfdsfslk 20-may-2014 dfdsljs 
sdfdsfslk 20-jan-2014 dfdsljs 
sdfdsfslk 20-feb-2014 dfdsljs 

を、それが最初の行の日付で失敗文字列。例外は次のとおりです。

java.time.format.DateTimeParseException: Text '28-mar-2007' could not be parsed at index 3 

私はロケールの一部を除去し、ちょうど持っている場合:

DateTimeFormatter.ofPattern("dd-MMM-yyyy"); 

そのような13-oct-2005として日付を検出するまで、それが動作します。それは英語 'oct'が好きでなく、LocalDate.parse行で失敗します。私がoctをokt(スウェーデン語、私がいるところ)に変換すると、それは解析されます。

ロケールを完全に変更する必要がありますか、ここで何が問題になっていますか?私がスウェーデンにいても英語で数ヶ月の日付を解析するにはどうすればいいですか?

+0

「ロケール」は常に関係しています。 'Locale'を明示的に指定しないと、JVMの現在のデフォルトロケールが暗黙的に適用されます。したがって、あなたのJVMの現在のデフォルトスウェーデンロケールがあなたのために適用されたため、 'okt'は正常に解析されました。 –

+0

ええ、問題は私がロケールを提供していたのですが、それは小文字の月を承認しませんでした。したがって、大文字から3月への変換。 –

+0

いいえ、本当に問題は、(a)月の名前の不適切な英語、および(b)日付値の形式の貧弱な選択のために使用される受信日付です。回避策として、不適切な英語に対応するために大文字と小文字を区別しないパーサを作成する方法については、[my answer](http://stackoverflow.com/a/39213129/642706)を参照してください。また、ソースデータのプログラマーに[ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)を教えてください。上記の私のコメントは、あなたの 'Locale'の削除がなぜスウェーデンのテキストで成功したのかを説明することでした。暗黙のスウェーデン語の 'Locale'は小文字の月名を、英語は初期値を期待しています。 –

答えて

6

月の最初の文字が小文字のであることが問題だと思います。 28-mar-2007の代わりに28-Mar-2007の同じコードを実行すると、すべて正常に動作します。

一つの迅速かつ汚いソリューションです:受け入れ答えと同じ

private String convertDate(String mydate) { 

     String date = mydate; 
     String firstLetter = date.substring(0,4).toUpperCase(); 
     String restLetters = date.substring(4).toLowerCase(); 
     date = firstLetter+restLetters; 

     DateTimeFormatter oldFormat = DateTimeFormatter.ofPattern("dd-MMM-yyyy", new Locale("en")); 
    LocalDate parsedDate = LocalDate.parse(date, oldFormat); 

    DateTimeFormatter newFormat = DateTimeFormatter.ISO_DATE; 
    String newDate = parsedDate.format(newFormat); 
    return newDate; 
    } 
+0

正解。これは、YYYY-MM-DDなどの標準の[ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)形式を使用して、日付と時刻の値を常にテキストに維持する必要がある理由の例です。 –

+0

@Plirkee、それは本当にうまくいって、あなたが言ったように、おそらく大文字/小文字の問題です。残念ながら、それは内部フォーマットを変更するのは難しいレガシーバックエンドです。 –

0
private static String convertDate(String daterec) { 
     String date = daterec; 
     String firstLetter = date.substring(0,4).toUpperCase(); 
     String restLetters = date.substring(4).toLowerCase(); 
     date = firstLetter+restLetters; 
     DateTimeFormatter dTF = DateTimeFormatter.ofPattern("dd-MMM-yyyy", new Locale("en")); 
     LocalDate lds = LocalDate.parse((date), dTF); 
    return lds.toString(); 
    } 

出力:

2007-03-28 
2014-04-20 
2005-10-13 
2007-03-28 
2014-01-20 
2014-02-20 

すでに述べたように無視できるがありますが、我々は、フォーマットを維持する必要があります私たちが 'dd-MMM-yyyy'を渡しているので避けることができる2行のコードはISO標準を意味しています。頌歌

4

TL; DR

LocalDate.parse ( 
    "13-oct-2005" , 
    new DateTimeFormatterBuilder() 
     .parseCaseInsensitive() 
     .appendPattern("dd-MMM-uuuu") 
     .toFormatter(Locale.US) 
) 

詳細

Answer by Plirkeeは正しいです:英語のロケールは、月の省略名が(大文字の)最初の大文字を持つことを期待します。この障害のある入力データが与えられ

DateTimeFormatterBuilder

、簡単にこの問題を回避するには、大文字と小文字を区別しませんフォーマッタを構築することです。 DateTimeFormatterBuilderクラスを使用すると、単なる書式設定のコード文字列パターンでできるより細かくカスタマイズされたフォーマッタを構築できます。

DateTimeFormatterDateTimeFormatterBuilderを含むjava.timeクラスはスレッドセーフです。したがって、繰り返し使用するためにインスタンスを保持することができます。

ビルダーパターン

Builder design pattern上に読んでいない場合はお馴染み。多数の引数を持つコンストラクタを呼び出すのではなく、必要に応じてさまざまなメソッドの呼び出しチェーンを使用してBuilderオブジェクトを構築します。最後に、実際に必要なオブジェクトをインスタンス化するようBuilderに依頼してください。この場合はDateTimeFormatterです。

.parseCaseInsensitive()

私たちが必要とするトリックは.parseCaseInsensitive()への呼び出しです。この呼び出しを省略したコメントアウトされた行と交換することで、この呼び出しが重要な要素であることを確認できます。

// DateTimeFormatterBuilder fbuilder = new DateTimeFormatterBuilder().appendPattern ("dd-MMM-uuuu"); // Case-sensitive by default. 
DateTimeFormatterBuilder fbuilder = new DateTimeFormatterBuilder().parseCaseInsensitive().appendPattern ("dd-MMM-uuuu"); // Case-insensitive to handle improper English. 

String input = "13-oct-2005"; // Incorrect English. Should be uppercase 'Oct'. 
DateTimeFormatter f = fbuilder.toFormatter (Locale.US); 
LocalDate ld = LocalDate.parse (input , f); 

2005-10-13

→ld.toString()ISO 8601

ヒント:テキストとして日時値を交換するときは、必ず標準ISO 8601フォーマットを使用してではなく、質問に見られるようなあなた自身のファンキーなフォーマットを考案してください。 java.timeクラスでは、文字列の解析/生成時にこれらの標準形式がデフォルトで使用されます。

+0

DateTimeFormatterBuilderを使用していただきありがとうございます。それは大変便利です。私は、Javaの文書では例がJunであることが分かったが、大文字と小文字が区別されているとは思わなかった。しかし、確かに.parseCaseInsensitive()でそれを解析するためには、はるかにtidierに見えます。 –

関連する問題