tech.guitarrapc.cóm

Technical updates

PowerShellで[DateTime]::ParseExact利用時にCultureInfoを指定する

前回、[DateTime]::ParseExactメソッドを利用したカスタム日付書式について紹介しました。
PowerShellで日付書式にカスタム書式パターンを指定する
今回は、CultureInfoを指定してみます。

現在のCultureInfoを取得する

まずはCultureInfoクラスから確認します。
MSDN - CultureInfo Class
どうやらCurrentCultureプロパティから取得できますね。 以下のコードで取得できます。
[System.Globalization.CultureInfo]::CurrentCulture
現在のThreadで取得しましょう。
$currentThread = [System.Threading.Thread]::CurrentThread
$currentThread.CurrentCulture
取得結果です。
LCID             Name             DisplayName
----             ----             -----------
1041             ja-JP            日本語 (日本)

CultureInfoの一覧を取得する

何はともあれ、指定できるCultureInfoの一覧が知りたいですね。 必要な情報は、CultureInfo.CreateSpecificCultureで取得できそうです。
MSDN - CultureInfo.CreateSpecificCulture Method
PowerShellでは、このコードでSpecificCulturesのコード一覧を取得できます。
[System.Globalization.Cultureinfo]::getcultures([System.Globalization.CultureTypes]::SpecificCultures)
取得した一覧です。
LCID  Name           DisplayName
----  ----           -----------
1025  ar-SA          アラビア語 (サウジアラビア)
1026  bg-BG          ブルガリア語 (ブルガリア)
1027  ca-ES          カタルニア語 (カタルニア)
1028  zh-TW          中国語 (繁体字、台湾)
1029  cs-CZ          チェコ語 (チェコ共和国)
1030  da-DK          デンマーク語 (デンマーク)
1031  de-DE          ドイツ語 (ドイツ)
1032  el-GR          ギリシャ語 (ギリシャ)
1033  en-US          英語 (米国)
1035  fi-FI          フィンランド語 (フィンランド)
1036  fr-FR          フランス語 (フランス)
1037  he-IL          ヘブライ語 (イスラエル)
1038  hu-HU          ハンガリー語 (ハンガリー)
1039  is-IS          アイスランド語 (アイスランド)
1040  it-IT          イタリア語 (イタリア)
1041  ja-JP          日本語 (日本)
1042  ko-KR          韓国語 (韓国)
1043  nl-NL          オランダ語 (オランダ)
1044  nb-NO          ノルウェー語 ブークモール (ノルウェー)
1045  pl-PL          ポーランド語 (ポーランド)
1046  pt-BR          ポルトガル語 (ブラジル)
1047  rm-CH          ロマンシュ語 (スイス)
1048  ro-RO          ルーマニア語 (ルーマニア)
1049  ru-RU          ロシア語 (ロシア)
1050  hr-HR          クロアチア語 (クロアチア)
1051  sk-SK          スロバキア語 (スロバキア)
1052  sq-AL          アルバニア語 (アルバニア)
1053  sv-SE          スウェーデン語 (スウェーデン)
1054  th-TH          タイ語 (タイ)
1055  tr-TR          トルコ語 (トルコ)
1056  ur-PK          ウルドゥー語 (パキスタン・イスラム共和国)
1057  id-ID          インドネシア語 (インドネシア)
1058  uk-UA          ウクライナ語 (ウクライナ)
1059  be-BY          ベラルーシ語 (ベラルーシ)
1060  sl-SI          スロベニア語 (スロベニア)
1061  et-EE          エストニア語 (エストニア)
1062  lv-LV          ラトビア語 (ラトビア)
1063  lt-LT          リトアニア語 (リトアニア)
1064  tg-Cyrl-TJ     タジク語 (キリル、タジキスタン)
1065  fa-IR          ペルシャ語
1066  vi-VN          ベトナム語 (ベトナム)
1067  hy-AM          アルメニア語 (アルメニア)
1068  az-Latn-AZ     アゼルバイジャン語 (ラテン、アゼルバイジャン)
1069  eu-ES          バスク語 (バスク)
1070  hsb-DE         上ソルブ語 (ドイツ)
1071  mk-MK          マケドニア語 (マケドニア旧ユーゴスラビア共和国)
1074  tn-ZA          セツワナ語 (南アフリカ)
1076  xh-ZA          コサ語 (南アフリカ)
1077  zu-ZA          ズールー語 (南アフリカ)
1078  af-ZA          アフリカーンス語 (南アフリカ)
1079  ka-GE          グルジア語 (グルジア)
1080  fo-FO          フェロー語 (フェロー諸島)
1081  hi-IN          ヒンディー語 (インド)
1082  mt-MT          マルタ語 (マルタ)
1083  se-NO          北サーミ語 (ノルウェー)
1086  ms-MY          マレー語 (マレーシア)
1087  kk-KZ          カザーフ語 (カザフスタン)
1088  ky-KG          キルギス語 (キルギス)
1089  sw-KE          スワヒリ語 (ケニア)
1090  tk-TM          トルクメン語 (トルクメニスタン)
1091  uz-Latn-UZ     ウズベク語 (ラテン、ウズベキスタン)
1092  tt-RU          タタール語 (ロシア)
1093  bn-IN          ベンガル語 (インド)
1094  pa-IN          パンジャーブ語 (インド)
1095  gu-IN          グジャラート語 (インド)
1096  or-IN          オリヤー語 (インド)
1097  ta-IN          タミール語 (インド)
1098  te-IN          テルグ語 (インド)
1099  kn-IN          カナラ語 (インド)
1100  ml-IN          マラヤラム語 (インド)
1101  as-IN          アッサム語 (インド)
1102  mr-IN          マラーティー語 (インド)
1103  sa-IN          サンスクリット語 (インド)
1104  mn-MN          モンゴル語 (キリル、モンゴル)
1105  bo-CN          チベット語 (中国)
1106  cy-GB          ウェールズ語 (英国)
1107  km-KH          クメール語 (カンボジア)
1108  lo-LA          ラオス語 (ラオス人民民主共和国)
1110  gl-ES          ガリシア語 (ガリシア)
1111  kok-IN         コンカニ語 (インド)
1114  syr-SY         シリア語 (シリア)
1115  si-LK          シンハラ語 (スリランカ)
1116  chr-Cher-US    チェロキー語 (チェロキー)
1117  iu-Cans-CA     イヌクティトット語 (カナダ音節文字、カナダ)
1118  am-ET          アムハラ語 (エチオピア)
1121  ne-NP          ネパール語 (ネパール)
1122  fy-NL          フリジア語 (オランダ)
1123  ps-AF          パシュトゥー語 (アフガニスタン)
1124  fil-PH         フィリピノ語 (フィリピン)
1125  dv-MV          ディヘビ語 (モルディブ)
1128  ha-Latn-NG     ハウサ語 (ラテン、ナイジェリア)
1130  yo-NG          ヨルバ語 (ナイジェリア)
1131  quz-BO         ケチュア語 (ボリビア)
1132  nso-ZA         セソト サ レボア語 (南アフリカ)
1133  ba-RU          バシュキール語 (ロシア)
1134  lb-LU          ルクセングルグ語 (ルクセンブルグ)
1135  kl-GL          グリーンランド語 (グリーンランド)
1136  ig-NG          イボ語 (ナイジェリア)
1139  ti-ET          ティグリニア語 (エチオピア)
1141  haw-US         ハワイ語 (米国)
1144  ii-CN          イ語 (中国)
1146  arn-CL         マプドゥングン語 (チリ)
1148  moh-CA         モホーク語 (モホーク)
1150  br-FR          ブルトン語 (フランス)
1152  ug-CN          ウイグル語 (中国)
1153  mi-NZ          マオリ語 (ニュージーランド)
1154  oc-FR          オクシタン語 (フランス)
1155  co-FR          コルシカ語 (フランス)
1156  gsw-FR         アルザス語 (フランス)
1157  sah-RU         サハ語 (ロシア)
1158  qut-GT         キチェ語 (グアテマラ)
1159  rw-RW          キニヤルワンダ語 (ルワンダ)
1160  wo-SN          ウォロフ語 (セネガル)
1164  prs-AF         ダリー語 (アフガニスタン)
1169  gd-GB          スコットランド ゲール語 (英国)
1170  ku-Arab-IQ     中央クルド語 (イラク)
2049  ar-IQ          アラビア語 (イラク)
2051  ca-ES-valencia バレンシア語 (スペイン)
2052  zh-CN          中国語 (簡体字、中国)
2055  de-CH          ドイツ語 (スイス)
2057  en-GB          英語 (英国)
2058  es-MX          スペイン語 (メキシコ)
2060  fr-BE          フランス語 (ベルギー)
2064  it-CH          イタリア語 (スイス)
2067  nl-BE          オランダ語 (ベルギー)
2068  nn-NO          ノルウェー語 ニーノシク (ノルウェー)
2070  pt-PT          ポルトガル語 (ポルトガル)
2074  sr-Latn-CS     セルビア語 (ラテン、セルビアおよびモンテネグロ (旧))
2077  sv-FI          スウェーデン語 (フィンランド)
2092  az-Cyrl-AZ     アゼルバイジャン語 (キリル、アゼルバイジャン)
2094  dsb-DE         下ソルブ語 (ドイツ)
2098  tn-BW          セツワナ語 (ボツワナ)
2107  se-SE          北サーミ語 (スウェーデン)
2108  ga-IE          アイルランド語 (アイルランド)
2110  ms-BN          マレー語 (ブルネイ・ダルサラーム国)
2115  uz-Cyrl-UZ     ウズベク語 (キリル、ウズベキスタン)
2117  bn-BD          ベンガル語 (バングラデシュ)
2118  pa-Arab-PK     パンジャブ語 (パキスタン・イスラム共和国)
2121  ta-LK          タミール語 (スリランカ)
2128  mn-Mong-CN     モンゴル語 (伝統的モンゴル文字、中国)
2137  sd-Arab-PK     シンド語 (パキスタン・イスラム共和国)
2141  iu-Latn-CA     イヌクティトット語 (ラテン、カナダ)
2143  tzm-Latn-DZ    タマジット語 (ラテン、アルジェリア)
2151  ff-Latn-SN     フラニ語 (ラテン、セネガル)
2155  quz-EC         ケチュア語 (エクアドル)
2163  ti-ER          ティグリニア語 (エリトリア)
3073  ar-EG          アラビア語 (エジプト)
3076  zh-HK          中国語 (繁体字、香港)
3079  de-AT          ドイツ語 (オーストリア)
3081  en-AU          英語 (オーストラリア)
3082  es-ES          スペイン語 (スペイン)
3084  fr-CA          フランス語 (カナダ)
3098  sr-Cyrl-CS     セルビア語 (キリル、セルビアおよびモンテネグロ (旧))
3131  se-FI          北サーミ語 (フィンランド)
3179  quz-PE         ケチュア語 (ペルー)
4097  ar-LY          アラビア語 (リビア)
4100  zh-SG          中国語 (簡体字、シンガポール)
4103  de-LU          ドイツ語 (ルクセンブルグ)
4105  en-CA          英語 (カナダ)
4106  es-GT          スペイン語 (グアテマラ)
4108  fr-CH          フランス語 (スイス)
4122  hr-BA          クロアチア語 (ラテン、ボスニア ヘルツェゴビナ)
4155  smj-NO         ルレ サーミ語 (ノルウェー)
4191  tzm-Tfng-MA    中央アトラス タマジット語 (ティフィナグ、モロッコ)
5121  ar-DZ          アラビア語 (アルジェリア)
5124  zh-MO          中国語 (繁体字、マカオ)
5127  de-LI          ドイツ語 (リヒテンシュタイン)
5129  en-NZ          英語 (ニュージーランド)
5130  es-CR          スペイン語 (コスタリカ)
5132  fr-LU          フランス語 (ルクセンブルグ)
5146  bs-Latn-BA     ボスニア語 (ラテン、ボスニア ヘルツェゴビナ)
5179  smj-SE         ルレ サーミ語 (スウェーデン)
6145  ar-MA          アラビア語 (モロッコ)
6153  en-IE          英語 (アイルランド)
6154  es-PA          スペイン語 (パナマ)
6156  fr-MC          フランス語 (モナコ)
6170  sr-Latn-BA     セルビア語 (ラテン、ボスニア ヘルツェゴビナ)
6203  sma-NO         南サーミ語 (ノルウェー)
7169  ar-TN          アラビア語 (チュニジア)
7177  en-ZA          英語 (南アフリカ)
7178  es-DO          スペイン語 (ドミニカ共和国)
7194  sr-Cyrl-BA     セルビア語 (キリル、ボスニア ヘルツェゴビナ)
7227  sma-SE         南サーミ語 (スウェーデン)
8193  ar-OM          アラビア語 (オマーン)
8201  en-JM          英語 (ジャマイカ)
8202  es-VE          スペイン語 (ベネズエラ ボリバル共和国)
8218  bs-Cyrl-BA     ボスニア語 (キリル、ボスニア ヘルツェゴビナ)
8251  sms-FI         スコルト サーミ語 (フィンランド)
9217  ar-YE          アラビア語 (イエメン)
9225  en-029         英語 (カリブ)
9226  es-CO          スペイン語 (コロンビア)
9242  sr-Latn-RS     セルビア語 (ラテン、セルビア)
9275  smn-FI         イナリ サーミ語 (フィンランド)
10241 ar-SY          アラビア語 (シリア)
10249 en-BZ          英語 (ベリーズ)
10250 es-PE          スペイン語 (ペルー)
10266 sr-Cyrl-RS     セルビア語 (キリル、セルビア)
11265 ar-JO          アラビア語 (ヨルダン)
11273 en-TT          英語 (トリニダード・トバゴ)
11274 es-AR          スペイン語 (アルゼンチン)
11290 sr-Latn-ME     セルビア語 (ラテン、モンテネグロ)
12289 ar-LB          アラビア語 (レバノン)
12297 en-ZW          英語 (ジンバブエ)
12298 es-EC          スペイン語 (エクアドル)
12314 sr-Cyrl-ME     セルビア語 (キリル、モンテネグロ)
13313 ar-KW          アラビア語 (クウェート)
13321 en-PH          英語 (フィリピン共和国)
13322 es-CL          スペイン語 (チリ)
14337 ar-AE          アラビア語 (アラブ首長国連邦)
14346 es-UY          スペイン語 (ウルグアイ)
15361 ar-BH          アラビア語 (バーレーン)
15370 es-PY          スペイン語 (パラグアイ)
16385 ar-QA          アラビア語 (カタール)
16393 en-IN          英語 (インド)
16394 es-BO          スペイン語 (ボリビア)
17417 en-MY          英語 (マレーシア)
17418 es-SV          スペイン語 (エルサルバドル)
18441 en-SG          英語 (シンガポール)
18442 es-HN          スペイン語 (ホンジュラス)
19466 es-NI          スペイン語 (ニカラグア)
20490 es-PR          スペイン語 (プエルトリコ)
21514 es-US          スペイン語 (米国)

