LoginSignup
3
0

Ruby 3.2以上 (というか psych 4.0.5 以上) と timecop 0.9.5以下を組み合わせると、YAML の Date がパースされなくなる

Last updated at Posted at 2024-03-14

現象

Ruby 3.2 (というか psych 4.0.5以上)と timecop 0.9.5以下を組み合わせると、YAML の Date がパースされなくなる。

Gemfile
gem 'psych', '4.0.5'
gem 'timecop', '0.9.5'
main.rb
require 'yaml'
require 'timecop'

puts YAML.unsafe_load('2024-03-14').class
# => String

原因

psych の Psych::ScalarScanner#tokenize で Date のパースをしているので、モンキーパッチしてエラーを見てみる。v4.0.5/lib/psych/scalar_scanner.rb#L63-L69 を一部改変したものが以下。

module Psych
  # 省略...
  class ScalarScanner
    # 省略...
    def def tokenize string
      # 省略...
      elsif string.match?(/^\d{4}-(?:1[012]|0\d|\d)-(?:[12]\d|3[01]|0\d|\d)$/)
        require 'date'
        begin
          class_loader.date.strptime(string, '%F', Date::GREGORIAN)
        rescue ArgumentError => e
          # エラーの出力
          pp e
          string
        end

これで動かすと、以下のようなエラーが得られる。 Timecop が Date.strptime の第3引数で怒っていることがわかる

#<ArgumentError: Timecop's Date::strptime_with_mock_date only supports Date::ITALY for the start argument.>

Ruby 3.1 には psych 4.0.3が同梱されており、 Ruby 3.2 には psych 5.0.1が同梱されているが、psych 4.0.5 で、Date 型とみなした文字列のパース(Date.strptime)の基準日が Date::ITARLY から DATE::GREGORIAN に変更される修正が行われた。

参考

timecop は Date.strptime をモックしているが、0.9.5 以下だと Date::ITALY 以外を例外にしている。

def strptime_with_mock_date(str = '-4712-01-01', fmt = '%F', start = Date::ITALY)
  unless start == Date::ITALY
    raise ArgumentError, "Timecop's #{self}::#{__method__} only " +
      "supports Date::ITALY for the start argument."
  end

v0.9.5/lib/timecop/time_extensions.rb#L48-L51 より

解決方法

timecop を 0.9.6 以上にすればいい。以下の timecop の PRで修正されている。
Allow other "dates of calendar reform" in Date#strptime #389

Gemfile
gem 'psych', '4.0.5'
gem 'timecop', '0.9.6'
require 'yaml'
require 'timecop'

puts YAML.unsafe_load('2024-03-14').class
# => Date
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0