Nushellの日付・時刻計算の基本について

Nushellの日付・時刻計算の基本について

Clock Icon2024.07.07

しばたです。

先月ごろから普段使いのシェルをPowerShellからNushellに切り替えて実用に耐えるか検証中です。

https://www.nushell.sh/

現時点では「完全切替は難しい。」と感じつつもNushell 8 : PowerShell 2くらいの比率にはできているので存外いい感じです。

そんな中で両者ともに特徴のある日付や時刻の扱いについて比較しつつ語りたいと思います。

はじめに

比較用 : PowerShellでの日付・時刻計算

まずは比較用にPowerShellでの日付・時刻の取り扱いについて説明します。

PowerShellは.NETのオブジェクトを扱うシェルであり、日付や時刻は.NETのSystem.DateTime型(構造体)System.TimeSpam型(構造体)をそのまま採用しています。

PowerShell
# 現在時刻を得るコマンドレット
PS C:\> Get-Date

2024年7月7日 10:55:55

# 得られる値は System.DateTime 型のオブジェクト
PS C:\> Get-Date | Get-Member

   TypeName: System.DateTime

Name                 MemberType     Definition
----                 ----------     ----------
Add                  Method         datetime Add(timespan value)
AddDays              Method         datetime AddDays(double value)
AddHours             Method         datetime AddHours(double value)
AddMilliseconds      Method         datetime AddMilliseconds(double value)
AddMinutes           Method         datetime AddMinutes(double value)
# ・・・後略・・・

# System.DateTime型 を直接扱うことも可能
PS C:\> [Datetime]::Now

2024年7月7日 10:57:49

このため日付計算のためのプロパティやメソッドを直接利用することができます。
これはPowerShellの非常に良い点であり、PowerShellでは日付・時刻の計算がめちゃくちゃ楽にできます。

PowerShell
# 現在時刻を変数に保存
PS C:\> $v1 = Get-Date
PS C:\> $v1

2024年7月7日 11:31:10

# .NETのメソッドで日付・時刻計算が非常に楽にできる
PS C:\> $v2 = $v1.AddDays(10) # 10日後を取得
PS C:\> $v2

2024年7月17日 11:31:10

# 時刻同士の演算も可能
PS C:\> $v2 - $v1 | Select-Object TotalHours

TotalHours
----------
       240

ちょっとした応用例として「当月の月末日」を求めようとすると以下の様に書けます。

PowerShell (当月の月末日を求める応用例)
# 「翌月1日の前日」を計算することで月末日を算出
# .NETのメソッドやプロパティを使うことで簡単に計算できる
Get-Date | % { (Get-Date -Year $_.Year -Month $_.Month -Day 1).AddMonths(1).AddDays(-1).Date }

結果はこんな感じ。

PowerShell
# 当月(2024年7月)の月末日を取得
PS C:\> Get-Date | % { (Get-Date -Year $_.Year -Month $_.Month -Day 1).AddMonths(1).AddDays(-1).Date }

2024年7月31日 0:00:00

# うるう年も問題無し : 2024年2月の月末日
PS C:\> [datetime]::Parse('2024-02-02') | % { (Get-Date -Year $_.Year -Month $_.Month -Day 1).AddMonths(1).AddDays(-1).Date }

2024年2月29日 0:00:00

Nushellでの日付・時刻の扱い

対してNushellでは日付・時刻を独自のdate型で扱います。

内部的にはchronoのDateTime型(chrono::DateTime)の様でタイムゾーン付きの時刻である点が特徴的です。

Nushell
# 現在時刻を得るコマンド
~> let v1 = date now
~> $v1
Sun, 7 Jul 2024 11:50:17 +0900 (now)

# 得られる値は独自のdate型
~> date now | describe
date

# タイムゾーン付きのため変換が可能 : JST → UTC
~> $v1 | date to-timezone '+0000' 
Sun, 7 Jul 2024 02:50:17 +0000 (3 minutes ago)

このdate型には時刻計算のためのプロパティやメソッドはありませんが、代わりに時間長を表現するdurationが存在します。

durationは単位付きの数字で記述し、現時点では以下の8単位存在します。

単位 時間
ns ナノ秒
us マイクロ秒
ms ミリ秒
sec
min
hr 時間
day
wk

残念ながら「月」と「年」は存在せず、GitHubを見ても取り扱いに相当難儀している様なので今後も増えることは無い予感がします。

date型に対してこのdurationを使い演算が可能となっています。

Nushell
# Nushellでは日付・時刻の演算に Duration が使える
~> let v2 = $v1 + 10day # 10日後を取得
~> $v2
Wed, 17 Jul 2024 11:50:17 +0900 (in a week)

# 時刻同士の演算も可能 : Durationを返す
~> $v2 - $v1
1wk 3day

# 表示形式を変えたい場合はformatコマンドを使う : これはstringを返す
~> $v2 - $v1 | format duration day
10 day

そしてdate型に対するプロパティアクセスに相当するものとして型変換のコマンドが存在します。
date to-recorddate to-tableコマンドでそれぞれの型に変換し要素の値を取得可能です。

Nushell
~> $v1
Sun, 7 Jul 2024 11:50:17 +0900