ThreadのCultrueInfoを指定する

まず、先ほど取得したコードをCultureInfoに指定します。 例えば、USを指定するならこうです。
[System.Globalization.CultureInfo]::CreateSpecificCulture("en-US")
あとは、現在のThreadに指定するだけですね。 現ThreadのCultureは、CurrentCultureCurrentUICultureプロパティです。 このコードで指定します。
$currentThread = [System.Threading.Thread]::CurrentThread
$culture = [System.Globalization.CultureInfo]::CreateSpecificCulture("en-US")
$currentThread.CurrentCulture = $culture
$currentThread.CurrentUICulture = $culture
さあ、出力してみましょう。
$currentThread = [System.Threading.Thread]::CurrentThread
$culture = [System.Globalization.CultureInfo]::CreateSpecificCulture("en-US")
$currentThread.CurrentCulture = $culture
$currentThread.CurrentUICulture = $culture

[DateTime]::ParseExact("20121112","yyyyMMdd",[System.Globalization.CultureInfo]::CurrentCulture)
[DateTime]::ParseExact("20121112","yyyyMMdd",[System.Globalization.CultureInfo]::InvariantCulture)
[DateTime]::ParseExact("20121112","yyyyMMdd",$null)
出力結果です。
Monday, November 12, 2012 12:00:00 AM
Monday, November 12, 2012 12:00:00 AM
Monday, November 12, 2012 12:00:00 AM
勿論日本語(ja-JP)を指定しなおせば、日本語書式になります。
$currentThread = [System.Threading.Thread]::CurrentThread
$culture = [System.Globalization.CultureInfo]::CreateSpecificCulture("en-US")
$currentThread.CurrentCulture = $culture
$currentThread.CurrentUICulture = $culture

