foobar2000の日付時刻データをDate型の値に変換する
foobar2000の日付時刻データをDate型の値に変換する
Date
型の値を、foobar2000独自の日付時刻文字列に変換するのは
で作成済み。
今度は、その逆をやってみる。
考え方
foobar2000のPlayback Statisticsでは、再生日時を18ケタの数字で表現している。
そのうち、整数秒までの表現で良いのなら、下7ケタは無視してもよい。
上11ケタが秒単位で表された日時である。
そして、たとえば2012年1月1日 0:00:00が12969817200
であることはわかっている。
したがって、foobar2000独自の日付時刻文字列(めんどくさいので、以下「日時コード」という。)を求めるには、
- 当該の日時コードと基準日の日時コードとの差を求める
- 上記1.を
86400
(24 * 60 * 60
)で割る - 上記2.で得た数が基準日からの日数差なので、基準日に加算する
- 上記3.で得た値が年月日
- 上記2.で得た余りを
3600
(60 * 60)で割る - 上記5.で得た商が時
- 上記5.で得た余りを
60
で割る - 上記7.で得た商が分
- 上記7.で得た余りが秒
まあ、比較的簡単に求めることができる。
必要な値の特定
日時コードは最大3種類(「FirstPlayed
」「LastPlayed
」「Added
」)あり、それぞれの要素が必ず書き込まれているとは限らないので、XMLの要素の中のどの部分が対象の日時コードであるのかは一意に決まっていない。
そこで、XML要素の中から対象の日時コードを引っ張ってくるFunctionが必要である。ただ、幸い日時コードは18文字であることが決まっているので、対象の日時コードの先頭位置さえわかれば、抜き出すことはわけもなくできる。
コーディング
……というわけで、コーディング。
まずは、XML要素の中から、対象の日時コードを引っ張ってくるFunction。
リスト1 標準モジュール宣言セクション
'Constants' Public Enum DateMode '……(1)' [_dmStart] = 3 dmFirstPlayed = [_dmStart] dmLastPlayed = dmFirstPlayed + 2 dmAdded = dmLastPlayed + 2 End Enum '2012年1月1日 0時00分00秒の日付時刻値の上11桁' Private Const STANDARD_DATE_VALUE As Currency = 12969817200# Private Const STANDARD_DATE As Date = "2012/1/1" '一日あたりの秒数' Private Const DAY_BY_SECONDS As Currency = 86400
(1)からの6行では、Enumの宣言をするのに、id:x1xy2xyz3 さんに教えてもらったテクニックを早速使ってみた。
メンバの値を「3
」、「5
」、「7
」にしているのには一応意味があるんだが、今回は関係ないので説明は省く。
後半の定数群は、コメントで記したとおり。変換計算をするときに必要な値なので、定数化しているだけ。
リスト2 標準モジュール
お次は対象の日時コードを抜き出すFunction。
Public Function getDateTimeCodeFromElement( _ ByVal targetElement As String, _ ByVal targetMode As DateMode) As String '……(2)' Dim ret As String ret = "" Dim modeStr As String Select Case targetMode '……(3)' Case dmFirstPlayed: modeStr = "FirstPlayed=""" Case dmLastPlayed: modeStr = "LastPlayed=""" Case dmAdded: modeStr = "Added=""" End Select Dim startPos As Long startPos = InStr(1, targetElement, modeStr) '……(4)' If startPos = 0 Then GoTo Finalizer '……(5)' startPos = startPos + Len(modeStr) '……(6)' ret = Mid(targetElement, startPos, 18) '……(7)' Finalizer: getDateTimeCodeFromElement = ret End Function
まず(2)の
Public Function getDateTimeCodeFromElement( _ ByVal targetElement As String, _ ByVal targetMode As DateMode) As String
で引数設定。
第1引数targetElement
で、XMLの要素文字列を受け取る。
ちなみに、XML要素文字列は、全ての内容が書き込まれていると
<Entry ID="f1a6d9d835f110fa" Count="2" FirstPlayed="129945499660000000" LastPlayed="131972743430000000" Added="131907872960000000" />
こんなクソ長ったらしいものである。
第2引数は、リスト1で設定した列挙体DateMode
型。どの日時コードを抜き出すのかを指定するのに使う。
(3)からの5行
Select Case targetMode Case dmFirstPlayed: modeStr = "FirstPlayed=""" Case dmLastPlayed: modeStr = "LastPlayed=""" Case dmAdded: modeStr = "Added=""" End Select
で、対象の日時コードを探すためのキーとなる文字列を変数targetMode
にセットする。
「FirstPlayed
」だけでなく、「FirstPlayed="
」までをキーにしているのには意味がある。後述する。
(4)の
startPos = InStr(1, targetElement, modeStr)
で、まず「FirstPlayed="
」、「LastPlayed="
」、「Added="
」を検索し、XML要素の何文字目の位置に出てくるのかを割り出す。
この段階で変数startPos
にぶち込まれるのは、たとえば「FirstPlayed="
」の先頭の「F
」の位置に過ぎない。
(5)の
If startPos = 0 Then GoTo Finalizer
はガード節。この段階でstartPos
が0
だということは、対象の日時が設定されていないということなので、ここで処理を打ち切る。
(5)をくぐり抜けたということは、対象の日時が設定されているということになるので次へ進む。
(6)の
startPos = startPos + Len(modeStr)
で日時コードの先頭位置を割り出すことができる。
たとえば、「FirstPlayed
」の場合、先頭の「F
」が20
文字目に出てくるとすると、「FirstPlayed="
」が13
文字あるので、startPos
の値は「33
」。
先頭から33
文字目というのは、「FirstPlayed="
」のすぐ次の位置なのである。
日時コードは18文字と決まっているので、あとは(7)の
ret = Mid(targetElement, startPos, 18)
で先頭位置から18文字分切り出してやればよい。
これで、日時コードを抜き出すことができる。
リスト3 標準モジュール
そして、日時コードをDate
型の値に変換するFunction。
Public Function getDateTime( _ ByVal dateCode As String) As Date Dim ret As Date '上11ケタを切り出す' Dim tmpCode As String tmpCode = Left(dateCode, 11) '基準日の値との差を求める' Dim tmpCurr As Currency tmpCurr = CCur(tmpCode) Dim dateDiff As Currency dateDiff = tmpCurr - STANDARD_DATE_VALUE '日数差を求める' Dim tmpDay As Currency tmpDay = dateDiff \ DAY_BY_SECONDS '日数差を元に日付を求める' Dim tmpDate As Date tmpDate = tmpDay + STANDARD_DATE ret = tmpDate '時刻を求める' Dim tmpTime As Currency tmpTime = dateDiff Mod DAY_BY_SECONDS Dim tmpHour As Long tmpHour = tmpTime \ 3600 Dim tmpMinute As Long tmpMinute = (tmpTime Mod 3600) \ 60 Dim tmpSecond As Long tmpSecond = ((tmpTime Mod 3600) Mod 60) '日付と時刻を合成する' ret = tmpDate + TimeSerial(tmpHour, tmpMinute, tmpSecond) getDateTime = ret End Function
まあ、コード中のコメントを見たら、何をやっているのかはわかると思う。
使ってみる
上で挙げた
<Entry ID="f1a6d9d835f110fa" Count="2" FirstPlayed="129945499660000000" LastPlayed="131972743430000000" Added="131907872960000000" />
のFirstPlayed
の日時コード「129945499660000000
」がいつのことを表すのか、調べてみる。
ちなみに、このXMLの元になった曲データは
こいつのもの。
FirstPlayed
は、「2012-10-13 06:12:46
」である。
イミディエイト・ウインドウに、
?getDateTime(getDateTimeCodeFromElement(" <entry count="" added="" lastplayed="" firstplayed="" f1a6d9d835f110fa="" 131907872960000000="" 131972743430000000="" 129945499660000000="" 2="">",dmFirstPlayed))
と打ち込んで、[Enter]。
うむ。バッチリ。
おわりに
で、何に使うんでしょう???