diff --git a/dynamoid.gemspec b/dynamoid.gemspec index 74b14ce2..754411d6 100644 --- a/dynamoid.gemspec +++ b/dynamoid.gemspec @@ -23,7 +23,8 @@ Gem::Specification.new do |spec| 'Pascal Corpet', 'Brian Glusman', 'Peter Boling', - 'Andrew Konchin' + 'Andrew Konchin', + 'Ryan Foster' ] spec.email = ['peter.boling@gmail.com', 'brian@stellaservice.com'] diff --git a/lib/dynamoid/dumping.rb b/lib/dynamoid/dumping.rb index 7af26ba6..bdc6b64d 100644 --- a/lib/dynamoid/dumping.rb +++ b/lib/dynamoid/dumping.rb @@ -222,7 +222,11 @@ def format_datetime(value, options) if use_string_format value_in_time_zone = Dynamoid::DynamodbTimeZone.in_time_zone(value) - value_in_time_zone.iso8601 + if options[:iso_precision].nil? + value_in_time_zone.iso8601 + else + value_in_time_zone.iso8601(options[:iso_precision].to_i) + end else unless value.respond_to?(:to_i) && value.respond_to?(:nsec) value = value.to_time diff --git a/lib/dynamoid/type_casting.rb b/lib/dynamoid/type_casting.rb index 08411576..092a5b6e 100644 --- a/lib/dynamoid/type_casting.rb +++ b/lib/dynamoid/type_casting.rb @@ -227,14 +227,22 @@ def process(value) nil elsif value.is_a?(String) dt = begin + # parse first for stricter constraint failure before attempting to parse iso for precision support + # e.g. DateTime.iso8601("2018-12") is valid DateTime.parse(value) + DateTime.iso8601(value) rescue StandardError nil end if dt - seconds = string_utc_offset(value) || ApplicationTimeZone.utc_offset - offset = seconds_to_offset(seconds) - DateTime.new(dt.year, dt.mon, dt.mday, dt.hour, dt.min, dt.sec, offset) + dt_has_precision = dt.to_f % 1 != 0 + if dt_has_precision + dt.utc + else + seconds = string_utc_offset(value) || ApplicationTimeZone.utc_offset + offset = seconds_to_offset(seconds) + DateTime.new(dt.year, dt.mon, dt.mday, dt.hour, dt.min, dt.sec, offset) + end end else value.to_datetime diff --git a/lib/dynamoid/undumping.rb b/lib/dynamoid/undumping.rb index a1ee1015..5dfb0965 100644 --- a/lib/dynamoid/undumping.rb +++ b/lib/dynamoid/undumping.rb @@ -202,7 +202,7 @@ def process(value) else @options[:store_as_string] end - value = DateTime.iso8601(value).to_time.to_i if use_string_format + value = DateTime.iso8601(value).to_time if use_string_format ApplicationTimeZone.at(value) end end diff --git a/lib/dynamoid/version.rb b/lib/dynamoid/version.rb index cc72136f..278fcf7e 100644 --- a/lib/dynamoid/version.rb +++ b/lib/dynamoid/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Dynamoid - VERSION = '3.2.0' + VERSION = '3.2.1' end diff --git a/spec/dynamoid/dumping_spec.rb b/spec/dynamoid/dumping_spec.rb index 216be603..979b8351 100644 --- a/spec/dynamoid/dumping_spec.rb +++ b/spec/dynamoid/dumping_spec.rb @@ -239,6 +239,8 @@ Dynamoid.config.store_datetime_as_string = store_datetime_as_string end + + it 'prioritize field option over global one' do store_datetime_as_string = Dynamoid.config.store_datetime_as_string Dynamoid.config.store_datetime_as_string = false @@ -366,6 +368,18 @@ expect(raw_attributes(obj)[:signed_up_on]).to eql('2017-09-25') end + it 'stores in string format with precision when :iso_precision is present' do + klass = new_class do + field :signed_up_on, :datetime, store_as_string: true, iso_precision: 6 + end + + signed_up_on = DateTime.now.utc.iso8601(6) + + obj = klass.create(signed_up_on: signed_up_on) + + expect(reload(obj).signed_up_on.to_f).to eql(DateTime.iso8601(signed_up_on).to_f) + end + it 'stores in string format when global option :store_date_as_string is true' do klass = new_class do field :signed_up_on, :date