西暦の日付を和暦の日付に変換してみる

2008/03/15

というような西暦の日付を、

平成20年3月15日(土)

といような形式でデータが欲しいので挑戦してみた。他のサイトを見てみるとformat関数で西暦から和暦に変換というのが検索エンジンで沢山でてきたので簡単にできるだろうとタカをくくっていたら、いや、できないんだけど・・・。Format(Now,”gggyy年MM月”)で一発変換というようなことをどこのサイトを見ても書いているので真似してやってみたら西暦08年とかわけのわからない数字を返してくれてどっと疲れた。仕方がないので地道に変換をしてみることにする。

とりあえず手がかりとして見つかったのはJapaneseCalendarクラスgetEraメソッド(ゲッテラメソッド)を使うと元号のようなものを取得できるらしいとのこと。System.Globalization.JapaneseCalendar をimportsすると使えるようになる模様。2008を平成20年という形式でデータが欲しいため、下記のgetGengoYear関数を作成してみた。date型のパラメータを渡して呼ぶと文字列型の元号プラス年で返してくれる。

元号を返してくれる関数:getGengoYear関数

    Private Function getGengoYear(ByVal targetDate As Date) As String

        'これに元号を入れる
        Dim gengo As String

        'これに和暦の年を入れる
        Dim targetGengoYear As String

        'JapaneseCalendarクラスのインスタンス化
        Dim jpCal As Globalization.JapaneseCalendar = New Globalization.JapaneseCalendar

        'パラメータの日付から元号を判断する
        Select Case jpCal.GetEra(targetDate)
            Case 1
                gengo = "明治"
            Case 2
                gengo = "大正"
            Case 3
                gengo = "昭和"
            Case Else
                gengo = "平成"
        End Select

        '元号と和暦の年とをくっつける
        targetGengoYear = gengo & jpCal.GetYear(targetDate) & "年"

        '出来上がった元号プラス和暦年を返す
        Return targetGengoYear

    End Function

これで難関の元号を取得することができた。次は3月15日(土)の部分。さっき作ったgetGengoYear関数と組み合わせて下記のようなgetJapaneseDate関数というものを作ってみた。date型のパラメータを渡してあげると、平成20年3月15日(土)という形式で返してくれる。

和暦を返してくれる関数:getJapaneseDate関数

    private Function getJapaneseDate(ByVal targetDate As Date) As String

        'これに和暦を入れる
        Dim japaneseDate As String

        'これに元号を格納する
        Dim targetYear As String = getGengoYear(targetDate)

        '曜日の条件分岐。
        If targetDate.DayOfWeek = DayOfWeek.Monday Then

            japaneseDate = targetYear & Format(targetDate, "MM月dd日") & "(月)"

        ElseIf targetDate.DayOfWeek = DayOfWeek.Tuesday Then

            japaneseDate = targetYear & Format(targetDate, "MM月dd日") & "(火)"

        ElseIf targetDate.DayOfWeek = DayOfWeek.Wednesday Then

            japaneseDate = targetYear & Format(targetDate, "MM月dd日") & "(水)"

        ElseIf targetDate.DayOfWeek = DayOfWeek.Thursday Then

            japaneseDate = targetYear & Format(targetDate, "MM月dd日") & "(木)"

        ElseIf targetDate.DayOfWeek = DayOfWeek.Friday Then

            japaneseDate = targetYear & Format(targetDate, "MM月dd日") & "(金)"

        ElseIf targetDate.DayOfWeek = DayOfWeek.Saturday Then

            japaneseDate = targetYear & Format(targetDate, "MM月dd日") & "(土)"

        Else

            japaneseDate = targetYear & Format(targetDate, "MM月dd日") & "(日)"

        End If

        '出来上がった和暦を返す
        Return japaneseDate

    End Function

サンプルコード

実際に使うときは下記のような感じになる・・・かな。

