日付の書式について

タグの編集
投稿者 Coleman  (社会人) 投稿日時 2020/10/5 12:09:52
使用言語:VB2013(.net framework 4.5)

こんにちは。
WinFormアプリを作成していますが、処理したテキストデータを保存する時に、
例えば2020年10月5日の場合、"20201005.txt"のようにしています。

この文字列は、「Today.ToString("yyyyMMdd") & ".txt"」のようにしていますが、
一部の人では、例えば2020年10月5日だと"321005.txt"のように、
年を表す"yyyy"の部分が和暦になっています。
(おそらく、frameworkが古いため、令和2年が平成32年と認識されているものと推測します。)

令和・平成の問題はさて置き、以下の質問があります。

1.書式"yyyy"に和暦が適用されてしまうのは、Windows側の設定等が
  関係しているのでしょうか?

2.いかなる場合でも、書式"yyyy"に西暦4桁を適用するには、
  VB側でどのようにすればいいでしょうか?

よろしくお願いします。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/10/5 12:53:56
> 1.書式"yyyy"に和暦が適用されてしまうのは、Windows側の設定等が
>   関係しているのでしょうか?

Windows の地域設定です。

たとえば Today.ToString("MM/dd") が "10-05" になる可能性もありますし、
1234.ToString("#,##0") が "1.234" になる可能性もあります。





> 2.いかなる場合でも、書式"yyyy"に西暦4桁を適用するには、
>   VB側でどのようにすればいいでしょうか?
ToString メソッドや String.Format メソッドの引数に、
CultureInfo クラスのインスタンスを渡します。
たとえば CultureInfo.InvariantCulture など。
https://qiita.com/tomboyboy/items/daf0d8427ba392c11a53

あるいは My.Application.ChangeCulture を使う手も。
投稿者 Coleman  (社会人) 投稿日時 2020/10/5 13:20:59
魔界の仮面弁士さま、ご丁寧にご説明頂きありがとうございました。
CultureInfoを引数に渡す方法は知らなかったので、勉強になりました。

あと、My.Application.ChangeCultureだと一度に全体の対応ができそうなので、こちらを試してみようと思います。
これは引数の型がStringですが、CultureInfo.InvariantCultureに相当するカルチャを設定するには
My.Application.ChangeCulture("")でよいのでしょうか?

※以下「Application​Base.​Change​Culture(String) メソッド」を参照しました。
https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.visualbasic.applicationservices.applicationbase.changeculture?view=netframework-4.8
投稿者 Coleman  (社会人) 投稿日時 2020/10/19 17:51:29
とりあえず、My.Application.ChangeCulture("")を行うことで、
日付のToString("yyyyMMdd") は、西暦年4桁+月2桁+日2桁(月と日は左側0埋め)になったようですが、
これとは別に、例えば、Decimal変数dec1に対して
dec1 = "10.2"
のように全角数字文字列を与えると例外が発生します。

この例外はSystem.InvalidCastExceptionで、要はDecimalに変換できない、ということで、
本来ならば半角数字にしたものをDecimalにキャストすべきところを
それを忘れていた所を発見できて、これはこれで良かったのですが、
その他、My.Application.ChangeCulture("")を行うことで、
どのような影響が出てくるでしょうか?
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/10/19 19:45:43
My.Application.ChangeCulture() には、"ja" や "ja-JP" などのカルチャ名を渡します。
"" を渡すと、ニュートラルカルチャ(米語相当)となります。


ただし My.Application.ChangeCulture では、日付設定の
「西暦 (日本語)」「西暦 (英語)」「和暦」といった細かい設定が行えません。
カレンダーまで指定する場合は、System.Globalization.CultureInfo.CurrentCulture を
差し替える方法を使います。


これは、CultureInfo の DateTimeFormat.Calendar を切り替えることで対応できます。

Private jp As New System.Globalization.CultureInfo("ja-JP")   '日本のカルチャ 
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim dic As New Dictionary(Of String, System.Globalization.Calendar)()
    Dim dfi = DirectCast(jp.DateTimeFormat.Clone(), System.Globalization.DateTimeFormatInfo)
    For Each cal In jp.OptionalCalendars   '日本のカルチャで使用可能なカレンダーを列挙 
        dfi.Calendar = cal
        dic.Add(dfi.NativeCalendarName, cal)
        Debug.WriteLine(cal.GetType().FullName)
    Next
    ComboBox1.DisplayMember = "Key"
    ComboBox1.ValueMember = "Value"
    ComboBox1.DataSource = dic.ToArray()  '使用可能なカレンダーを ComboBox に列挙してみます 
End Sub
Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
    ' カレンダーを変更してみてみます 
    Dim cal = TryCast(ComboBox1.SelectedValue, System.Globalization.Calendar)
    If cal Is Nothing Then
        TextBox1.Text = ""
        TextBox2.Text = ""
    Else
        jp.DateTimeFormat.Calendar = cal
        System.Globalization.CultureInfo.CurrentCulture = jp
        TextBox1.Text = Now.ToString("G")

        ' CurrentCulture を差し替えるのではなく、Format あるいは ToString 時に 
        ' カルチャを指定する方法もあります。この方法だと、現在のカルチャに依存せずに 
        ' 書式を切り替えられますので、たとえば「令和2年(2020年)」などのような 
        ' 複数カルチャの文字列を扱う際にも応用できます。 
        TextBox2.Text = String.Format(jp, "{0:ggg yyyy/MM/dd HH:mm:ss.fffffff}", Now)
    End If
End Sub
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/10/19 20:15:05
> その他、My.Application.ChangeCulture("")を行うことで、
> どのような影響が出てくるでしょうか?
たとえば、日付書式なら、gg や MMMM や t などに影響を与えます。
数値書式なら、Double.PositiveInfinity.ToString() なども変わりますね。


> dec1 = "10.2"
> のように全角数字文字列を与えると例外が発生します。
これを許可したいなら、 "ja-JP" のカルチャのままにした方が良いでしょう。
String.Format や ToString や TryParse の際にカルチャを明示することで、
文字列への/文字列からの変換を現在のカルチャに依存させることなく
開発者が定めた特定のカルチャに基づいて行わせることができます。


TextBox1.Text = String.Format(CultureInfo.InvariantCulture, "{0:ggg yyyy/MM/dd HH:mm:ss.fffffff}", Now)



なお、使用可能なカルチャの一覧は、CultureInfo.GetCultures メソッドで得られます。

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    ComboBox1.DataSource = System.Globalization.CultureInfo.GetCultures(System.Globalization.CultureTypes.AllCultures)
End Sub
Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
    ListBox1.Items.Clear()
    Dim ci = TryCast(ComboBox1.SelectedItem, System.Globalization.CultureInfo)
    If ci Is Nothing Then
        Return
    Else
        System.Threading.Thread.CurrentThread.CurrentCulture = ci
    End If

    TextBox1.Text = Now.ToString("G")
    TextBox2.Text = 1234.5678D.ToString("G")

    ListBox1.Items.Add($"Name = {ci.Name}")
    ListBox1.Items.Add($"EnglishName = {ci.EnglishName}")
    ListBox1.Items.Add($"NativeName = {ci.NativeName}")
    ListBox1.Items.Add($"DisplayName = {ci.DisplayName}")
    ListBox1.Items.Add($"IetfLanguageTag = {ci.IetfLanguageTag}")
    ListBox1.Items.Add($"TwoLetterISOLanguageName = {ci.TwoLetterISOLanguageName}")
    ListBox1.Items.Add($"ThreeLetterISOLanguageName = {ci.ThreeLetterISOLanguageName}")
    ListBox1.Items.Add($"ThreeLetterWindowsLanguageName = {ci.ThreeLetterWindowsLanguageName}")
    ListBox1.Items.Add($"LCID = {ci.LCID}")
End Sub


各カルチャの Name が、My.Application.ChangeCulture に渡せる文字列となりますが、
先の例のように、細かい制御ができないなどの理由から、「CurrentCulture を指定」するか
「変換時にカルチャを明示する」方が確実かもしれません。(自分は後者を使うことが多い)
投稿者 Coleman  (社会人) 投稿日時 2020/10/20 11:22:04
魔界の仮面弁士さま、いつもご丁寧にご説明頂きありがとうございます。
なるほど。色々と勉強してみます。
ありがとうございました。