[DateTime]::ParseExact("20121112","yyyyMMdd",[System.Globalization.CultureInfo]::CurrentCulture)
[DateTime]::ParseExact("20121112","yyyyMMdd",[System.Globalization.CultureInfo]::InvariantCulture)
[DateTime]::ParseExact("20121112","yyyyMMdd",$null)



$newCulture = [System.Globalization.CultureInfo]::CreateSpecificCulture("ja-JP")
$currentThread.CurrentCulture = $newCulture
$currentThread.CurrentUICulture = $newCulture

[DateTime]::ParseExact("20121112","yyyyMMdd",[System.Globalization.CultureInfo]::CurrentCulture)
[DateTime]::ParseExact("20121112","yyyyMMdd",[System.Globalization.CultureInfo]::InvariantCulture)
[DateTime]::ParseExact("20121112","yyyyMMdd",$null)
結果です。
Monday, November 12, 2012 12:00:00 AM
Monday, November 12, 2012 12:00:00 AM
Monday, November 12, 2012 12:00:00 AM
2012年11月12日 0:00:00
2012年11月12日 0:00:00
2012年11月12日 0:00:00

読み取り文字列のCultrueInfoを指定する

この応用で、読み取りたい書式のCultureInfoが現在のCultrureInfoと異なっていても読み取らせることが可能です。 例えば、現在がja-JPの時に、MMMにNovが指定されても通常は読めません。
[System.Threading.Thread]::CurrentThread.CurrentCulture.Name

