Ruby Time # to_json как поплавок - PullRequest
0 голосов
/ 13 ноября 2018

Библиотека json Ruby по умолчанию конвертирует объекты Time в строки

require 'json'
Time.at(1000).utc.to_json # => "\"1970-01-01 00:16:40 UTC\"" 

Проблема в том, что мы теряем точность.Вместо этого я хотел бы, чтобы to_json производил float.

Я также знаю, что есть некоторые обходные пути, использующие oj или требующие json/add/time, но оба они добавляют лишние данные к выводу и не являются наиболееportable.

Простым подходом является создание обезьяньего патча Time, хотя я не люблю этого делать, особенно для базовых классов

class Time
  def to_json(*a)
    self.to_f.to_json(*a)
  end
end

Есть ли лучшие подходы?

Ответы [ 2 ]

0 голосов
/ 14 ноября 2018

Вы можете сохранить количество наносекунд с начала эпохи.

require 'time'
require 'json'

t = Time.now
  #=> 2018-11-13 13:23:32 -0800
json = [t.to_i, t.nsec].to_json
  #=> "[1542144212,883611300]"

secs, nsecs = JSON.parse(json)
  #=> [1542144212, 883611300]
r = secs + 10**-9 * nsecs
  #=> (15421442128836113/10000000)
tt = Time.at r
  #=> 2018-11-13 13:23:32 -0800

t == tt
  #=> true

Примечание (10**-9).class #=> Rational.

0 голосов
/ 13 ноября 2018

Простой подход состоит в том, чтобы обезьянить патч Time, хотя мне это не нравится, особенно для базовых классов

Не существует формата JSON для датНасколько JSON заботится, они просто строки.Большинство языков понимают ISO 8601 , и это то, что Time#to_json производит.Пока Time#to_json продолжает производить дату и время ISO 8601, вы будете поддерживать обратную совместимость.

require 'json'
require 'time'  # for Time#iso8601 and Time.parse

class Time
  def to_json
    return self.iso8601(6).to_json
  end
end

time = Time.at(1000.123456)
puts "Before: #{time.iso8601(6)}"

json_time = Time.at(1000.123456).to_json
puts "As JSON: #{json_time}"

# Demonstrate round tripping.
puts "Round trip: #{Time.parse(JSON.parse(json_time)).iso8601(6)}"
Before: 1969-12-31T16:16:40.123456-08:00
As JSON: "1969-12-31T16:16:40.123456-08:00"
Round trip: 1969-12-31T16:16:40.123456-08:00

Если вам не нравится глобальное патчирование обезьян, вы можете использовать патч обезьянв изоляции путем реализации around.

class Time
  require 'time'
  require 'json'

  def precise_to_json(*args)
    return iso8601(6).to_json(*args)
  end

  alias_method :original_to_json, :to_json
end

module PreciseJson
  def self.around
    # Swap in our precise_to_json as Time#to_json
    Time.class_eval {
      alias_method :to_json, :precise_to_json
    }

    # This block will use Time#precise_to_json as Time#to_json
    yield

  # Always put the original Time#to_json back.
  ensure
    Time.class_eval {
      alias_method :to_json, :original_to_json
    }
  end
end

obj = { 
  time: Time.at(1000.123456),
  string: "Basset Hounds Got Long Ears"
}

puts "Before: #{obj.to_json}"

PreciseJson.around {
  puts "Around: #{obj.to_json}"
}

puts "After: #{obj.to_json}"

begin
  PreciseJson.around {
    raise Exception
  }
rescue Exception
end

puts "After exception: #{obj.to_json}"
Before: {"time":"1969-12-31 16:16:40 -0800","string":"Basset Hounds Got Long Ears"}
Around: {"time":"1969-12-31T16:16:40.123456-08:00","string":"Basset Hounds Got Long Ears"}
After: {"time":"1969-12-31 16:16:40 -0800","string":"Basset Hounds Got Long Ears"}
After exception: {"time":"1969-12-31 16:16:40 -0800","string":"Basset Hounds Got Long Ears"}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...