西暦から和暦へ

タグの編集
投稿者 YUU  (社会人) 投稿日時 2015/11/24 17:49:14
        '現在入力チェック用のプログラムを作成しております。
        'DBから取得した生年月日(YYYY/MM/DD)を年号省略状態でフォーム上に表示する必要があり困っております。
        '下記はイメージ

        Dim cultureInfo As DateTimeFormatInfo = New CultureInfo("ja-JP").DateTimeFormat
        cultureInfo.Calendar() = New JapaneseCalendar()
        Console.WriteLine(Date.Now.ToString("ggyy年MM月dd日", cultureInfo))
        '平成27年11月24日

        '現在は下記の手法で取得し変換しております。
    変換したいデータ 例:H27/11/24
        'これだとなんだか微妙・・・
        Dim aaa As New JapaneseCalendar

        Console.WriteLine(aaa.GetYear(Date.Now))
        Console.WriteLine(aaa.GetEra(Date.Now))
        '1:明治(M)、2:大正(T)、3:昭和(S)、4:平成(H)


        'テキストボックスに入力された日付の整合性をチェックしたいのが目的です。
        'DateTime.Parseでチェックはできるようですが・・・。
        '上記の手法で変換は可能かと思われますがその他の手法はございますでしょうか。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2015/11/24 19:46:43
アルファベット一文字を取得するためのスマートな方法は用意されていません。
"昭和" や "昭" なら得られるのですけれどね。

Parse はできるのに Format は出来ない謎仕様。



(案1) Reflection を用いて、非公開メンバー「AbbreviatedEnglishEraNames」に
 アクセスして取り出す。

(案2) 自前で変換表を用意する。

(案3) Windows 7 以降限定で、レジストリの
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese
にある、西暦/和暦の元号対応を参照する。

(案4) VarFormat API (VBA の Format 関数) で、書式 "GEE\/MM\/DD" を指定する。



自分の場合は、「DateTime → 和暦String への変換」、
および「和暦String → DateTime の解析変換」のために
自作ライブラリを作って対処しています。上記で言うと 案2 ですね。

(.NET のクラスだけでなく、データベースのストアド関数でも同種の物を用意)


> Console.WriteLine(Date.Now.ToString("ggyy年MM月dd日", cultureInfo))
ちなみに、案3 のレジストリに
 "2045 01 23" = "元号_元_Gengo_G"
と登録しておくと、
 Dim dt As Date = #11/22/2067#
 Console.WriteLine(dt.ToString("g.y.M.d", cultureInfo))
というコードによって、
 .NET 4 以降の場合 … 「元号.23.11.22」
 .NET 4 未満の場合 … 「平成.79.11.22」
が得られます。


元号って、突き詰めると結構ややこしいんですよね…。
昭和→平成は良いのですが、昭和以前が何とも。
http://hanatyan.sakura.ne.jp/logbbs1/wforum.cgi?mode=allread&no=8698&page=0

【日付を遡って改元された時期がある】
 「1868年1月25日~1868年10月23日」は、
 「慶応4年1月1日~慶應4年9月8日」であると同時に
 「明治元年1月1日~明治元年9月8日」でもある。

【明治時代に、旧暦から新暦の移行が行われた】
 新暦「明治6年1月1日(1873年1月1日)」の『前日』は、旧暦「明治5年12月2日」(1872年12月31日)

【同じ日に複数の元号が割り当てられている】
 「大正15年12月25日」と『同日』=「昭和元年12月25日」
 「明治45年7月30日」と『同日』=「大正元年7月30日」
投稿者 YUU  (社会人) 投稿日時 2015/11/24 21:07:02
 魔界の仮面弁士様いつも返信ありがとうございます。

いくつも案を出していただき助かりました。

>Parse はできるのに Format は出来ない謎仕様。
↑には驚かされました。まさかの落とし穴が。

>非公開メンバー「AbbreviatedEnglishEraNames」
非推奨とはいえこれも一つの手ですね。ありがとうございます。

> Windows 7 以降限定で、レジストリの
これはこちらで調べた際に上がった候補の一つではあったのですが環境に依存するため不採用ですね。

