PowerShellの覚書

Windowsでなにかちょっとしたことをしたいとき。

コンパイラを使うまでもなく。

なにか別パッケージをいれるまでもなく。

Windows付属のPowerShellで、データのモデリングを細かくできて。

表示や加工も問題ないことに気づきましたので。

覚書など。


PowerShellの覚書

祝日の一覧を返すスクリプトブロック

日付オブジェクトの生成

日付を扱いたいとき。

DateTimeにキャストしても、うまく取り込めない場合があります。

こちらの方法なら

 [datetime]::parse("2025年8月14日 午後1時20分20秒")

漢字や、「午後」が入っていても、たいてい取り込めるようです。


祝日の一覧をダウンロードして変数に格納

Shift-JisのCSVをダウンロードして、モデリングで使えるようにするには。

# 祝日をダウンロード
$tempfile = "$env:temp/syukujitsu.csv"
Invoke-WebRequest `
-Uri 'https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv' `
-OutFile $tempfile
# Shift-JISからUTF8に変換
$text = [System.IO.File]::ReadAllText($tempfile,[Text.Encoding]::GetEncoding(932))
# ファイルは削除
Remove-item -Path $tempfile
Remove-Variable -Name 'tempfile'

# 行ごとに分割してCSVとして読み込み すべてStringとして読み込み
# 型変換する場合は 次の節の方法を使用する
$csv = ($text -replace "`r`n","`n") | ConvertFrom-Csv
# フィルタしてみる
$csv | Where-object { $_.'国民の祝日・休日月日' -eq '2025/8/11' }

まず、Shift-JISからUTF-8の変換は、ファイルである必要があるため、ファイルとして保存後、エンコーディングを変換して$text変数に格納します。

次に、改行コードごとに分割ですが、CR(0xD)が邪魔のため、LF(0xA)のみに変換して、$csv変数に配列として格納します。

配列に入れてしまえば、あとは好きなようにモデリングしたり、集計やフィルタリングが可能です。

おそらく、PSCustomObjectの配列、というデータの持ち方が、最も汎用的。

Select-ObjectやGroup-Objectを使用したり、ForEach-Objectを使用したり、色々と応用が効くと思いますが。

すべてテキストで読み込んでいます。モデリングして型変換しましょう。


モデリングしながらPSCustomObjectの配列を作成

先程の$csvは、単にImport-Csvで読み込んだだけで、型の変換はしていません。

使いやすいように、型変換、モデリングするとこんな感じでしょうか。

#モデリングしながらCSVを配列に読み込み
$typedCsv = ($text -replace "`r`n","`n")  | ConvertFrom-Csv `
 | Select-Object `
    -Property @{Name = '国民の祝日・休日月日' ; Expression = { [datetime]::parse($_.'国民の祝日・休日月日') }},`
   @{Name = '国民の祝日・休日名称' ; Expression = { $_.'国民の祝日・休日名称' } }
# フィルタしてみる
$typedCsv | `
Where-object { ($_.'国民の祝日・休日月日').ToString('yyyyMMdd') -eq (Get-Date).AddDays(-3).ToString('yyyyMMdd') }

$csvは、すべてのメンバーをテキストとして読み込みましたが。

$typedCSVは、日付の部分をDateTime型に変換して読み込んだ形になります。
このような感じで、Get-Dateのような、DateTimeオブジェクトを比較が可能になりますが。

元々のデータは時刻が無いため、00:00:00として扱われます。

Get-Dateには時刻も入っているため、比較する場合は、トリムする必要がありますが。

まあ日付として扱ったほうが、利便性は高いと思います。


祝日の一覧を返すスクリプトブロック

というわけで、祝日のダウンロードから、モデリング、変数として格納するところを、スクリプトブロックにしてみましょう。

