背景
YouTube の API や動画の構造化データで、PT4M13S のような文字列をよく見かけます。
私はこれを読むたびに頭の中で分解していて、正直ちょっと面倒でした…
なので、期間の文字列を秒数や「何時間何分」に直すツールを置いています。
https://hashitosystem.com/tools/duration/
この表記、P1M と PT1M を取り違えると結果が4万倍以上ずれます。
ツールの実装を読み直しながら確かめたことのメモです。
期間表記の読み方
ISO 8601 は日付や時刻の書き方を決めた国際規格だ。
その中に「期間(duration)」の書き方があり、P で始めて、数字と単位の文字を並べる。
単位は Y(年)、M(月)、W(週)、D(日)で、時刻の部分は T で区切ってから H(時)、M(分)、S(秒)を書く。
M が2回出てくるのがポイントです。
T より前の M は月、T より後ろの M は分である。
P1M は1か月、PT1M は1分。
PT1H30M は1時間30分で、P1DT12H は1日と12時間になります。
ツールの正規表現
ツールでは、下記の正規表現で各単位の数字を取り出しています。
入力は大文字にそろえた文字列で、m[2] から m[8] に年・月・週・日・時・分・秒が入ります。
const RE = /^([+-])?P(?!$)(?:(\d+(?:[.,]\d+)?)Y)?(?:(\d+(?:[.,]\d+)?)M)?(?:(\d+(?:[.,]\d+)?)W)?(?:(\d+(?:[.,]\d+)?)D)?(?:T(?!$)(?:(\d+(?:[.,]\d+)?)H)?(?:(\d+(?:[.,]\d+)?)M)?(?:(\d+(?:[.,]\d+)?)S)?)?$/;
T の前と後ろで M を別のグループにしているので、位置だけで月か分かが決まります。
P(?!$) と T(?!$) は、P や PT だけで中身が無い文字列をはじくための先読みです。
小数の区切りに , も許しているのは、ISO 8601 がカンマも小数点として認めているからです。
年と月は秒数にできない
ここが本題です。
1日は 86,400 秒、1時間は 3,600 秒と決まっていますが、1か月の長さは月によって違います。
下記は、各月の1日から「1か月後」までの日数を数えたコードです。
const days = (y, m) => (Date.UTC(y, m, 1) - Date.UTC(y, m - 1, 1)) / 86400000;
for (const [y, m] of [[2026, 1], [2026, 2], [2026, 4], [2028, 2]]) {
console.log(`${y}-${m}-01 + 1か月 =`, days(y, m), '日');
}
結果は下記です。
2026-1-01 + 1か月 = 31 日
2026-2-01 + 1か月 = 28 日
2026-4-01 + 1か月 = 30 日
2028-2-01 + 1か月 = 29 日
同じ P1M でも、28日から31日まで4通りの長さになります。
つまり、どの日から数えるかという起点が無いと、P1M は秒数に確定できない。
年も、うるう年があるので同じことが言えます。
そこでツールは、年を365日・月を30日として概算し、その場合は結果に「概算です」と添えるようにしました。
下記は、ツールのパース関数を同じ正規表現でいくつかの入力に通した結果です。
exact は年と月が入っていないとき true になる値で、秒数を確定値として出してよいかの印です。
PT1M 60 exact=true
P1M 2592000 exact=false
P1Y 31536000 exact=false
PT1H30M 5400 exact=true
P1DT12H 129600 exact=true
PT36H 129600 exact=true
P NG(format)
PT NG(format)
P1H NG(format)
PT0,5H 1800 exact=true
PT1M は 60 秒、P1M は 2,592,000 秒で、ちょうど 43,200 倍です。
1文字の位置違いでこれだけ変わるのは、ちょっと怖いですね…
P1H は T を書き忘れた形で、H は時刻側にしか書けないのでエラーにしています。
なぜ「概算」を結果と一緒に返すのか
月を30日にする計算自体は、よくある近似です。
ただ、それを黙って秒数だけ返すと、受け取った側は確定値だと思って使ってしまう。
exact を一緒に返しておけば、呼び出し側で「月を含むなら日付ライブラリで起点から足す」と分岐できます。
ちなみに日(D)も、夏時間のある地域では23時間や25時間の日があるので厳密には揺れます。
ツールは日本で使う前提で、1日を 86,400 秒に固定しています。
期間の文字列を秒に直すコードを書くときは、M がどちら側にあるかと、年・月を含むかどうかの2点だけは必ず見てください。
本記事はAI補助で執筆した、個人開発の紹介記事です。