>VBA の Format 関数で、書式
一応、Format関数は対応していたのですね。勉強不足でした。

>自前で変換表
この処理専用のクラスということでしょうか。ちなみに参考までにどのような処理をされているのですか?差支えなければお伺いしたいのですが。Case文のような分岐処理?ですかね。

>元号って、突き詰めると結構ややこしいんです
たかが元号、されど・・ですかね。消費税等と同じくいつかは変化するものですから。

投稿者 shu  (社会人) 投稿日時 2015/11/24 23:15:56
変換表バージョンかなり手抜き

        Dim cultureInfo As DateTimeFormatInfo = New CultureInfo("ja-JP").DateTimeFormat
        cultureInfo.Calendar() = New JapaneseCalendar()
        Dim tmp = DateTimePicker1.Value.ToString("ggyy/MM/dd", cultureInfo)
        Dim dic As New Dictionary(Of StringStringFrom {{"明治""M"}, {"大正""T"}, {"昭和""S"}, {"平成""H"}}
        TextBox1.Text = dic(tmp.Substring(0, 2)) & tmp.Substring(2)
投稿者 YUU  (社会人) 投稿日時 2015/11/25 20:59:38
 shu様、返信ありがとうございます。

変換表というのはDictionaryを指していたのですね。keyとvalueをセットし良きように変換する。

うまく処理できました。和暦変換はできたのですが、それの正否判定にてお聞きしたいことがあります。
下記が詳細のコードです。生年月日の判定を行っております。(txtDateはテキストボックス)

(txtDateは既に和暦変換済み。例:H27/11/20)
Dim dt As DateTime           
If DateTime.TryParse(txtDate.Text, dt) = False Then
    '変換失敗
    Return
Else
    Dim cultureInfo As DateTimeFormatInfo = New CultureInfo("ja-JP").DateTimeFormat
    cultureInfo.Calendar() = New JapaneseCalendar()
    txtDate.Text = CStr(DateTime.Parse(txtDate.Text, cultureInfo))
    Return
    'OK
End If

↑は変換された和暦に対して修正をかけた際にテキストボックスからフォーカスが離れたときにおこるイベントの一部です。
もう少し行数を減らせたり他の方法があったりするのでしょうか?
    Dim cultureInfo As DateTimeFormatInfo = New CultureInfo("ja-JP").DateTimeFormat
    cultureInfo.Calendar() = New JapaneseCalendar()

Dim dt As DateTime           
If DateTime.TryParse(txtDate.Text, dt) = False Then
End If
↑も一行でまとめられるのか?

お知恵を貸していただけると幸いです。

投稿者 魔界の仮面弁士  (社会人) 投稿日時 2015/11/26 12:10:57
> txtDate.Text = CStr(DateTime.Parse(txtDate.Text, cultureInfo))
CStr(日付型) にすると、コントロールパネルの地域設定に依存して
出力結果が異なりますが、大丈夫でしょうか?
(たとえば、地域設定を和暦モードにしていた場合など)

ついでに言えば、個人的には .TryParseExact を使った方が好みです。


> もう少し行数を減らせたり他の方法があったりするのでしょうか?
利用者側のイベント実装が毎回面倒だというのなら、
拡張メソッドや継承コントロールなどの手法を用いて、
よく使う機能をライブラリ化してみてはいかがでしょう。

たとえば、DateTime 型に、「和暦を返す拡張メソッド」を用意するとか。


この手の入力ボックスは、複数の画面、あるいは複数のプロジェクトで使われるため、
自分のところだと、利用側のコードを簡潔にするために、
入力検証系のイベントを拡張したフォームやコントロール、
日付入力特化型の TextBox 継承クラス等の自社ライブラリを用意していたりします。
投稿者 YUU  (社会人) 投稿日時 2015/12/1 23:50:15
返信遅れてすみませんでした。

無事解決しました。

>ついでに言えば、個人的には .TryParseExact を使った方が好みです。
上記へと変更しました。

>自作ライブラリ
今回限りの単発処理のため見送りました。

想定していたより処理が複雑になったので少し見直してみたいと思います。
ありがとうございました。