none
.Net4.6でのDateTime.ParseExactの動作について RRS feed

  • 質問

  • お世話になっております。

    DateTime.ParseExactメソッドについて質問がございます。

    コントロールパネルで日付の短い形式を「yyyy-MM-dd」に変更し、以下のようなコードを実行したところ動作させる.NetのVerで動作が異なりました。

    【実行コード】
     DateTime.ParseExact("9999/12/31 00:00:00", "yyyy/MM/dd HH:mm:ss", CultureInfo.CurrentCulture);
     
    【実行結果】
    .Net3.5(2.0):エラーとなる
    .Net4.5.1   :エラーとなる
    .Net4.6       :変換成功

    実行結果から.Net4.6からDateTime.ParseExactの仕様が変更されたのでは、と思い公式ドキュメントを見てみたのですが情報は乗っておりませんでした。
    他にも調べてみたのですが、仕様変更があったなどの情報は見かけませんでした。

    本件について何か情報をお持ちの方がいらっしゃいましたらご教授いただけないでしょうか。
    ※「.Net4.6からDateTime.ParseExactの仕様が変更された」という認識でいいのか不安があり、裏付けの情報があればと思い投稿させていただきました。


    よろしくお願いいたします。
    • 編集済み r_naka 2015年12月4日 1:26
    2015年12月4日 1:26

