VBAでUTC時刻・オフセット付き時刻を取得する（WMI使用）

VBAで時差とかを意識して時間を扱いたい場合にはWMI関連のものが使えそうだったので、備忘録としてメモ。

もっと良い方法があれば指摘していただけると嬉しいです。

# [`SWbemDateTime`](https://docs.microsoft.com/en-us/windows/desktop/wmisdk/swbemdatetime "SWbemDateTime object | Microsoft Docs")のインスタンス
WMI関連のCOMのライブラリ`Microsoft WMI Scripting V1.2 Library`内にある[`SWbemDateTime`](https://docs.microsoft.com/en-us/windows/desktop/wmisdk/swbemdatetime "SWbemDateTime object | Microsoft Docs")オブジェクトを使用すると、簡単に時刻の変換が行えます。

VBAでインスタンスする場合は以下のように行います。
`Microsoft WMI Scripting V1.2 Library`を参照することで型付きで扱うことができます。

```vb
Dim wmDate As Object 'As WbemScripting.SWbemDateTime
Set wmDate = CreateObject("WbemScripting.SWbemDateTime")
```

# SWbemDateTimeに時刻を設定する

SWbemDateTimeはインスタンス直後は値が入っていないため、時刻を設定する必要があります。

例として日本標準時で2018年8月12日 12時34分56秒を設定する場合を見てみます。

## ローカル時刻から設定

`SetVarDate`メソッドにTrueを指定することで、Date型でローカル時刻を設定できます。

```vb:fromLocalvbDate
Dim localDate As Date
localDate = #8/12/2018 12:34:56 PM#

Call wmDate.SetVarDate(localDate, True)
'Trueは省略可
Call wmDate.SetVarDate(localDate)
```

## UTCから設定

`SetVarDate`メソッドにFalseを指定することで、Date型でUTCを設定できます。

```vb:fromUTCvbDate
Dim utcDate As Date
utcDate = #8/12/2018 3:34:56 AM#

Call wmDate.SetVarDate(utcDate, False)
```

## [`CIM_DATETIME`](https://docs.microsoft.com/en-us/windows/desktop/wmisdk/cim-datetime "CIM\_DATETIME | Microsoft Docs")形式の文字列から設定

WMI関連のオブジェクトからは`yyyymmddHHMMSS.mmmmmmsUUU`の形式（[CIM\_DATETIME](https://docs.microsoft.com/en-us/windows/desktop/wmisdk/cim-datetime "CIM\_DATETIME | Microsoft Docs")）の文字列として、オフセット付き時刻を取得できます。

この文字列から時刻を設定する場合は、`Value`プロパティに値を設定します。

```vb:fromCIM_DATETIME
Dim localCIMDate As String
localCIMDate = "20180812123456.000000+540" ' = #8/12/2018 12:34:56 PM#
wmDate.Value = localCIMDate

Dim utcCIMDate As String
utcCIMDate = "20180812033456.000000+000" ' = #8/12/2018 3:34:56 AM#
wmDate.Value = utcCIMDate
```

# SWbemDateTimeから時刻を取得

## ローカル時刻として取得

`GetVarDate`メソッドにTrueを指定することで、Date型でローカル時刻を取得できます。

```vb
Debug.Print wmDate.GetVarDate(True)
'Trueは省略可
Debug.Print wmDate.GetVarDate()
```

## UTCとして取得

`GetVarDate`メソッドにFalseを指定することで、Date型でUTCを取得できます。

```vb
Debug.Print wmDate.GetVarDate(False)
```

## [`CIM_DATETIME`](https://docs.microsoft.com/en-us/windows/desktop/wmisdk/cim-datetime "CIM\_DATETIME | Microsoft Docs")として取得

`Value`プロパティからCIM_DATETIMEを取得できます。

```vb
Debug.Print wmDate.Value
```


# おまけ

## オフセット（UTCからの時差）の取得

SWbemDateTimeの`UTC`プロパティから分単位でオフセットを取得できます。

## 現在時刻をCIM_DATETIMEとして取得

WMIのOS情報から直接引っ張ってくる関数。

`SWbemLocator`さえ取得してしまえば、後は型情報が保持される（入力補完がつながる）ので1ステートメントで取得。

```vb
Function GetCIM_DATETIME() As String
    Const LocalDateTime = "LocalDateTime"
    Const WQL = "SELECT " & LocalDateTime & " FROM Win32_OperatingSystem"
    
    Dim locator As Object 'As WbemScripting.SWbemLocator
    Set locator = VBA.CreateObject("WbemScripting.SWbemLocator")
    
    Let GetCIM_DATETIME = _
            locator.ConnectServer().ExecQuery(WQL) _
                   .ItemIndex(0).Properties_ _
                   .Item(LocalDateTime).Value
    
End Function
```

## WMIのOS情報一覧表示

上記関数作成時の情報確認に使ったもの。

```vb
Sub printOSInfoByWMI()
    Dim locator As Object 'As WbemScripting.SWbemLocator
    Set locator = VBA.CreateObject("WbemScripting.SWbemLocator")
    Dim services As Object 'As WbemScripting.SWbemServices
    Set services = locator.ConnectServer()
    
    Dim objSet As Object 'As WbemScripting.SWbemObjectSet
    Set objSet = services.ExecQuery("SELECT * FROM Win32_OperatingSystem")
    
    Dim osInfo As Object 'As WbemScripting.SWbemObjectEx
    Set osInfo = objSet.ItemIndex(0)
    
    Dim prop As Object 'As WbemScripting.SWbemProperty
    For Each prop In osInfo.Properties_
        Debug.Print prop.Name, ;
        If prop.IsArray Then
            Debug.Print VBA.Join(prop.Value, ",")
        Else
            Debug.Print prop.Value
        End If
    Next prop
    
    Stop
    
End Sub
```


## レジストリのタイムゾーンの一覧

[.NET Framework における時差情報（サマータイム）の取り扱い – とあるコンサルタントのつぶやき](https://blogs.msdn.microsoft.com/nakama/2018/08/11/timezone/ ".NET Framework における時差情報（サマータイム）の取り扱い – とあるコンサルタントのつぶやき")より。

```powershell:WindowsPowerShell
Get-ChildItem -LiteralPath 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones'
```


# 参考

タイムゾーン関連全般の概要
> [タイムゾーン呪いの書 - Qiita](https://qiita.com/dmikurube/items/15899ec9de643e91497c "タイムゾーン呪いの書 - Qiita")

Windowsにおける時差関係の取り扱い
> [.NET Framework における時差情報（サマータイム）の取り扱い – とあるコンサルタントのつぶやき](https://blogs.msdn.microsoft.com/nakama/2018/08/11/timezone/ ".NET Framework における時差情報（サマータイム）の取り扱い – とあるコンサルタントのつぶやき")

WMI関連情報

> [SWbemDateTime object | Microsoft Docs](https://docs.microsoft.com/en-us/windows/desktop/wmisdk/swbemdatetime "SWbemDateTime object | Microsoft Docs")
> [CIM\_DATETIME | Microsoft Docs](https://docs.microsoft.com/en-us/windows/desktop/wmisdk/cim-datetime "CIM\_DATETIME | Microsoft Docs")
> [[WMI Class] Win32_OperatingSystem クラス - WMI Library](http://www.wmifun.net/library/win32_operatingsystem.html "[WMI Class] Win32_OperatingSystem クラス - WMI Library")