[DateTime]::ParseExact("12/Nov/2012 13:59:51","dd/MMM/yyyy HH:mm:ss",$null)
当然例外が出ます。
ja-JP

"3" 個の引数を指定して "ParseExact" を呼び出し中に例外が発生しました: "文字列は有効な DateTime ではありませんでした。"
発生場所 C:\Users\acquire\AppData\Local\Temp\fd6f6e29-713f-486c-82cb-e3b7f1d513b9.ps1:9 文字:1
+ [DateTime]::ParseExact("12/Nov/2012 13:59:51","dd/MMM/yyyy HH:mm:ss",$null)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : FormatException
そこで、読み取り時のCultureInfoにen-USを指定すれば読み取り可能です。
[System.Threading.Thread]::CurrentThread.CurrentCulture.Name

[DateTime]::ParseExact("12/Nov/2012 13:59:51","dd/MMM/yyyy HH:mm:ss",[System.Globalization.CultureInfo]::CreateSpecificCulture("en-US"))
読めましたね。
ja-JP

2012年11月12日 13:59:51

読み取り日付書式をパターンとしておく

これまでの知識を利用すると、このようなことが可能になります。
Parsing Custom Date and Time Formats
これを、日本語ロケールのPCで実行しても、CultureInfoが異なるためにエラーが出ますが、en-USを指定することで実行できます。
$information = '12Nov(2012)18h30m17s'
$pattern = 'ddMMM\(yyyy\)HH\hmm\mss\s'
[datetime]::ParseExact($information, $pattern, [System.Globalization.CultureInfo]::CreateSpecificCulture("en-US"))
読み取れました。
2012年11月12日 18:30:17
これの素晴らしい所は、カスタム書式をパターンとして変数に保持して読み取りに一貫性を持たせられることを示唆していることです。 カスタム書式は、[datetime]::ParseExact()を利用することが適した場面はたくさん見かけるので活用できるといいかと思います。 もちろん、元々の書式がja-JPのCultureInfoで読めるパターンならCultureInfoの指定は不要です。
$information = '1211(2012)18h30m17s'
$pattern = 'ddMMM\(yyyy\)HH\hmm\mss\s'
[datetime]::ParseExact($information, $pattern, $null)
読み取れますね。
2012年11月12日 18:30:17
事前に、CurrentCultureをその国のCultureInfoに変更すると出力も変わります。
$currentThread = [System.Threading.Thread]::CurrentThread
$culture = [System.Globalization.CultureInfo]::CreateSpecificCulture("en-US")
$currentThread.CurrentCulture = $culture
$currentThread.CurrentUICulture = $culture

$information = '12Nov(2012)18h30m17s'
$pattern = 'ddMMM\(yyyy\)HH\hmm\mss\s'
[datetime]::ParseExact($information, $pattern, $null)
en-USの書式に変わっていますね。
Monday, November 12, 2012 6:30:17 PM

まとめ

これで、各国の日付書式にも対応させる方法が分かりました。 CurrentCultureを変える場面は少なく、.ToString()で書式を変える方が多いかとは思います。 が、読み取り時に柔軟に対応できることを覚えておくと、海外製品のログ分析などで有用になります。(経験談) 活用していただけると嬉しい限りです。

参考サイト

Random Holiday destinations in Powershell PowerShell Scripts Blog - Show-Formatting1.ps1 How to set culture in PowerShell?