Imports System.Globalization.JapaneseCalendar

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Dim todayDate As Date = Today.Date
        MsgBox(getJapaneseDate(todayDate))

    End Sub

    private Function getJapaneseDate(ByVal targetDate As Date) As String

        'これに和暦を入れる
        Dim japaneseDate As String

        'これに元号を格納する
        Dim targetYear As String = getGengoYear(targetDate)

        '曜日の条件分岐。
        If targetDate.DayOfWeek = DayOfWeek.Monday Then

            japaneseDate = targetYear & Format(targetDate, "MM月dd日") & "(月)"

        ElseIf targetDate.DayOfWeek = DayOfWeek.Tuesday Then

            japaneseDate = targetYear & Format(targetDate, "MM月dd日") & "(火)"

        ElseIf targetDate.DayOfWeek = DayOfWeek.Wednesday Then

            japaneseDate = targetYear & Format(targetDate, "MM月dd日") & "(水)"

        ElseIf targetDate.DayOfWeek = DayOfWeek.Thursday Then

            japaneseDate = targetYear & Format(targetDate, "MM月dd日") & "(木)"

        ElseIf targetDate.DayOfWeek = DayOfWeek.Friday Then

            japaneseDate = targetYear & Format(targetDate, "MM月dd日") & "(金)"

        ElseIf targetDate.DayOfWeek = DayOfWeek.Saturday Then

            japaneseDate = targetYear & Format(targetDate, "MM月dd日") & "(土)"

        Else

            japaneseDate = targetYear & Format(targetDate, "MM月dd日") & "(日)"

        End If

        '出来上がった和暦を返す
        Return japaneseDate

    End Function

    Private Function getGengoYear(ByVal targetDate As Date) As String

        'これに元号を入れる
        Dim gengo As String

        'これに和暦の年を入れる
        Dim targetGengoYear As String

        'JapaneseCalendarクラスのインスタンス化
        Dim jpCal As Globalization.JapaneseCalendar = New Globalization.JapaneseCalendar

        'パラメータの日付から元号を判断する
        Select Case jpCal.GetEra(targetDate)
            Case 1
                gengo = "明治"
            Case 2
                gengo = "大正"
            Case 3
                gengo = "昭和"
            Case Else
                gengo = "平成"
        End Select

        '元号と和暦の年とをくっつける
        targetGengoYear = gengo & jpCal.GetYear(targetDate) & "年"

        '出来上がった元号プラス和暦年を返す
        Return targetGengoYear

    End Function

End Class

datagridの列幅の調整ができない

こんなことで嵌るのは私くらいかもしれないけれど一応備忘録

datagridviewのプロパティにある、AllowUserToResizeColumnsの値をtrueに設定しても、実際にデータをバインドしてみたら列幅の調整が効かない場合の原因。原因は、AutoSizeColumnsModeの値でAllCellsを設定していたために調整することが出来なかった模様。まあ、いわゆる機能が競合していたというか、AutoSizeColumnsModeの方が優先されていたためということかな。値はnoneに設定して、コードでcolumnのwidthを設定することに変更した。

謎のエラーが発生した・・・

‘System.InvalidOperationException’ の初回例外が System.Data.dll で発生しました。
‘System.Data.OleDb.OleDbException’の初回例外が system.data.dllで発生しました。

プログラムをリファクタリングしていたら起動時にコンソールへ上記のエラーが表示されるようになった。思い当たる節がないんだが・・・。sqlのinsert文を手直ししたけれど、起動時に引っかかる箇所じゃないし、DBのテーブルにフィールドを一個追加したことはしたけれど、起動時に見るテーブルとは無関係だし・・・。謎。

バックアップを作業前に念のためとっていたため、どこが引っかかるか再現させてみようとしたら、今度は現れない・・・。謎。

(その後)
データベースのテーブルのフィールド名を追加したり、名前を変えたりしたら、
‘System.Data.OleDb.OleDbException’の初回例外が system.data.dllで発生しました。
というエラーがまた出た。一度visual studioを終了して、プロジェクトを開きなおしたら出なくなった。またしても謎。推測だけれど、デバッグでDB接続関連のエラーが一度出たらその後もずっとで続けるのだろうか。それとも私のvisualstudioが壊れてるか。

メモ2

構文の備忘録。下記のコードは拙作のシンプルな家計簿(仮)で使っているコードです。あくまでサンプルです。

if文

if文のコードサンプル。下記のコードは時間によって挨拶文を変化させる分岐。

        '現在の時間を取得
        Dim nowHour As integer = DateTime.Now.Hour

        '現在時刻が5時から11時の間ならば、
        If (nowHour > 4) And (nowHour < 12) Then

                'ユーザ名+おはよう
                Me.hello_label.Text = title_user & "さん おはよう!"

        '現在時刻が12時から17時の間ならば、
        ElseIf (nowHour >= 12) And (nowHour < 18) Then

                'ユーザ名+こんにちは
                Me.hello_label.Text = title_user & "さん こんにちは!"

        '上記以外の場合は、
        Else

                'ユーザ名+こんばんは
                Me.hello_label.Text = title_user & "さん こんばんは!"

        End If