回答

  • DateTimeParse.ParseByFormat という内部メソッドの '/' に対する実装が変わっているためでしょう。

    http://referencesource.microsoft.com/#mscorlib/system/globalization/datetimeparse.cs,3777

    このメソッドは DateTime.ParseExact から最終的に到達する場所です。
    .NET 3.5.1 では単に '/' のところの if 文が if (!str.Match(dtfi.DateSeparator)) { だったのですが、少なくとも、.NET 4.6.1 のソースコードでは複合条件に変わっています。
    直前のコメントを参照する限り、意図的に変更されているようです。理由は存じませんが。

    なお、「ある振る舞い」について仕様か、不具合かを最終的に確定するためには、Microsoft のドキュメントで見つけるか、Microsoft に有償サポートで聞くしかないと思います。

    2015年12月4日 23:33
    モデレータ
  • なちゃ様

    >DateTime.ParseExact("9999-12-31 00:00:00", "yyyy/MM/dd HH:mm:ss", CultureInfo.CurrentCulture);
    こちらを試してみました。

    【検証結果】
    VS2010 + ターゲットフレームワーク:4.0 + 実行環境:3.5・4.5 →エラーなし(.Net4.5で動作する)
    VS2010 + ターゲットフレームワーク:4.0 + 実行環境:3.5・4.6 →エラーなし(.Net4.6で動作する)

    第一引数の文字列が第二引数のフォーマットと一致していないのにエラーにならず変換できるのですね…
    とりあえずご報告まで。

    なるほど…

    これは、DateTime.Parse関連の仕様変更っぽいですね…CultureInfoの方ではなく。

    ※/は常にセパレータとして認識されるようになったのかもしれません。

    つまるところAzuleanさんの書かれているところの動作ということだと思いますが。

    --追記

    Azuleanさんが示されたソース見てみました。

    コメントで、

    ----------------------------------------

    We match the separator in date pattern with the character in the date string if both equal to '/' or the date separator is matching the characters in the date string

    ----------------------------------------

    とあり、 つまるところ予想通りではありますが「/」は常にセパレータとみなすよ、ということですね。

    4.6でこう変わったのであれば、そういう仕様変更ということでしょうね。

    • 編集済み なちゃ 2015年12月7日 3:00
    • 回答としてマーク r_naka 2015年12月8日 4:37
    2015年12月7日 2:44

すべての返信

  • どちらかというと、CultureInfoの方の仕様変更もしくは不具合のようか感じもしますね…

    すみませんが情報は持っていません。

    2015年12月4日 1:47
  • 確認ですが、そのテストは同一マシンの同一OSで実行されているのでしょうか?

    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2015年12月4日 2:31
    モデレータ
  • プロジェクトを作成して試行した所、以下の結果になりました。

    VS2008 + .Net3.5 : エラー無し
    VS2010 + .Net4 : エラー無し
    VS2015 + .Net4.6 : エラー無し
    VS2015 + .Net3.5 : エラー無し

    但し、VS2015 + .Net4.6からターゲットフレームワークを.Net3.5に変更してビルドした所、
    CS0234エラーがでました。

    そちらで確認できたエラー番号はCS0234ですか?
    であれば、DateTime.ParseExactが問題なのではなく、ターゲットフレームワークを古いバージョンに変更した際、
    新しい.Netでしか使用できない参照が残っているのが理由だと思われます。

    例えば、using System.Threading.Tasksをコメントアウトすれば動作しませんか。

    (エラーを記載してもらえると、もっと回答が得やすくなると思います。)



    • 編集済み せれ 2015年12月4日 3:06
    2015年12月4日 2:58
  • 関係あるかわかりませんが、検索したら以下のページに.Net4.6のDateTime.Parseのカルチャ関係を修正してるらしいっぽい情報が。
    4.6でバグってたんですかね。あるいは新たにバグったのか…

    http://www.heikniemi.net/hardcoded/2015/08/windows-10-breaks-net-date-parsing-in-certain-locales/

    https://support.microsoft.com/ja-jp/kb/3088955
    https://support.microsoft.com/ja-jp/kb/3088956
    https://support.microsoft.com/ja-jp/kb/3088957


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2015年12月4日 3:43
  • trapemiya様

    ご回答ありがとうございます。

    テストは同一マシンで確認しました。

    2015年12月4日 5:36
  • せれ様

    ご回答ありがとうございます。

    こちらでプロジェクトを作成て検証した結果は以下の通りになります。

    VS2010 + ターゲットフレームワーク:2.0 + 実行環境:3.5・4.5 →エラー発生(.Net3.5で動作する)
    VS2010 + ターゲットフレームワーク:4.0 + 実行環境:3.5・4.5 →エラー発生(.Net4.5で動作する)
    VS2010 + ターゲットフレームワーク:2.0 + 実行環境:3.5・4.6 →エラー発生(.Net3.5で動作する)
    VS2010 + ターゲットフレームワーク:4.0 + 実行環境:3.5・4.6 →エラーなし(.Net4.6で動作する)

    発生したエラーは[System.FormatException:文字列は有効な DateTime ではありませんでした。]です。CS0234ではありませんでした。
    また、コードにはusing System.Threading.Tasksは設定しておりません。

    ほか確認してみましたが、今回問題となっているコード以外はtry・catchとmessageBoxくらいしか使用していないので新しい機能は使用しておりません…


    • 編集済み r_naka 2015年12月4日 5:54
    2015年12月4日 5:49
  • なちゃ様

    ご回答ありがとうございます。

    >どちらかというと、CultureInfoの方の仕様変更もしくは不具合のようか感じもしますね…

    gekka様よりCultureInfoかもという情報をいただきましたので、そちらを確認してみます。

    おっしゃる通り、DateTime.ParseExactではなくCultureInfoのほうが疑わしいですね…ありがとうございました。

    2015年12月4日 5:59
  • これに該当しませんか?

    http://stackoverflow.com/questions/24823219/datetime-parseexact-throws-system-formatexception

    これは関係ないと思います。

    というか今回の現象とは違う話(原因は明らか)ですね。

    今回はOSで区切り文字を-に設定した場合に、4.6ではCurrentCultureでもこの設定が認識されていないっぽい?というところだと思います。

    ちなみに、4.6では

    DateTime.ParseExact("9999-12-31 00:00:00", "yyyy/MM/dd HH:mm:ss", CultureInfo.CurrentCulture);

    だとエラーになりますかね?(4.6以外だとエラーにはならないですよね?)

    2015年12月4日 6:33
  • DateTimeParse.ParseByFormat という内部メソッドの '/' に対する実装が変わっているためでしょう。

    http://referencesource.microsoft.com/#mscorlib/system/globalization/datetimeparse.cs,3777

    このメソッドは DateTime.ParseExact から最終的に到達する場所です。
    .NET 3.5.1 では単に '/' のところの if 文が if (!str.Match(dtfi.DateSeparator)) { だったのですが、少なくとも、.NET 4.6.1 のソースコードでは複合条件に変わっています。
    直前のコメントを参照する限り、意図的に変更されているようです。理由は存じませんが。

    なお、「ある振る舞い」について仕様か、不具合かを最終的に確定するためには、Microsoft のドキュメントで見つけるか、Microsoft に有償サポートで聞くしかないと思います。

    2015年12月4日 23:33
    モデレータ
  • gekka様

    ご回答ありがとうございます。
    内容を確認していましたゆえ、回答が遅れてしまい申し訳ありません。

    やはりカルチャの実装が変わったことによる影響かもしれませんね…
    ただ、今回はカルチャというより「日付の書式」なので影響あるかどうかは何とも言えないような気もします。(難しいですね…)
    情報ありがとうございました。

    2015年12月7日 1:06
  • なちゃ様

    >DateTime.ParseExact("9999-12-31 00:00:00", "yyyy/MM/dd HH:mm:ss", CultureInfo.CurrentCulture);
    こちらを試してみました。

    【検証結果】
    VS2010 + ターゲットフレームワーク:4.0 + 実行環境:3.5・4.5 →エラーなし(.Net4.5で動作する)
    VS2010 + ターゲットフレームワーク:4.0 + 実行環境:3.5・4.6 →エラーなし(.Net4.6で動作する)

    第一引数の文字列が第二引数のフォーマットと一致していないのにエラーにならず変換できるのですね…
    とりあえずご報告まで。

    2015年12月7日 1:19
  • Azulean様

    ご回答ありがとうございます。

    ご教示いただいたコードが影響している可能性は確かにありそうです…
    貴重な情報を教えていただきありがとうございました。

    >なお、「ある振る舞い」について仕様か、不具合かを最終的に確定するためには、Microsoft のドキュメントで見つけるか、Microsoft に有償サポートで聞くしかないと思います。
    ドキュメントは自力では見つからず、まずはと思いこちらに投稿させていただきました。
    いろいろな方から情報をいただきましたが、確実な情報というのは難しそうです。
    有償サービスを利用するかどうかについては改めて検討したいと思います。

    2015年12月7日 1:24
  • なちゃ様

    >DateTime.ParseExact("9999-12-31 00:00:00", "yyyy/MM/dd HH:mm:ss", CultureInfo.CurrentCulture);
    こちらを試してみました。

    【検証結果】
    VS2010 + ターゲットフレームワーク:4.0 + 実行環境:3.5・4.5 →エラーなし(.Net4.5で動作する)
    VS2010 + ターゲットフレームワーク:4.0 + 実行環境:3.5・4.6 →エラーなし(.Net4.6で動作する)

    第一引数の文字列が第二引数のフォーマットと一致していないのにエラーにならず変換できるのですね…
    とりあえずご報告まで。

    なるほど…

    これは、DateTime.Parse関連の仕様変更っぽいですね…CultureInfoの方ではなく。

    ※/は常にセパレータとして認識されるようになったのかもしれません。

    つまるところAzuleanさんの書かれているところの動作ということだと思いますが。

    --追記

    Azuleanさんが示されたソース見てみました。

    コメントで、

    ----------------------------------------

    We match the separator in date pattern with the character in the date string if both equal to '/' or the date separator is matching the characters in the date string

    ----------------------------------------

    とあり、 つまるところ予想通りではありますが「/」は常にセパレータとみなすよ、ということですね。

    4.6でこう変わったのであれば、そういう仕様変更ということでしょうね。

    • 編集済み なちゃ 2015年12月7日 3:00
    • 回答としてマーク r_naka 2015年12月8日 4:37
    2015年12月7日 2:44
  • 第一引数の文字列が第二引数のフォーマットと一致していないのにエラーにならず変換できるのですね…
    とりあえずご報告まで。

    フォーマットの/文字は、/文字自身を表すのではなく、セパレータを指定する書式文字列です。
    ここがややこしいというか、若干トラブルのもとでもあるんですが。

    OSでセパレータを変えると、変えたセパレータが認識されるので、-がセパレータとして正常に動作するわけです。

    で、以前はこの場合に/と書くと、セパレータ設定と異なるのでエラーになったのが、/でもセパレータとして認識されるようになったということなのかもしれません(未確認)。


    • 編集済み なちゃ 2015年12月7日 2:48
    2015年12月7日 2:47
  • なちゃ様

    ご回答ありがとうございます。

    すごくわかりやすく回答いただきありがとうございます。
    DateTime.ParseExact以外にもDateTime.Parseなど日付に変換するメソッドに影響ありそうですね…
    そちらに関しては別途検証してみたいと思います。

    公式ドキュメントでは見つかりませんでしたが、ソースコードより今回の動きは仕様変更だということで納得したいと思います。

    2015年12月8日 4:32
  • Azulean様となちゃ様の回答を回答としてマークさせていただきました。

    このたびは大変お世話になりました。
    まだまだ未熟ではございますが、今後ともよろしくお願いいたいます。


    • 編集済み r_naka 2015年12月8日 4:38
    2015年12月8日 4:37