Skip to content

[pull] master from ruby:master#1226

Merged
pull[bot] merged 4 commits into
turkdevops:masterfrom
ruby:master
Jul 19, 2026
Merged

[pull] master from ruby:master#1226
pull[bot] merged 4 commits into
turkdevops:masterfrom
ruby:master

Conversation

@pull

@pull pull Bot commented Jul 19, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

jeremyevans and others added 4 commits July 19, 2026 16:08
This allows easier transition of code using DateTime to using Time.

Time#rfc3339 is an alias to #xmlschema and is just for consistency.

Time.rfc3339 is similar to Time.xmlschema, but allows a space
between the date and time, while Time.xmlschema does not (it
requires a "T").

Introduce a Time._xmlschema private method so that Time.xmlschema
and Time.rfc3339 can share most of the implementation.

ruby/time@9bca33976e
Methods with mandatory only support only dumps the ISEQ of the case with
all the arguments and not the mandatory only case. This commit changes
it to also dump the mandatory only ISEQ.
It is currentyl fairly slow as it rely on a Regexp match, which
isn't very fast an allocates a lot.

Since Ruby 3.2, `Time.new(iso_string)` is supported, and over
8x faster than `Time.xmlschema`.

So if we use the regexp with `match?` to avoid the allocations,
and then delegate to `.new`, we can make `Time.xmlschema` around
4 times faster:

```
ruby 4.0.5 (2026-05-20 revision ruby/time@64336ffd0e) +YJIT +PRISM [arm64-darwin25]
Warming up --------------------------------------
           xmlschema    67.645k i/100ms
            Time.new   531.202k i/100ms
                 opt   263.541k i/100ms
Calculating -------------------------------------
           xmlschema    680.426k (± 0.7%) i/s    (1.47 μs/i) -      3.450M in   5.070197s
            Time.new      5.736M (± 0.9%) i/s  (174.34 ns/i) -     28.685M in   5.000832s
                 opt      2.779M (± 1.1%) i/s  (359.78 ns/i) -     13.968M in   5.025252s

Comparison:
xmlschema:   680426.2 i/s
 Time.new:  5736027.1 i/s - 8.43x  faster
      opt:  2779497.0 i/s - 4.08x  faster
```

```ruby
require 'time'
require 'benchmark/ips'

class Time
  class << self
    def baseline(time)
      if /\A\s*
          (-?\d+)-(\d\d)-(\d\d)
          T
          (\d\d):(\d\d):(\d\d)
          (\.\d+)?
          (Z|[+-]\d\d(?::?\d\d)?)?
          \s*\z/ix =~ time
        year = $1.to_i
        mon = $2.to_i
        day = $3.to_i
        hour = $4.to_i
        min = $5.to_i
        sec = $6.to_i
        usec = 0
        if $7
          usec = Rational($7) * 1000000
        end
        if $8
          zone = $8
          off = zone_offset(zone)
          year, mon, day, hour, min, sec =
            apply_offset(year, mon, day, hour, min, sec, off)
          t = self.utc(year, mon, day, hour, min, sec, usec)
          force_zone!(t, zone, off)
          t
        else
          self.local(year, mon, day, hour, min, sec, usec)
        end
      else
        raise ArgumentError.new("invalid xmlschema format: #{time.inspect}")
      end
    end

    def match_new(time)
      if /\A\s*
          (-?\d+)-(\d\d)-(\d\d)
          T
          (\d\d):(\d\d):(\d\d)
          (\.\d+)?
          (Z|[+-]\d\d(?::?\d\d)?)?
          \s*\z/ix.match?(time)
        new(time)
      else
        raise ArgumentError.new("invalid xmlschema format: #{time.inspect}")
      end
    end
  end
end

now = Time.now
iso = now.iso8601(6).freeze
p now
p Time.match_new(iso)

Benchmark.ips do |x|
  x.report('xmlschema') { Time.baseline(iso) }
  x.report('Time.new') { Time.new(iso) }
  x.report('opt') { Time.match_new(iso) }

  x.compare!(order: :baseline)
end
```

ruby/time@b8c50d236c
This is to allow for easier conversion of DateTime.parse code to
Time.parse. DateTime.parse will assume UTC for parsed times without a
time zone or offset, while Time will assume local time. This allows
using `zone: "UTC"` to match the DateTime.parse behavior.

Ideally, it would have pulled the timezone from the `now` argument,
but I believe that backwards incompatibility from doing that now
is too great.

You can work around this now by appending " UTC" to the parsed string,
as that is ignored if the string actually has a timezone or offset,
but that doesn't seem like a clean approach.

ruby/time@62dc50dae5
@pull pull Bot locked and limited conversation to collaborators Jul 19, 2026
@pull pull Bot added the ⤵️ pull label Jul 19, 2026
@pull
pull Bot merged commit 183c9f8 into turkdevops:master Jul 19, 2026
4 of 5 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants