Windowsのバッチで %date% を加工して使うのはやめよう！

# サマリ
* Windowsでdateやtimeコマンド/環境変数はOS設定依存
    * 「地域」の設定
    * レジストリ`HKEY_CURRENT_USER\Control Panel\International`に格納
* powershellで`Get-Date -Format "任意表示書式"`が利用できるならおすすめしたい

:::note info
(2025/6/3追記)地域設定に影響されない書き方について言及されていたので共有する

[バッチファイルでの日付取得（地域設定に依存しない）](https://qiita.com/minorufujimoto/items/78f9da9fbff69d304cf4)

ただし、こちらで使っているwmicコマンドも、powershellに代替しなさいとしている記事ある。（一次ソースでない）

[【解説】Windowsのwmicコマンドの使い方](https://blackmagicdesign-creatorscom.jp/cmd-wmic/#index_id13)

:::

# バッチで日付を使ったあれこれをしたい
ってことありませんか？ありますよね（圧）

ファイル名につけて歴管理したいだとか、ログ出力に使いたいだとか。

で、標準コマンドとかシステム環境変数の `date`を持ち出して、

```bat
rem 分解して取得
set /a YYYY=%DATE:~0,4%
set /a MM=%DATE:~5,2%
set /a DD=%DATE:~8,2%

rem 区切りの/を消す
set /a ymd=%date:/=%
```

みたいなこと、すると思うんです。

# 前提条件を無視してはいけない

上記コマンドが成立する前提とは、

> `%date%`の書式が`yyyy/mm/dd`であること

である。

これを満たさなくなった瞬間、ファイル名に`-`とか`WED`とか`Jan`とか出てきて不思議ちゃんするのである。
OS初期設定で日本語にしてりゃほぼ問題なく動作するが、
初期設定を別言語でやっただとか、
「日付と時刻の書式」をあとで上書きしただとかすると、
前提が壊れて誤動作するのである。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226001/ca9eef96-0479-b9f2-23bc-0690078386ae.png)


「日付をYYYYMMDDで取得するには」のようなネット記事は大体先の前提を無視して記載されている。
条件付きならば条件付きと注記するべきだが、これを記載している記事はそう見ない。

> 当記事のdateコマンドで得られる結果はYYYY/MM/DD形式が前提で説明しております。Windowsの環境によりYYYY-MM-DD などの場合は当記事の内容は使えません。

https://ossan-tech.work/cmd-prompt-date/

※他にもあったらご容赦。

※追記
自己防衛のために補足するが、上記はWindows 10 環境で筆者が検証している内容である。
Win11とかWin95とかでどうかはわからんので各自で検証なり検索なりしてくりゃれ。

# じゃあどうしろと
条件付きである点を理解して、条件を検証するか、条件外は保証しないと宣言するか、別の方法で対応する。

## 保証しないと宣言する。
提供物の場合、客に「日付の書式変えてたり海外の人が使うとバグるんです」って説明して納得してもらえる場合に限る。
内輪で使うツールとかなら、環境設定を変更した理由に折れてもらう必要がある。

合わせて、保証外の場合は警告出すとかしたほうがよかろう

## 条件を保証する。(検証して問題ないことを確認する)
具体的なHow2はここでは書かないけど、`%date%`の書式チェックを噛ませばいいんじゃないかな。
10ケタであること、数字であって欲しいところが数字であること、/を含むかどうか...etc

後述するレジストリ値を使うのが確実なのだけど、書式部分だけ切り出すのがちと面倒。
(`reg query "HKEY_CURRENT_USER\Control Panel\International" /v "sShortDate"`)

## 別の方法で対応する。
### powershellコマンドを利用する
powershellの`Get-Date -Format <string>`で書式指定してとってくるのがそこそこ確実。

```powershell:具体例
Get-Date -Format "yyyy/MM/dd HH:mm:ss"
Get-Date -Format "yyyyMMddTHHmmss"
Get-Date -Format "yyyy-MM-ddTHH:mm:ssK"
```


```bat:バッチスクリプトから利用する場合
for /f %%a in ('powershell Get-Date -Format "yyyyMMddTHHmmss"') DO set yyyyMMddTHHmmss=%%a
echo %yyyyMMddTHHmmss%
```

https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.utility/get-date?view=powershell-7.3

```powershell:利用例
Copy "FileName.ext" "!bk\FileName_"+(Get-Date -Format "yyyyMMdd_HHmmss")+".ext"
```

### レジストリをいじる
同時に他のシステムがコマンドから日付を参照するようなことがなければ有効

#### レジストリ値の参照・書き換え
日付の書式は`HKEY_CURRENT_USER\Control Panel\International`に登録される。
これをいじいじすれば、一応書き換えも可能。

ユーザー設定なので、管理者権限も(基本的には)要らない。

```bat:読み込み
rem 日付(dateコマンドの書式)
reg query "HKEY_CURRENT_USER\Control Panel\International" /v "sShortDate"

rem 日付(timeコマンドの書式)
reg query "HKEY_CURRENT_USER\Control Panel\International" /v "sShortTime"
```

```:出力
HKEY_CURRENT_USER\Control Panel\International
    sShortDate    REG_SZ    yyyy/MM/dd

HKEY_CURRENT_USER\Control Panel\International
    sShortTime    REG_SZ    H:mm
```

```winbatch:書き換え(一般的な設定にもどす)
reg add "HKEY_CURRENT_USER\Control Panel\International" /v "sShortDate" /d  "yyyy/MM/dd" /f
rem ※ reg add /? でコマンドの詳細が見れる
```

即時反映される。

上記で上書きしてネットに転がっている書式編集でイジイジしてもいいけど、
どうせ書式を弄くるんだから、都合のいい書式にして使えばええがな。
書式仕様は、冒頭画像の「データ形式の表記」とか見ればだいたい分かるのでそれみてちょんまげ。
時刻の書式仕様も書いてあったよ。

言うまでもないことだが、レジストリをイジるからには、確実に復元されるよう注意を払いたい。
(今の設定をどっかにコピー→作業用の値に書き換え→復元)

ネット記事で下のような方法で値だけ取る方法が紹介されていた。
いたが、これ、値にスペース入ってたら困っちゃうねぇ。
(Forコマンドがスペースをセパレータとして処理することを利用して3個目の値を取り出している)
まあ多分、`reg load`とか`reg save`とかを使用してあーだこーだするのが正しい気がするけど、ここでは割愛する(めんどい)

```winbatch
for /f "tokens=3" %A in ('reg query "HKEY_CURRENT_USER\Control Panel\International" /v "sShortDate"') do set REG_RESULT=%A

echo %REG_RESULT%
```

```:出力
yyyy/MM/dd
```

まあ設定上書きするって時点でだいぶアレなので、僕は大人しくpowershell使ったほうがいいと思いマシュマロ

# 補足
> 2022/10/27追記

ちなみに、VBScriptの`NOW`の出力なんかもこの記事で紹介したような挙動を確認している
まあこれは`DATE`型の値を文字列に静的変換する際のデフォルト書式がOSの環境設定依存ということだろう。

```vb
Year(now)&"/"&Month(now)&"/"&Day(now)&" "&Hour(now)&":"&Minute(now)&":"&Second(now)
```

のようにちまちま対応可能

# 参考記事

https://flying-foozy.hatenablog.com/entry/20140205/1391620373

https://k-sugi.sakura.ne.jp/it_synthesis/windows/4877/

https://learn.microsoft.com/ja-jp/windows-server/administration/windows-commands/reg

https://correct-log.com/bat_get_registry_value/#toc3

# 余談
冒頭の画像の「カレンダー」の欄に「２桁の数字で年を入力すると、次の範囲内での暦年として解釈する」とかいうオプションがあるんですけど、これ[2038年](https://ja.wikipedia.org/wiki/2038%E5%B9%B4%E5%95%8F%E9%A1%8C)の次の地雷になりそうじゃねって思った。
まあ、先に2038年が16年後にくるし、その次の12年後にくるかもしれない程度の問題なのだけどね。

## 一度もAskされていないFAQ

* Q.そんなところ変える人いるわけないでしょ
    * A.せやろな。俺は存在しないとされる人だな。
    * ※OutLookの日付の設定が同じところ見るらしくてそこを弄れって記事が出回っている。つまり、いる。
* Q.レジストリ弄ってたら壊れた
    * A.自己責任でどうぞ。あ、壊れちゃったあとか、そっか...そっかぁ...
    * ※日付書式が壊れたって話なら、コンパネからの機能に「リセット」ボタンがあるから開ければデフォ値にはできるんじゃないですかね
    * ※正直レジストリ弄るのもどうなんだろうって思うのでpowershell利用を推奨したい。
* Q.powershellって難しそうでよくわかんない🥺
    * A.だいじょうぶ！怖くないよー。オジさんと一緒に1Liner沼にハマろうねぇ
        * ※正直cmdとはかなり毛色が違う利便性があるので、cmdと同様にOS機能を使う手段としてある程度理解したほうが良い。出力結果に対する検索性とか、切り出しとかはpowershellのほうが断然強い、と思うし、繰り返し処理の記述は個人的にはpowershellのほうが易しい。処理構文とかの言語的なベースの感触はCMDよりJavaScriptとかVBSに近い。
    * ※1-Linerにする必要性はない。というか同じ結果でも処理順とかの制御によってパフォーマンスがモロに変わる場合もあるので、用途によっては複数案出して経過時間とかを比較したい。
    * ※と、言うように長文をカマす人を作るタイプの沼(ﾀﾉｼｲ...ﾀﾉｼｲ...)