🔖

[Bug #21199] 特定の日付を yaml で dump -> load すると日付がおかしくなるバグ報告

に公開

[Bug #21199] Psych.dump and load on Date before 1582-10-15

  • 次のように DatePsychdump -> load したときに 1582-10-15 よりも過去の日付の場合に正しく dump / load されないというバグ報告
    • Psychyaml ファイルの読み書きで使用されているライブラリ
    • なのでここに書いてる話は yaml ファイルを利用している場合に関係がある
require 'date'
require 'psych'

def check(date)
  puts "date=#{date.inspect}"
  str = Psych.dump(date, permitted_classes: [Date])
  puts "str=#{str}"
  date2 = Psych.load(str, permitted_classes: [Date])
  puts "date2=#{date2.inspect}"
end

puts "OK"
check Date.new(2025, 9, 28)
puts
check Date.new(1582, 10, 15)
# 上の2つは意図する結果になっている
# 下は `psych.load` 後に日付がずれている
puts
puts "NG"
check Date.new(1582, 10, 4)
__END__
OK
date=#<Date: 2025-09-28 ((2460947j,0s,0n),+0s,2299161j)>
str=--- 2025-09-28
date2=#<Date: 2025-09-28 ((2460947j,0s,0n),+0s,-Infj)>

date=#<Date: 1582-10-15 ((2299161j,0s,0n),+0s,2299161j)>
str=--- 1582-10-15
date2=#<Date: 1582-10-15 ((2299161j,0s,0n),+0s,-Infj)>

NG
date=#<Date: 1582-10-04 ((2299160j,0s,0n),+0s,2299161j)>
str=--- 1582-10-14
date2=#<Date: 1582-10-14 ((2299160j,0s,0n),+0s,-Infj)>
  • 1582-10-04dump すると 1582-10-14 になってしまうのが問題みたい?
require "date"
require "psych"

# date は 1582-10-04 だが Psych.dump すると 1582-10-14 になる
date = Date.new(1582, 10, 4)
yaml = Psych.dump(date, permitted_classes: [Date])
puts yaml
# => --- 1582-10-14
require "date"
require "psych"

# Date のデフォルトではユリウス日が Date::ITALY (1582年10月15日) となっている
date = Date.new(1582, 10, 4)
pp date.start == Date::ITALY     # => true
pp date.start == Date::GREGORIAN # => false

# これが Psych では常にグレゴリオ暦になるように変換される
# ここで日付がずれてしまっている
date2 = date.new_start(Date::GREGORIAN)
pp date2.start == Date::ITALY     # => false
pp date2.start == Date::GREGORIAN # => true
pp date2
# => #<Date: 1582-10-14 ((2299160j,0s,0n),+0s,-Infj)>
  • また同様に Date をデシリアライズすると Date::GREGORIAN 基準で変換される
require 'date'
require 'psych'

yaml = "--- 1582-10-14"
date = Psych.load(yaml, permitted_classes: [Date])
pp date.start == Date::ITALY     # => false
pp date.start == Date::GREGORIAN # => true
pp date
# => #<Date: 1582-10-14 ((2299160j,0s,0n),+0s,-Infj)>
  • ややこしい…そもそも YAML 自体にこのあたりの情報を扱う仕組みがないのが問題みたいなんですかねー
  • で、これなんですが実は psych 5.2.3 までは上記のような挙動だったんですが psych 5.2.4 から自身のユリウス日の情報を準じるようになったみたいですね?
require 'date'
require 'psych'

# Psych.dump の結果が 5.2.3 と 5.2.4 で変わる
date = Date.new(1582, 10, 4)
yaml = Psych.dump(date, permitted_classes: [Date])
puts yaml
# psych 5.2.3 => --- 1582-10-14
# psych 5.2.4 => --- 1582-10-04
  • これにより Psych.dump の結果が変わる可能性があるので注意する必要がある
    • とはいえピンポイントなのでまあ〜
  • ちなみに Psych.dump の挙動はそのまま
# Psych.load の結果はそのまま
date = Psych.load("--- 1582-10-04", permitted_classes: [Date])
pp date
# psych 5.2.3 => #<Date: 1582-10-04 ((2299150j,0s,0n),+0s,-Infj)>
# psych 5.2.4 => #<Date: 1582-10-04 ((2299150j,0s,0n),+0s,-Infj)>

# Psych.load の結果はそのまま
date = Psych.load("--- 1582-10-14", permitted_classes: [Date])
pp date
# psych 5.2.3 => #<Date: 1582-10-14 ((2299160j,0s,0n),+0s,-Infj)>
# psych 5.2.4 => #<Date: 1582-10-14 ((2299160j,0s,0n),+0s,-Infj)>
  • 暦周り難しい…
GitHubで編集を提案

Discussion