| Commit message (Collapse) | Author | Age | Files | Lines |
|
|
|
| |
This basically reverts 8da30ad6be34339124ba4cb4e36aea260dda12bc
|
| |
|
| |
|
| |
|
|
|
|
|
|
|
| |
Support `:offset` in `Time#change` and `:zone` or `:offset`
in `ActiveSupport::TimeWithZone#change`.
Fixes #28723.
|
|
|
|
|
|
| |
We are overriding it in `Time` and `ActiveSupport::TimeWithZone` so
there's no point in having it in the `DateAndTime::Compatibility`
module. Also add some docs for the `to_time` implementations.
|
|\
| |
| | |
Allow Time#to_time on frozen objects. Return frozen time rather than "RuntimeError: can't modify frozen Time"
|
| |
| |
| |
| | |
state, and preserve_timezone flag.
|
|/
|
|
|
| |
For naming consistency when using the RFC 3339 profile
of ISO 8601 in applications.
|
| |
|
|\
| |
| | |
Speed up Time.zone.now
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
@amatsuda, during his RailsConf talk this past year, presented a
benchmark that showed `Time.zone.now` (an Active Support joint)
performing 24.97x slower than Ruby's `Time.now`. Rails master appears to
be a _bit_ faster than that, currently clocking in at 18.25x slower than
`Time.now`. Here's the exact benchmark data for that:
```
Warming up --------------------------------------
Time.now 127.923k i/100ms
Time.zone.now 10.275k i/100ms
Calculating -------------------------------------
Time.now 1.946M (± 5.9%) i/s - 9.722M in 5.010236s
Time.zone.now 106.625k (± 4.3%) i/s - 534.300k in 5.020343s
Comparison:
Time.now: 1946220.1 i/s
Time.zone.now: 106625.5 i/s - 18.25x slower
```
What if I told you we could make `Time.zone.now` _even_ faster? Well,
that's exactly what this patch accomplishes. When creating `ActiveSupport::TimeWithZone`
objects, we try to convert the provided time to be in a UTC format. All
this patch does is, in the method where we convert a provided time to
UTC, check if the provided time is already UTC, and is a `Time` object
and then return early if that is the case, This sidesteps having to continue on,
and create a new `Time` object from scratch. Here's the exact benchmark
data for my patch:
```
Warming up --------------------------------------
Time.now 124.136k i/100ms
Time.zone.now 26.260k i/100ms
Calculating -------------------------------------
Time.now 1.894M (± 6.4%) i/s - 9.434M in 5.000153s
Time.zone.now 301.654k (± 4.3%) i/s - 1.523M in 5.058328s
Comparison:
Time.now: 1893958.0 i/s
Time.zone.now: 301653.7 i/s - 6.28x slower
```
With this patch, we go from `Time.zone.now` being 18.25x slower than
`Time.now` to only being 6.28x slower than `Time.now`. I'd obviously love some
verification on this patch, since these numbers sound pretty interesting... :)
This is the benchmark-ips report I have been using while working on this:
```ruby
require 'benchmark/ips'
Time.zone = 'Eastern Time (US & Canada)'
Benchmark.ips do |x|
x.report('Time.now') {
Time.now
}
x.report('Time.zone.now') {
Time.zone.now
}
x.compare!
end
```
cc @amatsuda
cc performance folks @tenderlove and @schneems
![Pretty... pretty... pretty good.](https://media.giphy.com/media/bWeR8tA1QV4cM/giphy.gif)
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
ryandv/fix_performance_regression_in_timewithzone_to_time"
Turns out trying to cache on localtime with arguments is too hard
so we'll do it on DateAndTime::Compatibility#to_time instead.
This reverts commit 3132fa6b7d9585e04eb44b25b55d298391b040b5, reversing
changes made to 6949f8e5e7dc901d4e04ebab6c975afb33ca44c9.
|
|/
|
|
|
|
|
|
| |
Turns out trying to cache on localtime with arguments is too hard
so we'll do it on DateAndTime::Compatibility#to_time instead.
This reverts commit 9ce2d1b1a43fc4ef3db59849b7412d30583a4074, reversing
changes made to 53ede1aff2025d4391d0e05ba471fdaf3110a99c.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Previously memoization in `localtime` wasn't taking the `utc_offset`
parameter into account when returning a cached value. It now caches the
computed value depending on the `utc_offset` parameter, e.g:
Time.zone = "US/Eastern"
t = Time.zone.local(2016,5,2,11)
# => Mon, 02 May 2016 11:00:00 EDT -04:00
t.localtime(-7200)
# => 2016-05-02 13:00:00 -0200
t.localtime(-3600)
# => 2016-05-02 14:00:00 -0100
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Previously calls to `in` were being sent to the non-DST aware
method `Time#since` via `method_missing`. It is now aliased to
the DST aware `ActiveSupport::TimeWithZone#+` which handles
transitions across DST boundaries, e.g:
Time.zone = "US/Eastern"
t = Time.zone.local(2016,11,6,1)
# => Sun, 06 Nov 2016 01:00:00 EDT -05:00
t.in(1.hour)
# => Sun, 06 Nov 2016 01:00:00 EST -05:00
|
| |
|
|
|
|
|
|
|
| |
As demonstrated in #25880 the to_time method converts the utc time
instance to a local time instance which is an expensive operation.
Since to_datetime involves similar expensive operations we should
also cache it to speed up comparison with lots of values.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A performance regression was introduced by commit b79adc4323ff289aed3f5787fdfbb9542aa4f89f
from Rails 4.0.0.beta1, in which `TimeWithZone#to_time` no longer returns a
cached instance attribute but instead coerces the value to `Time`. This
coerced value is not cached, and recomputation degrades the performance
of comparisons between TimeWithZone objects.
See https://github.com/rails/rails/commit/b79adc4323ff289aed3f5787fdfbb9542aa4f89f#diff-3497a506c921a3a3e40fd517e92e4fe3R322
for the change in question.
The following benchmark, which reverts the change linked above, demonstrates
the performance regression:
require 'active_support/time'
require 'benchmark/ips'
utc = Time.utc(2000, 1, 1, 0)
time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
twz = ActiveSupport::TimeWithZone.new(utc, time_zone)
twz2 = ActiveSupport::TimeWithZone.new(Time.utc(1999, 12, 31, 23, 59, 59), ActiveSupport::TimeZone['UTC'])
patchedTimeWithZone = Class.new(ActiveSupport::TimeWithZone) do
def to_time
utc
end
end
patched_twz = patchedTimeWithZone.new(utc, time_zone)
patched_twz2 = patchedTimeWithZone.new(Time.utc(1999, 12, 31, 23, 59, 59), ActiveSupport::TimeZone['UTC'])
Benchmark.ips do |x|
x.report("comparison out of the box") { twz <=> twz2 }
x.report("comparison reverting to_time") { patched_twz <=> patched_twz2 }
x.compare!
end
The results, when run in rails-dev-box, are as follows:
Warming up --------------------------------------
comparison out of the box
24.765k i/100ms
comparison reverting to_time
57.237k i/100ms
Calculating -------------------------------------
comparison out of the box
517.245k (± 4.7%) i/s - 2.600M in 5.038700s
comparison reverting to_time
2.624M (± 5.0%) i/s - 13.050M in 4.985808s
Comparison:
comparison reverting to_time: 2624266.1 i/s
comparison out of the box: 517244.6 i/s - 5.07x slower
The change made to run the benchmark, however, is not possible, as it would
undo the intent to standardize the return value of `to_time` to `Time` in
the system timezone.
Our proposed solution is to restore the caching behaviour of `to_time`
as it existed prior to the change linked above.
Benchmark of our solution:
require 'active_support/time'
require 'benchmark/ips'
patchedTimeWithZone = Class.new(ActiveSupport::TimeWithZone) do
def to_time
@to_time ||= super
end
end
utc = Time.utc(2000, 1, 1, 0)
time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
twz = ActiveSupport::TimeWithZone.new(utc, time_zone)
twz2 = ActiveSupport::TimeWithZone.new(Time.utc(1999, 12, 31, 23, 59, 59), ActiveSupport::TimeZone['UTC'])
patched_twz = patchedTimeWithZone.new(utc, time_zone)
patched_twz2 = patchedTimeWithZone.new(Time.utc(1999, 12, 31, 23, 59, 59), ActiveSupport::TimeZone['UTC'])
Benchmark.ips do |x|
x.report("TimeWithZone comparison - existing implementation") { twz <=> twz2 }
x.report("TimeWithZone comparison - caching implementation") { patched_twz <=> patched_twz2 }
x.compare!
end
Results in rails-dev-box:
Warming up --------------------------------------
TimeWithZone comparison - existing implementation
26.629k i/100ms
TimeWithZone comparison - caching implementation
59.144k i/100ms
Calculating -------------------------------------
TimeWithZone comparison - existing implementation
489.757k (± 4.2%) i/s - 2.450M in 5.011639s
TimeWithZone comparison - caching implementation
2.802M (± 5.3%) i/s - 13.958M in 4.996116s
Comparison:
TimeWithZone comparison - caching implementation: 2801519.1 i/s
TimeWithZone comparison - existing implementation: 489756.7 i/s - 5.72x slower
|
|
|
|
|
|
|
|
| |
Style/SpaceBeforeBlockBraces
Style/SpaceInsideBlockBraces
Style/SpaceInsideHashLiteralBraces
Fix all violations in the repository.
|
|
|
|
|
|
|
|
|
| |
A few have been left for aesthetic reasons, but have made a pass
and removed most of them.
Note that if the method `foo` returns an array, `foo << 1`
is a regular push, nothing to do with assignments, so
no self required.
|
| |
|
|
|
|
|
| |
The current code base is not uniform. After some discussion,
we have chosen to go with double quotes by default.
|
|
|
|
|
|
|
|
| |
Since 434df00 week durations are no longer converted to days. This means
we need to add :weeks to the parts that ActiveSupport::TimeWithZone will
consider being of variable duration to take account of DST transitions.
Fixes #26039.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Previously these methods could return either a DateTime or a Time
depending on how the ActiveSupport::TimeWithZone instance had
been constructed. Changing to always return an instance of Time
eliminates a possible stack level too deep error in to_time where
it was wrapping a DateTime instance.
As a consequence of this the internal time value is now always an
instance of Time in the UTC timezone, whether that's as the UTC
time directly or a representation of the local time in the timezone.
There should be no consequences of this internal change and if
there are it's a bug due to leaky abstractions.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
In Ruby 2.4 the `to_time` method for both `DateTime` and `Time` will
preserve the timezone of the receiver when converting to an instance
of `Time`. Since Rails 5.0 will support Ruby 2.2, 2.3 and later we
need to introduce a compatibility layer so that apps that upgrade do
not break. New apps will have a config initializer file that defaults
to match the new Ruby 2.4 behavior going forward.
For information about the changes to Ruby see:
https://bugs.ruby-lang.org/issues/12189
https://bugs.ruby-lang.org/issues/12271
Fixes #24617.
|
|
|
|
|
|
|
| |
The stated value of `now` would actually give the same result for
`now - 24.hours` and `now - 1.day`. Use an alternative value for
`now` that demonstrates the difference between subtracting
`24.hours` and `1.day`.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Currently, ActiveSupport::TimeWithZone#utc? simply runs a check to see
if the linked ActiveSupport::TimeZone's name is "UTC". This will only
return true for ActiveSupport::TimeZone["UTC"], but not for time zones
such as "Etc/UTC", "Etc/Universal", or other time zones that are aliases
for UTC. Interestingly enough, ActiveSupport::TimeWithZone#utc? is also
aliased as #gmt? but will return false for the "GMT" timezone (along
with other TZInfo aliases for GMT).
Instead of running a simple check on the TimeZone name, we can rely on
the underlying TZInfo::TimezonePeriod and TZInfo::TimezoneOffset which
keep a record of of the offset's abbreviated name. The possibilities
here for UTC time zones are `:UTC`, `:UCT`, and `:GMT`.
Signed-off-by: David <me@davidcel.is>
|
| |
|
| |
|
|
|
|
|
|
|
| |
The concept of a blank date or time doesn't make sense so we can short
circuit the calls for `blank?` on these classes to gain small speed boost.
Fixes #21657
|
| |
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Before:
```ruby
twz = DateTime.now.in_time_zone
twz.eql?(twz.dup) => false
```
Now:
```ruby
twz = DateTime.now.in_time_zone
twz.eql?(twz.dup) => true
```
Please notice that this fix the `TimeWithZone` comparison to itself,
not to `DateTime`. Based on #3725, `DateTime` should not be equal to
`TimeWithZone`.
|
|
|
|
|
|
|
|
| |
ActiveSupport::TimeWithZone references `ActiveSupport::Duration` but
does not require it, which can result in a `LoadError` when required
directly without requiring a component less granular like
`active_support/time`, where the autoload for `ActiveSupport::Duration`
is set up.
|
|
|
|
|
|
|
|
|
|
|
| |
Previously when converting AS::TimeWithZone to YAML it would be output
as a UTC timestamp. Whilst this preserves the time information accurately
it loses the timezone information. This commit changes that so that it is
saved along with the time information. It also provides nicer encoding of
AS::TimeZone instances themselves which previously embedded all of the
data from the TZInfo records.
Fixes #9183.
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Stems from [Google group discussion](https://groups.google.com/forum/#!topic/rubyonrails-core/jSPbP-TNLb0).
Currently `AS::TimeWithZone` has two methods to add an interval to a time:
`+(other)` and `since(other)` ([docs](http://edgeapi.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html)).
The two methods are "pretty much" equivalent in every case:
1. When adding any interval to an `AS::TimeWithZone` representing a `Time`:
```ruby
t = Time.now.in_time_zone #=> Thu, 04 Dec 2014 18:56:28 EST -05:00
t + 1 == t.since(1) #=> true
t + 1.day == t.since(1.day) #=> true
t + 1.month == t.since(1.month) #=> true
```
2. When adding any interval to an `AS::TimeWithZone` representing a `Date`:
```ruby
d = Date.today.in_time_zone #=> Thu, 04 Dec 2014 00:00:00 EST -05:00
d + 1 == d.since(1) #=> true
d + 1.day == d.since(1.day) #=> true
d + 1.month == d.since(1.month) #=> true
```
3. When adding any interval to an `AS::TimeWithZone` representing a `DateTime`:
```ruby
dt = DateTime.now.in_time_zone #=> Thu, 04 Dec 2014 18:57:28 EST -05:00
dt + 1 == dt.since(1) #=> true
dt + 1.day == dt.since(1.day) #=> true
dt + 1.month == dt.since(1.month) #=> false
```
As you can see, the only case in which they differ is when the interval added
to a `DateTime` is in a format like `1.month`.
However, this usage of "since" is explicitly discouraged by the
[documentation of `DateTime#since`](https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/date_time/calculations.rb#L86L88):
> Returns a new DateTime representing the time a number of seconds since the instance time.
> Do not use this method in combination with x.months, use months_since instead!
And indeed, following this recommendation the correct result is returned:
```ruby
dt + 1.month == dt.months_since 1 #=> true
```
Therefore, my proposal is to remove the method definition of `TimeWithZone#since`
and instead replace it with a simple `alias_method :since, :+`.
The rationale is that the only case where they differ is a case that is
explicitly discouraged as "wrong".
In my opinion, having two methods named `since` and `+` and having to figure
out exactly what the difference is makes the codebase more confusing.
However, I understand this PR is "subjective", so if you feel like it's better
to ignore this, feel free to close the PR.
Thanks!
|
|\ |
|
| |
| |
| |
| |
| |
| | |
The example was taken from the commit message 676d6a6.
[ci skip]
|
|/
|
|
| |
[ci skip]
|
| |
|
|\
| |
| |
| | |
Fixed `ActiveSupport::TimeWithZone#-` so precision is not unnecessarily lost
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
When working with objects with a nanosecond component, the `-` method may
unnecessarily cause loss of precision.
`ActiveSupport::TimeWithZone#-` should return the same result as if we were
using `Time#-`:
Time.now.end_of_day - Time.now.beginning_of_day #=> 86399.999999999
Before:
Time.zone.now.end_of_day.nsec #=> 999999999
Time.zone.now.end_of_day - Time.zone.now.beginning_of_day #=> 86400.0
After:
Time.zone.now.end_of_day - Time.zone.now.beginning_of_day
#=> 86399.999999999
|
|/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
I've noticed that `String(model.created_at)` is performing poorly in comparision
with other fields. The source of the problem is a way `Kernel#String` works: it first
tries to call `to_str` (which causes `NoMethodError` in `method_missing`) and then calls `to_s`.
Performance tests:
tz = Time.zone.now
Benchmark.ips do |x|
x.report { String(tz) }
end
Without this code:
Calculating -------------------------------------
572 i/100ms
-------------------------------------------------
10177.7 (±18.2%) i/s - 48620 in 5.000325s
With this code:
Calculating -------------------------------------
1518 i/100ms
-------------------------------------------------
138984.2 (±10.1%) i/s - 677028 in 4.974897s
|
|
|
| |
Current docs are wrong. Does not accept strftime inputs.
|