for文

for文のコードサンプル。下記のコードはデータグリッド内の数値がプラスかマイナスかを一行ずつ判断例。

        'データグリッドの行数を取得
        ds_record = main_form.dg_search.Rows.Count

        'データグリッドの行数分ループ
        For i = 0 To (ds_record - 1)

            'データグリッド内の金額を取得。
            cb_item = Convert.ToDecimal(main_form.dg_search.Rows(i).Cells(4).Value)

            '金額が0以下(マイナス)ならば、
            If cb_item < 0 Then

                '金額のセルのカラーを赤色に設定
                main_form.dg_search.Rows(i).Cells(4).Style.ForeColor = Color.Red

            '金額が0以下以外ならば、
            Else

                '金額のセルのカラーを緑色に設定
                main_form.dg_search.Rows(i).Cells(4).Style.ForeColor = Color.Green

            End If

        '次の行へ
        Next i

select case 文

日付の値によって元号を分岐させるselect~case文のサンプル


        Select Case jpCal.GetEra(targetDate)

            Case 1
                gengo = "明治"

            Case 2
                gengo = "大正"

            Case 3
                gengo = "昭和"

            Case Else
                gengo = "平成"

        End Select

menustripのショートカットキーが効かない

また嵌ってしまったので備忘録。vb.netのmenustripコントロールに配置されたアイテムにshortcutkeysプロパティでキーを割り当てても、ショートカットが機能しない現象について。shortcutkeysプロパティでは色々と複雑なキーを割り当てることができるようになっているけれど、menustripの項目に設置しても何の反応もない・・・。あのファイル(F)や編集(E)や表示(V)といったお決まりの項目。ショートカットキーを設定して、設定したショートカットキーを押したときにはmenustripの項目がプルダウンするというか、メソッドとしてはshow()の動作をしてもらいたいのだけれど、効かない。なんでだ?としばらく考え込んでみたところ、menustripの項目にショートカットを割り当てるには&(アンパサンド)使わなければならない模様。テキストプロパティに、例えばファイルのメニュー項目ならば”ファイル(&F)”という風に入力すると、altキー+Fキーでプルダウンするようになる。こんな単純なことだとは知らずに、危うくkeypressイベントでkeycharを拾ってshowメソッドを呼ぶしかないかと実行するところだった。

・・・で、なんでshortcutkeysプロパティが効かないんだろう?

進捗状況を表示するプログレスバー

プログレスバーの使い方

プログレスバーは、アプリケーションが何かの処理中であることをユーザに知らせてあげるという役割を持っている。時間がかかる処理などの場合にその進行状況をプログレスバーのアニメーションで明示することで、ユーザに安心感を与えることができる・・・かもしれない。

自分なりにプログレスバーの使い方を調べてみたところ、どうやらプログレスバーはプロパティの初期値と最大値、増加率を設定し、増加するタイミングにメソッドを呼び出すことで進捗率を表現する模様。具体的なプロパティ名は下記の通り。

        'プログレスバーの初期値
        Me.progressBar.Value = 0

        'プログレスバーの最大値
        Me.progressBar.Maximum = 100

        'プログレスバーの増加率
        Me.progressBar.Step = 25

        'プログレスバーの値を増加させるメソッド
        (このメソッドを呼ぶごとに上の増加率がvalueに追加される)
        Me.progressBar.PerformStep()

使い方によっては上手いやりかたがありそう。例えばデータベースに関わるプログラムを作る場合などで、プログレスバーの最大値をdatasetのレコード件数にして、プログレスバーの増加率を1にし、ループ処理中にPerformStep()を組み込むとか・・・・思いつきでやったことはないけれども。

サンプルコード

プログレスバーを使ったサンプルコードを下記に記してみる。使い方としては大体こんなイメージだろうと思う。

        Me.ProgressBar1.Maximum = recordIds.Length
        Me.ProgressBar1.Step = 1
        Me.ProgressBar1.Value = 0
        Me.ProgressBar1.Visible = True

        For i = 0 To (recordIds.Length - 1)
                sqlStr = "UPDATE targetTable SET targetCol = #" & targetVal & "# WHERE targetId = " & recordIds(i)
                obj.execSql(sqlStr)
                Me.ProgressBar1.PerformStep()
        Next i

        Me.ProgressBar1.Visible = False

メモ

現在改良中の家計簿ソフトのデータモデルを変更するかどうかについてのメモ。今のところはmicrosoftのaccessのjetという名前のデータベースでデータを管理している。作る前まではデータを保存する方法といえばこれ一択だった。技術と知識があまりないためだけれども。で、多少慣れてきたのでxml形式でのデータ保存に変更しようかどうか考え中。

microsoft accessを使うことのメリット

  • windowsアプリケーションを作る上では親和性が高い
  • スタンドアロン形式のmdbファイルを使うことができるので外部サーバなどを用意しなくてもよい
  • テーブルやクエリなどの管理が開発者にとっては容易
  • データを一元的に管理できるため楽

microsoft accessを使うことのデメリット

  • ファイルが破損した場合全てのデータが失われる可能性がある
  • ファイルサイズが大きくなった場合に処理速度が低下する可能性
  • オフィスのバージョンアップ毎に機能の変更をチェックしなければならない(?)

今のところ、accessのデメリットというものはあまり感じてない。まあ、accessに限ったことではないし、そうそう壊れるものでもないだろう・・・という楽観的な考えで、バックアップ機能を実装することで最低限これで良しとかいいかげんに考えているからなわけで。それで、accessを使うことをやめてxml形式にした場合にどうなるか考えてみた。

xmlファイルをデータ保存に使うことのメリット

  • microsoft excel 2003以降で開くことができる
  • メモ帳でも開くことができる
  • 別のプログラムで加工して利用するというようなこともできる(かも)
  • mdbに比べてファイルサイズを小さくすることができる(かも)
  • windows以外のコンピュータでも開くことができる

xmlファイルをデータ保存に使うことのデメリット

  • sqlを発行するデータベースに比べてデータの検索や追加などの処理速度が遅くなる可能性がある
  • コード量が割り増ししそうで疲れそう(開発者にとって)
  • データの秘匿・機密性というものがない(私が知らないだけかも・・・)
  • パソコン初心者にはとっつきにくい。

結論

あまり、xmlを使うことのメリットらしいものは思いつかなかった。そもそも初心者向けの家計簿ソフトでxml形式のデータがどうしても欲しいなんていうことはないだろうしなぁ。処理速度が速くなってファイルサイズが小さくなるならxmlファイル形式にすぐにでも変更したいところだけど、そうとも言えないどころかパフォーマンスは悪化しそうだ。excelで開けるといっても年に1回するかどうかもわからない機能のためにこだわるわけにもいかんし。家計簿ソフトのxmlファイル形式への変更は、中止。やっぱり今のままaccessのmdbで運用して、xmlファイル形式に出力できる機能を追加するというのがベストの予感。

datasetの内容をxmlファイルに出力する

datasetの内容をxmlに出力するのにはたった一行、writexmlという関数を使うだけでできあがる模様。あいたたた。datagridにバインドされたデータを一生懸命xml化するためにコードを考えていたのが馬鹿らしくなった。datasetの内容をxmlに変換できるというのは知らないとわからないけれど、xmlデータをdatagridにバインドできるのを知ってたのに逆を想像できなかったのが鈍すぎる。

datasetをxmlファイルに出力するサンプルコード(スマートな方法)

    'datasetを作成するための素材
    Dim cn As New OleDb.OleDbConnection("データベース接続文字列")
    Dim da As OleDb.OleDbDataAdapter
    Dim ds As DataSet

    'datasetにsqlの結果のテーブルをセット
    ds = New DataSet("家計簿")
    da = New OleDb.OleDbDataAdapter("SELECT * FROM 収支", cn)
    da.Fill(ds, "家計簿")

    'datasetにセットされたテーブルをxmlファイル出力
    ds.WriteXml("./data/account.xml")

    'datasetの解放
    da.Dispose()
    ds.Dispose()