#祝日が入った配列を返します
$GetTypedSyukujitsu = {
    param ([String]$uri)
    # 祝日をダウンロード
    $tempfile = "$env:temp/syukujitsu.csv"
    Invoke-WebRequest `
    -Uri $uri `
    -OutFile $tempfile
    # Shift-JISからUTF8に変換
    $text = [System.IO.File]::ReadAllText($tempfile,[Text.Encoding]::GetEncoding(932))
    # ファイルは削除
    Remove-item -Path $tempfile
    Remove-Variable -Name 'tempfile'

    #モデリングしながらCSVを配列に読み込み
    return ($text -replace "`r`n","`n")  | ConvertFrom-Csv `
    | Select-Object `
        -Property @{Name = '国民の祝日・休日月日' ; Expression = { [datetime]::parse($_.'国民の祝日・休日月日') }},`
    @{Name = '国民の祝日・休日名称' ; Expression = { $_.'国民の祝日・休日名称' } }
}
# 祝日のダウンロードとモデリングはこの1行でおけ
$typedSyukujitsu = & $GetTypedSyukujitsu -uri 'https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv'

# フィルタしてみる
$typedSyukujitsu | `
Where-object { ($_.'国民の祝日・休日月日').ToString('yyyyMMdd') -eq (Get-Date).AddDays(-3).ToString('yyyyMMdd') }

スクリプトブロックにまとめると、こんな感じです。

このスクリプトブロックがあれば、いつでも最新の祝日一覧が取れる感じです。

スクリプトブロックを作る流れができれば。フィルタしているところも、日付が祝日かどうかを返す、スクリプトブロックを作れるわけですね。


日付から祝日名称を返すスクリプトブロック

というわけで、日付を入力すると、祝日の場合は名称を返すスクリプトブロックです。

#祝日が入った配列を返します
$GetTypedSyukujitsu = {
    param ([String]$uri)
    # 祝日をダウンロード
    $tempfile = "$env:temp/syukujitsu.csv"
    Invoke-WebRequest `
    -Uri $uri `
    -OutFile $tempfile
    # Shift-JISからUTF8に変換
    $text = [System.IO.File]::ReadAllText($tempfile,[Text.Encoding]::GetEncoding(932))
    # ファイルは削除
    Remove-item -Path $tempfile
    Remove-Variable -Name 'tempfile'

    #モデリングしながらCSVを配列に読み込み
    return  ($text -replace "`r`n","`n")  | ConvertFrom-Csv `
    | Select-Object `
        -Property @{Name = '国民の祝日・休日月日' ; Expression = { [datetime]::parse($_.'国民の祝日・休日月日') }},`
    @{Name = '国民の祝日・休日名称' ; Expression = { $_.'国民の祝日・休日名称' } }
}

$GetHolidayName = {
    param([datetime] $date)
    if ($script:holidays -eq $empty) {
        Write-Host '祝日CSをダウンロードします'
        # 祝日のダウンロード
        $script:holidays = & $GetTypedSyukujitsu -uri 'https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv'
    }
    return $script:holidays  | Where-object { ($_.'国民の祝日・休日月日').ToString('yyyyMMdd') -eq $date.ToString('yyyyMMdd') }
}    

& $GetHolidayName -date (Get-Date -Year 2025 -Month 8 -Day 11)

最初のスクリプトロックは、祝日の一覧のダウンロードとモデリング

2つ目のスクリプトブロックが、日付が祝日かどうか調べる内容です。

使用するのは2つ目ですが、毎回ダウンロードするのも無意味のため、$scriptスコープに祝日の一覧を入れて、値があれば、2回目からはダウンロードしない設計にしてみました。

クラスを作ればstaticを使えたり、GetClosureを使う方法もありそうですが。

まあスコープを工夫してでなんとか。

日付については、曜日は、Excelと同じでAAAで表示できますので、全く問題ありません。

Windowsが使えて、インターネットに接続していれば、祝日を調べる問題はほとんど解決しそうです。


祝日一覧のグリッド表示

ダウンロードした祝日のCSVをモデリング。

モデリングした祝日の一覧を、グリッド表示するとこのような感じになります。

#祝日が入った配列を返します
$GetTypedSyukujitsu = {
    param ([String]$uri = 'https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv')

    if ($script:holidays -ne $empty) {
         return $script:holidays
    }
    Write-Host '祝日CSをダウンロードします'
    # 祝日をダウンロード
    $tempfile = "$env:temp/syukujitsu.csv"
    Invoke-WebRequest `
    -Uri $uri `
    -OutFile $tempfile
    # Shift-JISからUTF8に変換
    $text = [System.IO.File]::ReadAllText($tempfile,[Text.Encoding]::GetEncoding(932))
    # ファイルは削除
    Remove-item -Path $tempfile
    Remove-Variable -Name 'tempfile'

    #モデリングしながらCSVを配列に読み込み
    $script:holidays =  ($text -replace "`r`n","`n")  | ConvertFrom-Csv `
    | Select-Object `
        -Property @{Name = '国民の祝日・休日月日' ; Expression = { [datetime]::parse($_.'国民の祝日・休日月日') }},`
    @{Name = '国民の祝日・休日名称' ; Expression = { $_.'国民の祝日・休日名称' } }
    return $script:holidays
}

& $GetTypedSyukujitsu | Out-GridView

一度ダウンロードしたCSVは、モデリング後に変数に入れておいて、次に使う場合は再利用という最終型のスクリプトブロックです。

これで、シェルを終了するまでは、無駄に何回もダウンロードせず、再利用が可能になります。

あれ?でもグリッドにて日付でソートすると、順番がおかしいような。

プロパティのMemberTypeは、確かにSystem.DateTimeになっていますから

型の変換は合っていると思います。グリッドの仕様なのかも?


インストール済みパッケージの一覧表示

PCにインストールされたパッケージの一覧を扱いたい場合。

Get-Package | Out-GridView

時差の計算

タイムゾーンの一覧表示

Get-TimeZone -ListAvailable | Out-GridView

時差の一覧表ですが。

海外の時刻を知りたいとき、こちらの「Id」を使用して計算します。

あれ?「PDT」米国太平洋標準時って存在しないのですね。代わりに「US Mountain Standard Time」が「PDT」になる感じでしょうか。


米国太平洋標準時が日本時間で何日何時か表示

[datetime]::ParseExact( `
('2025-08-26T11:00:00{0}' -f (Get-TimeZone -Id 'US Mountain Standard Time').BaseUtcOffset.ToString()), `
'yyyy-MM-ddTHH:mm:sszzz:00', $null)

この場合、PDT 2025年8月26日 11:00:00 が、日本時間で何日の何時かを表示しています。

タイムゾーン付きの日時の文字列を取り込むことで、取り込まれた値は日本時間に変換されます。

8月27日の3:00であることがわかります。

UTCが日本時間で何日何時か表示

[datetime]::ParseExact( `
('2025-08-26T18:00:00+{0}' -f (Get-TimeZone -Id 'UTC').BaseUtcOffset.ToString()), `
'yyyy-MM-ddTHH:mm:sszzz:00', $null)

同様に、UTCの2025年8月26日 18:00:00を取り込んでみますと。

 

同じように、8月27日の3:00であることがわかります。

うむ。火曜日の深夜ですね。

ゲームの無料配布の時刻ですが。翌日寝不足になりそうです。

UTCとPDTとの違いですが、UTCは{0}の部分が「00:00:00」になりますが、PDTは「-07:00:00」になります。

プラスはないけど、マイナスが付くため、プラスがない方に「+」を足しているわけですが。

これは見苦しいため、時差判定のスクリプトブロックを作って、汎用性をもたせたほうが格好良いと思います。(作ってナイ


PoweShellですが

ClassやFunctionを使って、ちまちま作っていたのですが。

セキュリティの関係で、Moduleがほぼ使えないため。

最近はScriptBlockを使用しています。

便利なScriptBlockは、追々追記しようと思いますが。

今はMacがメインなので、まあぼちぼち[amazonjs asin=”B0F2Y98MHW” locale=”JP” title=”【整備済み品】 Microsoft Surface Go2 / 10.5インチ タブレットPC/CPU:Pentium Gold 4425Y / メモリ:4GB / ストレージ:64GB / Win11 / MS Office 2021 / サーフィス サーフェス ゴー/Webカメラ(顔認証対応) / タイプカバーセット/PC King”]

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です