# date to-record コマンドでrecord型に
~> $v1 | date to-record 
╭────────────┬──────────╮
│ year       │ 2024     │
│ month      │ 7        │
│ day        │ 7        │
│ hour       │ 11       │
│ minute     │ 50       │
│ second     │ 17       │
│ nanosecond │ 50552100 │
│ timezone   │ +09:00   │
╰────────────┴──────────╯

# date to-table コマンドでtable型に
~> $v1 | date to-table
╭───┬──────┬───────┬─────┬──────┬────────┬────────┬────────────┬──────────╮
│ # │ year │ month │ day │ hour │ minute │ second │ nanosecond │ timezone │
├───┼──────┼───────┼─────┼──────┼────────┼────────┼────────────┼──────────┤
│ 0 │ 2024 │     7 │   7 │   11 │     50 │     17 │   50552100 │ +09:00   │
╰───┴──────┴───────┴─────┴──────┴────────┴────────┴────────────┴──────────╯

# プロパティアクセスを代替できる
~> $v1 | date to-record | get year
2024

前述の応用例「当月の月末日」を求めようとするとこんな感じで書けます。

Nushell (当月の月末日を求める応用例)
# 「翌月1日の前日」を計算することで月末日を算出しているが、durationに月がないため若干強引に計算している
date now | date to-record | if $in.month == 12 {$"($in.year + 1)-1-1"} else {$"($in.year)-($in.month + 1)-1"} | into datetime | $in - 1day

結果はこんな感じ。

Nushell
# 当月(2024年7月)の月末日を取得
~> date now | date to-record | if $in.month == 12 {$"($in.year + 1)-1-1"} else {$"($in.year)-($in.month + 1)-1"} | into datetime | $in - 1day
Wed, 31 Jul 2024 00:00:00 +0900 (in 3 weeks)

# うるう年も問題無し : 2024年2月の月末日
~> '2024-02-02' | into datetime | date to-record | if $in.month == 12 {$"($in.year + 1)-1-1"} else {$"($in.year)-($in.month + 1)-1"} | into datetime | $in - 1day
Thu, 29 Feb 2024 00:00:00 +0900 (4 months ago)

PowerShellよりは頑張る必要がありますが、Nushellもdurationのおかげで比較的直観的に日付や時刻を取り扱えます。

補足 : その他もろもろ

最後に補足としてNushellの日付を扱ううえで役に立ちそうな情報を記載しておきます。

1. into datetime

文字列をdate型に変換する際はinto datetimeコマンドを使います。

Nushell
# 実行例
~> '2024-02-02' | into datetime
Fri, 2 Feb 2024 00:00:00 +0900 (5 months ago)

2. into duration

文字列をduration型に変換する際はinto durationコマンドを使います。

Nushell
~> '10day' | into duration
1wk 3day

# 数字と単位の間にスペースがあるとエラーなので注意
~> '10 day' | into duration
Error: nu::shell::cant_convert_with_value

  × Can't convert string `10` to duration.
   ╭─[entry #64:1:1]
 1 │ '10 day' | into duration
   · ─┬┬
   ·  │╰── this string value...
   ·  ╰── can't be converted to duration
   ╰────
  help: supported units are ns, us/µs, ms, sec, min, hr, day, and wk

3. date list-timezone

タイムゾーンの一覧はdate list-timezoneコマンドで取得できます。

Nushell
~> date list-timezone | where timezone =~ Asia/T
╭───┬───────────────╮
│ # │   timezone    │
├───┼───────────────┤
│ 0 │ Asia/Taipei   │
│ 1 │ Asia/Tashkent │
│ 2 │ Asia/Tbilisi  │
│ 3 │ Asia/Tehran   │
│ 4 │ Asia/Tel_Aviv │
│ 5 │ Asia/Thimbu   │
│ 6 │ Asia/Thimphu  │
│ 7 │ Asia/Tokyo    │
│ 8 │ Asia/Tomsk    │
╰───┴───────────────╯

4. cal

calコマンドでカレンダーを表示できます。

Nushell
# 当月(2024年7月)のカレンダーを表示
~> cal
╭────┬────┬────┬────┬────┬────┬────╮
│ su │ mo │ tu │ we │ th │ fr │ sa │
├────┼────┼────┼────┼────┼────┼────┤
│    │  1 │  2 │  3 │  4 │  5 │  6 │
│ 7  │  8 │  9 │ 10 │ 11 │ 12 │ 13 │
│ 14 │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │
│ 21 │ 22 │ 23 │ 24 │ 25 │ 26 │ 27 │
│ 28 │ 29 │ 30 │ 31 │    │    │    │
╰────┴────┴────┴────┴────┴────┴────╯

出力をTableにする-tオプションがあり、たとえば当月の月末日の日付だけでよければ以下の様にして取得することもできたりします。

Nushell
# 当月の月末日(日付だけ)を取得 : 最終行の最大値から月末日を取得
~> cal -t | last | transpose prop value | filter {|v| $v.value != null } | get value | math max
31

最後に

簡単ですが以上となります。

Nushellの情報自体が少ないので記事にしてみました。
本記事の内容が誰かの役に立てば幸いです。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.