datasetをxmlファイルに出力するサンプルコード(悪い見本)

    'datasetを作成するための素材
    Dim cn As New OleDb.OleDbConnection("データベース接続文字列")
    Dim da As OleDb.OleDbDataAdapter
    Dim ds As DataSet

    ds = New DataSet("家計簿")
    da = New OleDb.OleDbDataAdapter(SELECT * FROM 収支, cn)
    da.Fill(ds, "家計簿")

    'xmlドキュメントの宣言
    Dim xmlDoc As New System.Xml.XmlDocument

    'XML宣言
    Dim xmlDecl As System.Xml.XmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", Nothing)

    '宣言を子要素として追加
    xmlDoc.AppendChild(xmlDecl)

    'ルート要素の作成
    Dim rootEle As System.Xml.XmlElement = xmlDoc.CreateElement("家計簿データ")

    'データグリッドのレコード件数をセット
    Dim records As Integer
    Dim i As Integer
    records = ds.Tables(0).Rows.Count
    i = ds.Tables(0).Rows.Count

    '家計簿のレコードの各要素の宣言
    Dim recordElements(i) As XmlElement
    Dim idEle(i) As XmlElement
    Dim dateEle(i) As XmlElement
    Dim catEle(i) As XmlElement
    Dim detEle(i) As XmlElement
    Dim accEle(i) As XmlElement
    Dim moneyEle(i) As XmlElement
    Dim comEle(i) As XmlElement

    'datasetのテーブルからレコードの各値を取り出してノードを作成。レコード件数分ループ。
    For i = 0 To (records - 1)

        recordElements(i) = xmlDoc.CreateElement("レコード")

        dateEle(i) = xmlDoc.CreateElement("入力日")
        dateEle(i).InnerText = ds.Tables(0).Rows(i).Item(0).ToString

        catEle(i) = xmlDoc.CreateElement("費目")
        catEle(i).InnerText = ds.Tables(0).Rows(i).Item(1).ToString

        detEle(i) = xmlDoc.CreateElement("内訳")
        detEle(i).InnerText = ds.Tables(0).Rows(i).Item(2).ToString

        accEle(i) = xmlDoc.CreateElement("口座名称")
        accEle(i).InnerText = ds.Tables(0).Rows(i).Item(3).ToString

        moneyEle(i) = xmlDoc.CreateElement("収支")
        moneyEle(i).InnerText = ds.Tables(0).Rows(i).Item(4).ToString

        comEle(i) = xmlDoc.CreateElement("備考")
        comEle(i).InnerText = ds.Tables(0).Rows(i).Item(5).ToString

        idEle(i) = xmlDoc.CreateElement("id")
        idEle(i).InnerText = ds.Tables(0).Rows(i).Item(6).ToString

        recordElements(i).AppendChild(dateEle(i))
        recordElements(i).AppendChild(catEle(i))
        recordElements(i).AppendChild(detEle(i))
        recordElements(i).AppendChild(accEle(i))
        recordElements(i).AppendChild(moneyEle(i))
        recordElements(i).AppendChild(comEle(i))
        recordElements(i).AppendChild(idEle(i))

        rootEle.AppendChild(recordElements(i))

    Next i

    '作成したルート要素をxmlDocの子要素として追加
    xmlDoc.AppendChild(rootEle)

    '作成したDOMドキュメントをファイルに保存
    xmlDoc.Save("./data/account.xml")

    'datasetの解放
    da.Dispose()
    ds.Dispose()

・・・こんなしんどいことをやろうとしてました(;´д` )

datagridにバインドしたデータが120秒経つと消える・・・

訳の分からない現象が現れた。accessのmdbへselect文を発行して取得したテーブルをdatagridコントロールにバインドするようにプログラムした。そしてデバッグで表示を確かめようとしたところ、一定時間経つとdatagridに一覧表示されていたレコードがフッと消えてしまう。一応時間を計ってみたところ120秒、2分ちょうどで消える・・・。なんだこれは。datagridのプロパティをいじりすぎてぶっ壊してしまったんだろうか。

とりあえず新規にdatagridコントロールを作り直して、みたところ訳の分からない現象は出なくなった。プログラミングのミスではないところのトラブルが対処法がないだけに一番困る。

※原因が判明した。monthcarendarコントロールのdatechangeイベントが悪さをしていた模様。私のプログラムミスでした。datagridコントロール作り直してもよくみたら直ってなかったヽ(´ー`)ノ

datagridの選択した行のセルの値を取得する

備忘。.netframeworks2.0のコントロールであるdatagridコントロールで、バインドされたデータ一覧から選択された行のセル値を取得する方法。datagridのSelectedRowsプロパティを使うとセルの値を取得することができるようになる。SelectedRowsという風に複数形になっているところに気づかなくてしばらく嵌ってしまった。行を複数選択された場合のことも想定されているようなのでインデックスを指定しなければセルの値は取得できない。SelectedRows(0)という風に。

サンプルコード

Dim selectedValue As String
selectedValue = Me.datagrid1.SelectedRows(0).Cells(0).Value