diff --git a/lib/subroutine.rb b/lib/subroutine.rb index 85b56c4..828fd72 100644 --- a/lib/subroutine.rb +++ b/lib/subroutine.rb @@ -3,3 +3,17 @@ require "subroutine/version" require "subroutine/fields" require "subroutine/op" + +module Subroutine + + def self.preserve_time_precision=(bool) + @preserve_time_precision = !!bool + end + + def self.preserve_time_precision? + return !!@preserve_time_precision if defined?(@preserve_time_precision) + + false + end + +end diff --git a/lib/subroutine/type_caster.rb b/lib/subroutine/type_caster.rb index 50a2130..8a23ed8 100644 --- a/lib/subroutine/type_caster.rb +++ b/lib/subroutine/type_caster.rb @@ -4,6 +4,8 @@ require 'time' require 'bigdecimal' require 'securerandom' +require 'active_support' +require 'active_support/json' require 'active_support/core_ext/date_time/acts_like' require 'active_support/core_ext/date_time/calculations' require 'active_support/core_ext/object/acts_like' @@ -111,7 +113,7 @@ def self.cast(value, options = {}) t ||= value if value.is_a?(::Time) t ||= value if value.try(:acts_like?, :time) t ||= ::Time.parse(String(value)) - t.utc.iso8601 + t.utc.iso8601(::ActiveSupport::JSON::Encoding.time_precision) end ::Subroutine::TypeCaster.register :date do |value, _options = {}| @@ -123,21 +125,16 @@ def self.cast(value, options = {}) ::Subroutine::TypeCaster.register :time, :timestamp, :datetime do |value, options = {}| next nil unless value.present? - if options[:precision] == :high - if value.try(:acts_like?, :time) - value.to_time - else - ::Time.parse(String(value)) - end - else # precision == :seconds - time = if value.try(:acts_like?, :time) - value.to_time - else - ::Time.parse(String(value)) - end - - time.change(usec: 0) + value = if value.try(:acts_like?, :time) + value.to_time + else + ::Time.parse(String(value)) end + + # High precision must be opted into. The original implementation is to set usec:0 + next value if options[:precision] == :high || ::Subroutine.preserve_time_precision? + + value.change(usec: 0) end ::Subroutine::TypeCaster.register :hash, :object, :hashmap, :dict do |value, _options = {}| diff --git a/lib/subroutine/version.rb b/lib/subroutine/version.rb index 3efc3fd..1889eff 100644 --- a/lib/subroutine/version.rb +++ b/lib/subroutine/version.rb @@ -4,7 +4,7 @@ module Subroutine MAJOR = 3 MINOR = 0 - PATCH = 4 + PATCH = 5 PRE = nil VERSION = [MAJOR, MINOR, PATCH, PRE].compact.join(".") diff --git a/test/subroutine/type_caster_test.rb b/test/subroutine/type_caster_test.rb index 9ad4e13..3d05f98 100644 --- a/test/subroutine/type_caster_test.rb +++ b/test/subroutine/type_caster_test.rb @@ -316,6 +316,20 @@ def test_time_inputs__with_seconds_precision assert_equal 0, op.time_input.usec end + def test_time_inputs__with_preserve_time_precision + Subroutine.stubs(:preserve_time_precision?).returns(true) + + time = Time.at(1678741605.123456).utc + op.time_input = time + assert_equal 2023, op.time_input.year + assert_equal 3, op.time_input.month + assert_equal 13, op.time_input.day + assert_equal 21, op.time_input.hour + assert_equal 6, op.time_input.min + assert_equal 45, op.time_input.sec + assert_equal 123456, op.time_input.usec + end + def test_time_inputs__with_high_precision op.precise_time_input = nil assert_nil op.precise_time_input @@ -410,11 +424,11 @@ def test_iso_time_inputs op.iso_time_input = '2022-12-22T10:30:24Z' assert_equal ::String, op.iso_time_input.class - assert_equal '2022-12-22T10:30:24Z', op.iso_time_input + assert_equal '2022-12-22T10:30:24.000Z', op.iso_time_input - op.iso_time_input = Time.parse('2022-12-22T10:30:24Z') + op.iso_time_input = Time.parse('2022-12-22T10:30:24.123456Z') assert_equal ::String, op.iso_time_input.class - assert_equal '2022-12-22T10:30:24Z', op.iso_time_input + assert_equal '2022-12-22T10:30:24.123Z', op.iso_time_input end def test_file_inputs