instance method change

Ruby on Rails 8.0.4

Since v3.0.20

Available in: v3.0.20 v3.1.12 v3.2.22.5 v4.0.13 v4.1.16 v4.2.9 v5.2.8.1 v6.0.6 v6.1.7.10 v7.0.10 v7.1.6 v7.2.3 v8.0.4 v8.1.2

Signature

change(options)

Returns a new Time where one or more of the elements have been changed according to the options parameter. The time options (:hour, :min, :sec, :usec, :nsec) reset cascadingly, so if only the hour is passed, then minute, sec, usec, and nsec is set to 0. If the hour and minute is passed, then sec, usec, and nsec is set to 0. The options parameter takes a hash with any of these keys: :year, :month, :day, :hour, :min, :sec, :usec, :nsec, :offset. Pass either :usec or :nsec, not both.

Time.new(2012, 8, 29, 22, 35, 0).change(day: 1)              # => Time.new(2012, 8, 1, 22, 35, 0)
Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1)  # => Time.new(1981, 8, 1, 22, 35, 0)
Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => Time.new(1981, 8, 29, 0, 0, 0)

Parameters

options req
Source
# File activesupport/lib/active_support/core_ext/time/calculations.rb, line 123
  def change(options)
    new_year   = options.fetch(:year, year)
    new_month  = options.fetch(:month, month)
    new_day    = options.fetch(:day, day)
    new_hour   = options.fetch(:hour, hour)
    new_min    = options.fetch(:min, options[:hour] ? 0 : min)
    new_sec    = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
    new_offset = options.fetch(:offset, nil)

    if new_nsec = options[:nsec]
      raise ArgumentError, "Can't change both :nsec and :usec at the same time: #{options.inspect}" if options[:usec]
      new_usec = Rational(new_nsec, 1000)
    else
      new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
    end

    raise ArgumentError, "argument out of range" if new_usec >= 1000000

    new_sec += Rational(new_usec, 1000000)

    if new_offset
      ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, new_offset)
    elsif utc?
      ::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec)
    elsif zone.respond_to?(:utc_to_local)
      new_time = ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, zone)

      # Some versions of Ruby have a bug where Time.new with a zone object and
      # fractional seconds will end up with a broken utc_offset.
      # This is fixed in Ruby 3.3.1 and 3.2.4
      unless new_time.utc_offset.integer?
        new_time += 0
      end

      # When there are two occurrences of a nominal time due to DST ending,
      # `Time.new` chooses the first chronological occurrence (the one with a
      # larger UTC offset). However, for `change`, we want to choose the
      # occurrence that matches this time's UTC offset.
      #
      # If the new time's UTC offset is larger than this time's UTC offset, the
      # new time might be a first chronological occurrence. So we add the offset
      # difference to fast-forward the new time, and check if the result has the
      # desired UTC offset (i.e. is the second chronological occurrence).
      offset_difference = new_time.utc_offset - utc_offset
      if offset_difference > 0 && (new_time_2 = new_time + offset_difference).utc_offset == utc_offset
        new_time_2
      else
        new_time
      end
    elsif zone
      ::Time.local(new_sec, new_min, new_hour, new_day, new_month, new_year, nil, nil, isdst, nil)
    else
      ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, utc_offset)
    end
  end

Defined in activesupport/lib/active_support/core_ext/time/calculations.rb line 123 · View on GitHub · Improve this page · Find usages on GitHub

Defined in Time

Type at least 2 characters to search.

↑↓ navigate · open · esc close