Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Inside and around ActiveSupport::Duration

Inside and around ActiveSupport::Duration

ActiveSupport::Durationと日付演算のはなし

Kento Nagata

April 20, 2017
Tweet

More Decks by Kento Nagata

Other Decks in Technology

Transcript

  1. $PSF&YU )BTI $MBTT %BUF %BUF5JNF 'JMF 'MPBU *OUFHFS ,FSOFM .PEVMF

    &OVNFSBCMF "SSBZ 4USJOH "VUPMPBE +40/ $BDIF /PUJpDBUJPOT "DUJWF4VQQPSU $POpHVSBCMF $PODFSO 5JNF;POF 9NM.JOJ ,FZ(FOFSBUPS -PHHFS 0SEFSFE)BTI 0SEFSFE0QUJPOT 3FTDVBCMF 4FDVSJUZ6UJMT 5JNF8JUI;POF .FTTBHF7FSJpFS .FTTBHF&ODSZQUPS $BMMCBDLT "SSBZ*ORVJSFS 4USJOH*ORVJSFS %VSBUJPO
  2. $PSF&YU )BTI $MBTT %BUF %BUF5JNF 'JMF 'MPBU *OUFHFS ,FSOFM .PEVMF

    &OVNFSBCMF "SSBZ 4USJOH "VUPMPBE +40/ $BDIF /PUJpDBUJPOT "DUJWF4VQQPSU $POpHVSBCMF $PODFSO 5JNF;POF 9NM.JOJ ,FZ(FOFSBUPS -PHHFS 0SEFSFE)BTI 0SEFSFE0QUJPOT 3FTDVBCMF 4FDVSJUZ6UJMT 5JNF8JUI;POF .FTTBHF7FSJpFS .FTTBHF&ODSZQUPS $BMMCBDLT "SSBZ*ORVJSFS 4USJOH*ORVJSFS %VSBUJPO /VNFSJD
  3. class Integer def months ActiveSupport::Duration.months(self) end alias :month :months def

    years ActiveSupport::Duration.years(self) end alias :year :years end
  4. class Numeric def seconds ActiveSupport::Duration.seconds(self) end alias :second :seconds def

    minutes ActiveSupport::Duration.minutes(self) end alias :minute :minutes def hours ActiveSupport::Duration.hours(self) end alias :hour :hours def days ActiveSupport::Duration.days(self) end alias :day :days def weeks ActiveSupport::Duration.weeks(self) end alias :week :weeks end
  5. module ActiveSupport class Duration SECONDS_PER_MINUTE = 60 SECONDS_PER_HOUR = 3600

    SECONDS_PER_DAY = 86400 SECONDS_PER_WEEK = 604800 SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year SECONDS_PER_YEAR = 31556952 # length of a gregorian year (365.2425 days) class << self def weeks(value) new(value * SECONDS_PER_WEEK, [[:weeks, value]]) end end def initialize(value, parts) @value, @parts = value, parts.to_h @parts.default = 0 end end end
  6. module ActiveSupport class Duration SECONDS_PER_MINUTE = 60 SECONDS_PER_HOUR = 3600

    SECONDS_PER_DAY = 86400 SECONDS_PER_WEEK = 604800 SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year SECONDS_PER_YEAR = 31556952 # length of a gregorian year (365.2425 days) class << self def weeks(value) new(value * SECONDS_PER_WEEK, [[:weeks, value]]) end end def initialize(value, parts) @value, @parts = value, parts.to_h @parts.default = 0 end end end  
  7. module ActiveSupport class Duration SECONDS_PER_MINUTE = 60 SECONDS_PER_HOUR = 3600

    SECONDS_PER_DAY = 86400 SECONDS_PER_WEEK = 604800 SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year SECONDS_PER_YEAR = 31556952 # length of a gregorian year (365.2425 days) class << self def weeks(value) new(value * SECONDS_PER_WEEK, [[:weeks, value]]) end end def initialize(value, parts) @value, @parts = value, parts.to_h @parts.default = 0 end end end   
  8. module ActiveSupport class Duration SECONDS_PER_MINUTE = 60 SECONDS_PER_HOUR = 3600

    SECONDS_PER_DAY = 86400 SECONDS_PER_WEEK = 604800 SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year SECONDS_PER_YEAR = 31556952 # length of a gregorian year (365.2425 days) class << self def weeks(value) new(value * SECONDS_PER_WEEK, [[:weeks, value]]) end end def initialize(value, parts) @value, @parts = value, parts.to_h @parts.default = 0 end end end  ඵ਺  ֤୯Ґͱ஋ͷ૊Έ߹Θͤ
  9. module ActiveSupport class Duration SECONDS_PER_MINUTE = 60 SECONDS_PER_HOUR = 3600

    SECONDS_PER_DAY = 86400 SECONDS_PER_WEEK = 604800 SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year SECONDS_PER_YEAR = 31556952 # length of a gregorian year (365.2425 days) class << self def weeks(value) new(value * SECONDS_PER_WEEK, [[:weeks, value]]) end end def initialize(value, parts) @value, @parts = value, parts.to_h @parts.default = 0 end end end  ඵ਺  ֤୯Ґͱ஋ͷ૊Έ߹Θͤ  \XFFLT^
  10. module ActiveSupport class Duration SECONDS_PER_MINUTE = 60 SECONDS_PER_HOUR = 3600

    SECONDS_PER_DAY = 86400 SECONDS_PER_WEEK = 604800 SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year SECONDS_PER_YEAR = 31556952 # length of a gregorian year (365.2425 days) class << self def seconds(value) new(value, [[:seconds, value]]) end def minutes(value) new(value * SECONDS_PER_MINUTE, [[:minutes, value]]) end def hours(value) new(value * SECONDS_PER_HOUR, [[:hours, value]]) end def days(value) new(value * SECONDS_PER_DAY, [[:days, value]]) end def weeks(value) new(value * SECONDS_PER_WEEK, [[:weeks, value]]) end def months(value) new(value * SECONDS_PER_MONTH, [[:months, value]]) end def years(value) new(value * SECONDS_PER_YEAR, [[:years, value]]) end end end end
  11. module ActiveSupport class Duration private def sum(sign, time = ::Time.current)

    parts.inject(time) do |t, (type, number)| if t.acts_like?(:time) || t.acts_like?(:date) if type == :seconds t.since(sign * number) elsif type == :minutes t.since(sign * number * 60) elsif type == :hours t.since(sign * number * 3600) else t.advance(type => sign * number) end else raise ::ArgumentError, "expected a time or date, got #{time.inspect}" end end end end end
  12. module ActiveSupport class Duration private def sum(sign, time = ::Time.current)

    parts.inject(time) do |t, (type, number)| if t.acts_like?(:time) || t.acts_like?(:date) if type == :seconds t.since(sign * number) elsif type == :minutes t.since(sign * number * 60) elsif type == :hours t.since(sign * number * 3600) else t.advance(type => sign * number) end else raise ::ArgumentError, "expected a time or date, got #{time.inspect}" end end end end end \XFFLT^
  13. module ActiveSupport class Duration private def sum(sign, time = ::Time.current)

    parts.inject(time) do |t, (type, number)| if t.acts_like?(:time) || t.acts_like?(:date) if type == :seconds t.since(sign * number) elsif type == :minutes t.since(sign * number * 60) elsif type == :hours t.since(sign * number * 3600) else t.advance(type => sign * number) end else raise ::ArgumentError, "expected a time or date, got #{time.inspect}" end end end end end XFFLT 
  14. module ActiveSupport class Duration private def sum(sign, time = ::Time.current)

    parts.inject(time) do |t, (type, number)| if t.acts_like?(:time) || t.acts_like?(:date) if type == :seconds t.since(sign * number) elsif type == :minutes t.since(sign * number * 60) elsif type == :hours t.since(sign * number * 3600) else t.advance(type => sign * number) end else raise ::ArgumentError, "expected a time or date, got #{time.inspect}" end end end end end
  15. module ActiveSupport class Duration private def sum(sign, time = ::Time.current)

    parts.inject(time) do |t, (type, number)| if t.acts_like?(:time) || t.acts_like?(:date) if type == :seconds t.since(sign * number) elsif type == :minutes t.since(sign * number * 60) elsif type == :hours t.since(sign * number * 3600) else t.advance(type => sign * number) end else raise ::ArgumentError, "expected a time or date, got #{time.inspect}" end end end end end
  16. module ActiveSupport class Duration private def sum(sign, time = ::Time.current)

    parts.inject(time) do |t, (type, number)| if t.acts_like?(:time) || t.acts_like?(:date) if type == :seconds t.since(sign * number) elsif type == :minutes t.since(sign * number * 60) elsif type == :hours t.since(sign * number * 3600) else t.advance(type => sign * number) end else raise ::ArgumentError, "expected a time or date, got #{time.inspect}" end end end end end XFFLT  
  17. class DateTime def advance(options) unless options[:weeks].nil? options[:weeks], partial_weeks = options[:weeks].divmod(1)

    options[:days] = options.fetch(:days, 0) + 7 * partial_weeks end unless options[:days].nil? options[:days], partial_days = options[:days].divmod(1) options[:hours] = options.fetch(:hours, 0) + 24 * partial_days end d = to_date.advance(options) datetime_advanced_by_date = change(year: d.year, month: d.month, day: d.day) seconds_to_advance = \ options.fetch(:seconds, 0) + options.fetch(:minutes, 0) * 60 + options.fetch(:hours, 0) * 3600 if seconds_to_advance.zero? datetime_advanced_by_date else datetime_advanced_by_date.since(seconds_to_advance) end end end
  18. class DateTime def advance(options) unless options[:weeks].nil? options[:weeks], partial_weeks = options[:weeks].divmod(1)

    options[:days] = options.fetch(:days, 0) + 7 * partial_weeks end unless options[:days].nil? options[:days], partial_days = options[:days].divmod(1) options[:hours] = options.fetch(:hours, 0) + 24 * partial_days end d = to_date.advance(options) datetime_advanced_by_date = change(year: d.year, month: d.month, day: d.day) seconds_to_advance = \ options.fetch(:seconds, 0) + options.fetch(:minutes, 0) * 60 + options.fetch(:hours, 0) * 3600 if seconds_to_advance.zero? datetime_advanced_by_date else datetime_advanced_by_date.since(seconds_to_advance) end end end \XFFLT^
  19. class DateTime def advance(options) unless options[:weeks].nil? options[:weeks], partial_weeks = options[:weeks].divmod(1)

    options[:days] = options.fetch(:days, 0) + 7 * partial_weeks end unless options[:days].nil? options[:days], partial_days = options[:days].divmod(1) options[:hours] = options.fetch(:hours, 0) + 24 * partial_days end d = to_date.advance(options) datetime_advanced_by_date = change(year: d.year, month: d.month, day: d.day) seconds_to_advance = \ options.fetch(:seconds, 0) + options.fetch(:minutes, 0) * 60 + options.fetch(:hours, 0) * 3600 if seconds_to_advance.zero? datetime_advanced_by_date else datetime_advanced_by_date.since(seconds_to_advance) end end end \XFFLT^ ༨Γ༗ΓͰׂΔ
  20. class DateTime def advance(options) unless options[:weeks].nil? options[:weeks], partial_weeks = options[:weeks].divmod(1)

    options[:days] = options.fetch(:days, 0) + 7 * partial_weeks end unless options[:days].nil? options[:days], partial_days = options[:days].divmod(1) options[:hours] = options.fetch(:hours, 0) + 24 * partial_days end d = to_date.advance(options) datetime_advanced_by_date = change(year: d.year, month: d.month, day: d.day) seconds_to_advance = \ options.fetch(:seconds, 0) + options.fetch(:minutes, 0) * 60 + options.fetch(:hours, 0) * 3600 if seconds_to_advance.zero? datetime_advanced_by_date else datetime_advanced_by_date.since(seconds_to_advance) end end end \XFFLT^ ༨Γ
  21. class DateTime def advance(options) unless options[:weeks].nil? options[:weeks], partial_weeks = options[:weeks].divmod(1)

    options[:days] = options.fetch(:days, 0) + 7 * partial_weeks end unless options[:days].nil? options[:days], partial_days = options[:days].divmod(1) options[:hours] = options.fetch(:hours, 0) + 24 * partial_days end d = to_date.advance(options) datetime_advanced_by_date = change(year: d.year, month: d.month, day: d.day) seconds_to_advance = \ options.fetch(:seconds, 0) + options.fetch(:minutes, 0) * 60 + options.fetch(:hours, 0) * 3600 if seconds_to_advance.zero? datetime_advanced_by_date else datetime_advanced_by_date.since(seconds_to_advance) end end end \XFFLT^ ༨Γ  
  22. class DateTime def advance(options) unless options[:weeks].nil? options[:weeks], partial_weeks = options[:weeks].divmod(1)

    options[:days] = options.fetch(:days, 0) + 7 * partial_weeks end unless options[:days].nil? options[:days], partial_days = options[:days].divmod(1) options[:hours] = options.fetch(:hours, 0) + 24 * partial_days end d = to_date.advance(options) datetime_advanced_by_date = change(year: d.year, month: d.month, day: d.day) seconds_to_advance = \ options.fetch(:seconds, 0) + options.fetch(:minutes, 0) * 60 + options.fetch(:hours, 0) * 3600 if seconds_to_advance.zero? datetime_advanced_by_date else datetime_advanced_by_date.since(seconds_to_advance) end end end \XFFLT^ ༨Γ  
  23. class DateTime def advance(options) unless options[:weeks].nil? options[:weeks], partial_weeks = options[:weeks].divmod(1)

    options[:days] = options.fetch(:days, 0) + 7 * partial_weeks end unless options[:days].nil? options[:days], partial_days = options[:days].divmod(1) options[:hours] = options.fetch(:hours, 0) + 24 * partial_days end d = to_date.advance(options) datetime_advanced_by_date = change(year: d.year, month: d.month, day: d.day) seconds_to_advance = \ options.fetch(:seconds, 0) + options.fetch(:minutes, 0) * 60 + options.fetch(:hours, 0) * 3600 if seconds_to_advance.zero? datetime_advanced_by_date else datetime_advanced_by_date.since(seconds_to_advance) end end end \XFFLT^    
  24. class DateTime def advance(options) unless options[:weeks].nil? options[:weeks], partial_weeks = options[:weeks].divmod(1)

    options[:days] = options.fetch(:days, 0) + 7 * partial_weeks end unless options[:days].nil? options[:days], partial_days = options[:days].divmod(1) options[:hours] = options.fetch(:hours, 0) + 24 * partial_days end d = to_date.advance(options) datetime_advanced_by_date = change(year: d.year, month: d.month, day: d.day) seconds_to_advance = \ options.fetch(:seconds, 0) + options.fetch(:minutes, 0) * 60 + options.fetch(:hours, 0) * 3600 if seconds_to_advance.zero? datetime_advanced_by_date else datetime_advanced_by_date.since(seconds_to_advance) end end end \XFFLT^ \XFFLT^
  25. class Date def advance(options) options = options.dup d = self

    d = d >> options.delete(:years) * 12 if options[:years] d = d >> options.delete(:months) if options[:months] d = d + options.delete(:weeks) * 7 if options[:weeks] d = d + options.delete(:days) if options[:days] d end end \XFFLT^
  26. class Date def advance(options) options = options.dup d = self

    d = d >> options.delete(:years) * 12 if options[:years] d = d >> options.delete(:months) if options[:months] d = d + options.delete(:weeks) * 7 if options[:weeks] d = d + options.delete(:days) if options[:days] d end end \XFFLT^ 
  27. class Date def advance(options) options = options.dup d = self

    d = d >> options.delete(:years) * 12 if options[:years] d = d >> options.delete(:months) if options[:months] d = d + options.delete(:weeks) * 7 if options[:weeks] d = d + options.delete(:days) if options[:days] d end end \XFFLT^ ೔લͷ೔෇
  28. class Date def advance(options) options = options.dup d = self

    d = d >> options.delete(:years) * 12 if options[:years] d = d >> options.delete(:months) if options[:months] d = d + options.delete(:weeks) * 7 if options[:weeks] d = d + options.delete(:days) if options[:days] d end end ݄ͷԋࢉ%BUF
  29. # nϲ݄ޙΛฦ͢ # ରԠ͢Δ݄ͷಉ͡೔͕ฦΔ Date.new(2001,1,28) >> 1 #=> #<Date: 2001-02-28

    …> # ରԠ͢Δ݄ʹಉ͡೔͕ͳ͍৔߹͸ɺ຤೔͕ฦΔ Date.new(2001,1,31) >> 1 #=> #<Date: 2001-02-28 ...> # ͦͷͨΊɺૢ࡞ͷ࢓ํʹΑͬͯ͸༧ظ͠ͳ͍ৼΔ෣͍Λ͢ΔՄೳੑ͕͋Δ Date.new(2001,1,31) >> 2 #=> #<Date: 2001-03-31 ...> Date.new(2001,1,31) >> 1 >> 1 #=> #<Date: 2001-03-28 ...> Date.new(2001,1,31) >> 1 >> -1 #=> #<Date: 2001-01-28 ...>
  30. class DateTime def advance(options) unless options[:weeks].nil? options[:weeks], partial_weeks = options[:weeks].divmod(1)

    options[:days] = options.fetch(:days, 0) + 7 * partial_weeks end unless options[:days].nil? options[:days], partial_days = options[:days].divmod(1) options[:hours] = options.fetch(:hours, 0) + 24 * partial_days end d = to_date.advance(options) datetime_advanced_by_date = change(year: d.year, month: d.month, day: d.day) seconds_to_advance = \ options.fetch(:seconds, 0) + options.fetch(:minutes, 0) * 60 + options.fetch(:hours, 0) * 3600 if seconds_to_advance.zero? datetime_advanced_by_date else datetime_advanced_by_date.since(seconds_to_advance) end end end \XFFLT^ िؒલͷ೔෇
  31. class DateTime def advance(options) unless options[:weeks].nil? options[:weeks], partial_weeks = options[:weeks].divmod(1)

    options[:days] = options.fetch(:days, 0) + 7 * partial_weeks end unless options[:days].nil? options[:days], partial_days = options[:days].divmod(1) options[:hours] = options.fetch(:hours, 0) + 24 * partial_days end d = to_date.advance(options) datetime_advanced_by_date = change(year: d.year, month: d.month, day: d.day) seconds_to_advance = \ options.fetch(:seconds, 0) + options.fetch(:minutes, 0) * 60 + options.fetch(:hours, 0) * 3600 if seconds_to_advance.zero? datetime_advanced_by_date else datetime_advanced_by_date.since(seconds_to_advance) end end end \XFFLT^ ೥݄೔͚ͩΛมߋ
  32. class DateTime def advance(options) unless options[:weeks].nil? options[:weeks], partial_weeks = options[:weeks].divmod(1)

    options[:days] = options.fetch(:days, 0) + 7 * partial_weeks end unless options[:days].nil? options[:days], partial_days = options[:days].divmod(1) options[:hours] = options.fetch(:hours, 0) + 24 * partial_days end d = to_date.advance(options) datetime_advanced_by_date = change(year: d.year, month: d.month, day: d.day) seconds_to_advance = \ options.fetch(:seconds, 0) + options.fetch(:minutes, 0) * 60 + options.fetch(:hours, 0) * 3600 if seconds_to_advance.zero? datetime_advanced_by_date else datetime_advanced_by_date.since(seconds_to_advance) end end end \XFFLT^ ೥݄೔͚ͩΛมߋͨ͠%BUFUJNF
  33. class DateTime def advance(options) unless options[:weeks].nil? options[:weeks], partial_weeks = options[:weeks].divmod(1)

    options[:days] = options.fetch(:days, 0) + 7 * partial_weeks end unless options[:days].nil? options[:days], partial_days = options[:days].divmod(1) options[:hours] = options.fetch(:hours, 0) + 24 * partial_days end d = to_date.advance(options) datetime_advanced_by_date = change(year: d.year, month: d.month, day: d.day) seconds_to_advance = \ options.fetch(:seconds, 0) + options.fetch(:minutes, 0) * 60 + options.fetch(:hours, 0) * 3600 if seconds_to_advance.zero? datetime_advanced_by_date else datetime_advanced_by_date.since(seconds_to_advance) end end end \XFFLT^ 
  34. class DateTime def advance(options) unless options[:weeks].nil? options[:weeks], partial_weeks = options[:weeks].divmod(1)

    options[:days] = options.fetch(:days, 0) + 7 * partial_weeks end unless options[:days].nil? options[:days], partial_days = options[:days].divmod(1) options[:hours] = options.fetch(:hours, 0) + 24 * partial_days end d = to_date.advance(options) datetime_advanced_by_date = change(year: d.year, month: d.month, day: d.day) seconds_to_advance = \ options.fetch(:seconds, 0) + options.fetch(:minutes, 0) * 60 + options.fetch(:hours, 0) * 3600 if seconds_to_advance.zero? datetime_advanced_by_date else datetime_advanced_by_date.since(seconds_to_advance) end end end \XFFLT^ िؒલͷ%BUFUJNF
  35. module ActiveSupport class Duration private def sum(sign, time = ::Time.current)

    parts.inject(time) do |t, (type, number)| if t.acts_like?(:time) || t.acts_like?(:date) if type == :seconds t.since(sign * number) elsif type == :minutes t.since(sign * number * 60) elsif type == :hours t.since(sign * number * 3600) else t.advance(type => sign * number) end else raise ::ArgumentError, "expected a time or date, got #{time.inspect}" end end end end end