activesupport 5.0.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (268) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +343 -590
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -4
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/all.rb +5 -3
  7. data/lib/active_support/array_inquirer.rb +11 -5
  8. data/lib/active_support/backtrace_cleaner.rb +33 -5
  9. data/lib/active_support/benchmarkable.rb +5 -3
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache/file_store.rb +45 -53
  12. data/lib/active_support/cache/mem_cache_store.rb +81 -79
  13. data/lib/active_support/cache/memory_store.rb +69 -41
  14. data/lib/active_support/cache/null_store.rb +11 -4
  15. data/lib/active_support/cache/redis_cache_store.rb +493 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +74 -37
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  18. data/lib/active_support/cache.rb +332 -161
  19. data/lib/active_support/callbacks.rb +657 -586
  20. data/lib/active_support/concern.rb +79 -6
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +35 -0
  22. data/lib/active_support/concurrency/share_lock.rb +59 -19
  23. data/lib/active_support/configurable.rb +15 -17
  24. data/lib/active_support/configuration_file.rb +46 -0
  25. data/lib/active_support/core_ext/array/access.rb +21 -7
  26. data/lib/active_support/core_ext/array/conversions.rb +20 -18
  27. data/lib/active_support/core_ext/array/extract.rb +21 -0
  28. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  29. data/lib/active_support/core_ext/array/grouping.rb +3 -1
  30. data/lib/active_support/core_ext/array/inquiry.rb +3 -1
  31. data/lib/active_support/core_ext/array/wrap.rb +2 -0
  32. data/lib/active_support/core_ext/array.rb +9 -7
  33. data/lib/active_support/core_ext/benchmark.rb +5 -3
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +6 -6
  35. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  36. data/lib/active_support/core_ext/class/attribute.rb +52 -49
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  38. data/lib/active_support/core_ext/class/subclasses.rb +18 -26
  39. data/lib/active_support/core_ext/class.rb +4 -2
  40. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  41. data/lib/active_support/core_ext/date/blank.rb +3 -1
  42. data/lib/active_support/core_ext/date/calculations.rb +16 -13
  43. data/lib/active_support/core_ext/date/conversions.rb +23 -21
  44. data/lib/active_support/core_ext/date/zones.rb +4 -2
  45. data/lib/active_support/core_ext/date.rb +7 -5
  46. data/lib/active_support/core_ext/date_and_time/calculations.rb +82 -53
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -5
  48. data/lib/active_support/core_ext/date_and_time/zones.rb +9 -9
  49. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  50. data/lib/active_support/core_ext/date_time/blank.rb +3 -1
  51. data/lib/active_support/core_ext/date_time/calculations.rb +23 -11
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +15 -2
  53. data/lib/active_support/core_ext/date_time/conversions.rb +14 -13
  54. data/lib/active_support/core_ext/date_time.rb +7 -5
  55. data/lib/active_support/core_ext/digest/uuid.rb +7 -5
  56. data/lib/active_support/core_ext/digest.rb +3 -0
  57. data/lib/active_support/core_ext/enumerable.rb +165 -29
  58. data/lib/active_support/core_ext/file/atomic.rb +7 -5
  59. data/lib/active_support/core_ext/file.rb +3 -1
  60. data/lib/active_support/core_ext/hash/conversions.rb +40 -39
  61. data/lib/active_support/core_ext/hash/deep_merge.rb +8 -12
  62. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  63. data/lib/active_support/core_ext/hash/except.rb +4 -2
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -2
  65. data/lib/active_support/core_ext/hash/keys.rb +9 -36
  66. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  67. data/lib/active_support/core_ext/hash/slice.rb +8 -29
  68. data/lib/active_support/core_ext/hash.rb +10 -9
  69. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  70. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  71. data/lib/active_support/core_ext/integer/time.rb +11 -18
  72. data/lib/active_support/core_ext/integer.rb +5 -3
  73. data/lib/active_support/core_ext/kernel/concern.rb +3 -1
  74. data/lib/active_support/core_ext/kernel/reporting.rb +3 -1
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +5 -4
  77. data/lib/active_support/core_ext/load_error.rb +2 -23
  78. data/lib/active_support/core_ext/marshal.rb +6 -2
  79. data/lib/active_support/core_ext/module/aliasing.rb +5 -48
  80. data/lib/active_support/core_ext/module/anonymous.rb +2 -0
  81. data/lib/active_support/core_ext/module/attr_internal.rb +7 -5
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +53 -59
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +31 -24
  84. data/lib/active_support/core_ext/module/concerning.rb +16 -11
  85. data/lib/active_support/core_ext/module/delegation.rb +159 -44
  86. data/lib/active_support/core_ext/module/deprecation.rb +2 -0
  87. data/lib/active_support/core_ext/module/introspection.rb +23 -26
  88. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  89. data/lib/active_support/core_ext/module/remove_method.rb +5 -23
  90. data/lib/active_support/core_ext/module.rb +13 -12
  91. data/lib/active_support/core_ext/name_error.rb +36 -2
  92. data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
  93. data/lib/active_support/core_ext/numeric/conversions.rb +129 -134
  94. data/lib/active_support/core_ext/numeric/time.rb +18 -26
  95. data/lib/active_support/core_ext/numeric.rb +5 -4
  96. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  97. data/lib/active_support/core_ext/object/blank.rb +14 -2
  98. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  99. data/lib/active_support/core_ext/object/deep_dup.rb +4 -2
  100. data/lib/active_support/core_ext/object/duplicable.rb +13 -62
  101. data/lib/active_support/core_ext/object/inclusion.rb +3 -1
  102. data/lib/active_support/core_ext/object/instance_variables.rb +2 -0
  103. data/lib/active_support/core_ext/object/json.rb +42 -15
  104. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  105. data/lib/active_support/core_ext/object/to_query.rb +10 -5
  106. data/lib/active_support/core_ext/object/try.rb +20 -8
  107. data/lib/active_support/core_ext/object/with_options.rb +15 -2
  108. data/lib/active_support/core_ext/object.rb +14 -12
  109. data/lib/active_support/core_ext/range/compare_range.rb +82 -0
  110. data/lib/active_support/core_ext/range/conversions.rb +35 -25
  111. data/lib/active_support/core_ext/range/each.rb +5 -2
  112. data/lib/active_support/core_ext/range/include_time_with_zone.rb +28 -0
  113. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  114. data/lib/active_support/core_ext/range.rb +7 -4
  115. data/lib/active_support/core_ext/regexp.rb +10 -1
  116. data/lib/active_support/core_ext/securerandom.rb +28 -6
  117. data/lib/active_support/core_ext/string/access.rb +9 -18
  118. data/lib/active_support/core_ext/string/behavior.rb +2 -0
  119. data/lib/active_support/core_ext/string/conversions.rb +5 -2
  120. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  121. data/lib/active_support/core_ext/string/filters.rb +47 -4
  122. data/lib/active_support/core_ext/string/indent.rb +6 -4
  123. data/lib/active_support/core_ext/string/inflections.rb +78 -29
  124. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  125. data/lib/active_support/core_ext/string/multibyte.rb +10 -5
  126. data/lib/active_support/core_ext/string/output_safety.rb +86 -31
  127. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  128. data/lib/active_support/core_ext/string/strip.rb +5 -1
  129. data/lib/active_support/core_ext/string/zones.rb +4 -2
  130. data/lib/active_support/core_ext/string.rb +15 -13
  131. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  132. data/lib/active_support/core_ext/symbol.rb +3 -0
  133. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  134. data/lib/active_support/core_ext/time/calculations.rb +117 -45
  135. data/lib/active_support/core_ext/time/compatibility.rb +13 -2
  136. data/lib/active_support/core_ext/time/conversions.rb +18 -12
  137. data/lib/active_support/core_ext/time/zones.rb +9 -7
  138. data/lib/active_support/core_ext/time.rb +7 -5
  139. data/lib/active_support/core_ext/uri.rb +12 -7
  140. data/lib/active_support/core_ext.rb +3 -2
  141. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  142. data/lib/active_support/current_attributes.rb +208 -0
  143. data/lib/active_support/dependencies/autoload.rb +2 -0
  144. data/lib/active_support/dependencies/interlock.rb +7 -1
  145. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  146. data/lib/active_support/dependencies.rb +172 -98
  147. data/lib/active_support/deprecation/behaviors.rb +45 -13
  148. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  149. data/lib/active_support/deprecation/disallowed.rb +56 -0
  150. data/lib/active_support/deprecation/instance_delegator.rb +16 -2
  151. data/lib/active_support/deprecation/method_wrappers.rb +32 -17
  152. data/lib/active_support/deprecation/proxy_wrappers.rb +35 -7
  153. data/lib/active_support/deprecation/reporting.rb +61 -16
  154. data/lib/active_support/deprecation.rb +17 -9
  155. data/lib/active_support/descendants_tracker.rb +61 -9
  156. data/lib/active_support/digest.rb +20 -0
  157. data/lib/active_support/duration/iso8601_parser.rb +67 -66
  158. data/lib/active_support/duration/iso8601_serializer.rb +25 -17
  159. data/lib/active_support/duration.rb +349 -46
  160. data/lib/active_support/encrypted_configuration.rb +45 -0
  161. data/lib/active_support/encrypted_file.rb +117 -0
  162. data/lib/active_support/environment_inquirer.rb +20 -0
  163. data/lib/active_support/evented_file_update_checker.rb +88 -112
  164. data/lib/active_support/execution_wrapper.rb +25 -13
  165. data/lib/active_support/executor.rb +3 -1
  166. data/lib/active_support/file_update_checker.rb +56 -51
  167. data/lib/active_support/fork_tracker.rb +62 -0
  168. data/lib/active_support/gem_version.rb +4 -2
  169. data/lib/active_support/gzip.rb +7 -5
  170. data/lib/active_support/hash_with_indifferent_access.rb +153 -49
  171. data/lib/active_support/i18n.rb +9 -6
  172. data/lib/active_support/i18n_railtie.rb +30 -20
  173. data/lib/active_support/inflections.rb +13 -11
  174. data/lib/active_support/inflector/inflections.rb +28 -15
  175. data/lib/active_support/inflector/methods.rb +120 -109
  176. data/lib/active_support/inflector/transliterate.rb +60 -25
  177. data/lib/active_support/inflector.rb +7 -5
  178. data/lib/active_support/json/decoding.rb +30 -29
  179. data/lib/active_support/json/encoding.rb +22 -11
  180. data/lib/active_support/json.rb +4 -2
  181. data/lib/active_support/key_generator.rb +6 -36
  182. data/lib/active_support/lazy_load_hooks.rb +53 -20
  183. data/lib/active_support/locale/en.rb +33 -0
  184. data/lib/active_support/locale/en.yml +7 -3
  185. data/lib/active_support/log_subscriber/test_helper.rb +11 -9
  186. data/lib/active_support/log_subscriber.rb +51 -18
  187. data/lib/active_support/logger.rb +9 -22
  188. data/lib/active_support/logger_silence.rb +14 -21
  189. data/lib/active_support/logger_thread_safe_level.rb +55 -8
  190. data/lib/active_support/message_encryptor.rb +170 -53
  191. data/lib/active_support/message_verifier.rb +91 -20
  192. data/lib/active_support/messages/metadata.rb +80 -0
  193. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  194. data/lib/active_support/messages/rotator.rb +57 -0
  195. data/lib/active_support/multibyte/chars.rb +24 -78
  196. data/lib/active_support/multibyte/unicode.rb +21 -352
  197. data/lib/active_support/multibyte.rb +4 -2
  198. data/lib/active_support/notifications/fanout.rb +121 -19
  199. data/lib/active_support/notifications/instrumenter.rb +78 -14
  200. data/lib/active_support/notifications.rb +80 -12
  201. data/lib/active_support/number_helper/number_converter.rb +17 -16
  202. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -9
  203. data/lib/active_support/number_helper/number_to_delimited_converter.rb +5 -3
  204. data/lib/active_support/number_helper/number_to_human_converter.rb +13 -12
  205. data/lib/active_support/number_helper/number_to_human_size_converter.rb +11 -13
  206. data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
  207. data/lib/active_support/number_helper/number_to_phone_converter.rb +5 -4
  208. data/lib/active_support/number_helper/number_to_rounded_converter.rb +18 -55
  209. data/lib/active_support/number_helper/rounding_helper.rb +50 -0
  210. data/lib/active_support/number_helper.rb +45 -16
  211. data/lib/active_support/option_merger.rb +25 -4
  212. data/lib/active_support/ordered_hash.rb +6 -4
  213. data/lib/active_support/ordered_options.rb +23 -9
  214. data/lib/active_support/parameter_filter.rb +133 -0
  215. data/lib/active_support/per_thread_registry.rb +7 -5
  216. data/lib/active_support/proxy_object.rb +2 -0
  217. data/lib/active_support/rails.rb +8 -9
  218. data/lib/active_support/railtie.rb +62 -11
  219. data/lib/active_support/reloader.rb +12 -11
  220. data/lib/active_support/rescuable.rb +20 -11
  221. data/lib/active_support/secure_compare_rotator.rb +51 -0
  222. data/lib/active_support/security_utils.rb +26 -15
  223. data/lib/active_support/string_inquirer.rb +12 -3
  224. data/lib/active_support/subscriber.rb +77 -23
  225. data/lib/active_support/tagged_logging.rb +52 -17
  226. data/lib/active_support/test_case.rb +106 -29
  227. data/lib/active_support/testing/assertions.rb +144 -8
  228. data/lib/active_support/testing/autorun.rb +5 -10
  229. data/lib/active_support/testing/constant_lookup.rb +2 -1
  230. data/lib/active_support/testing/declarative.rb +3 -1
  231. data/lib/active_support/testing/deprecation.rb +4 -2
  232. data/lib/active_support/testing/file_fixtures.rb +4 -0
  233. data/lib/active_support/testing/isolation.rb +19 -24
  234. data/lib/active_support/testing/method_call_assertions.rb +31 -2
  235. data/lib/active_support/testing/parallelization/server.rb +78 -0
  236. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  237. data/lib/active_support/testing/parallelization.rb +51 -0
  238. data/lib/active_support/testing/setup_and_teardown.rb +13 -8
  239. data/lib/active_support/testing/stream.rb +30 -29
  240. data/lib/active_support/testing/tagged_logging.rb +3 -1
  241. data/lib/active_support/testing/time_helpers.rb +125 -24
  242. data/lib/active_support/time.rb +14 -12
  243. data/lib/active_support/time_with_zone.rb +142 -55
  244. data/lib/active_support/values/time_zone.rb +160 -53
  245. data/lib/active_support/version.rb +3 -1
  246. data/lib/active_support/xml_mini/jdom.rb +115 -114
  247. data/lib/active_support/xml_mini/libxml.rb +15 -14
  248. data/lib/active_support/xml_mini/libxmlsax.rb +16 -18
  249. data/lib/active_support/xml_mini/nokogiri.rb +13 -13
  250. data/lib/active_support/xml_mini/nokogirisax.rb +15 -16
  251. data/lib/active_support/xml_mini/rexml.rb +18 -9
  252. data/lib/active_support/xml_mini.rb +44 -42
  253. data/lib/active_support.rb +19 -10
  254. metadata +79 -37
  255. data/lib/active_support/concurrency/latch.rb +0 -19
  256. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  257. data/lib/active_support/core_ext/hash/compact.rb +0 -20
  258. data/lib/active_support/core_ext/hash/transform_values.rb +0 -29
  259. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  260. data/lib/active_support/core_ext/kernel/debugger.rb +0 -3
  261. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -3
  262. data/lib/active_support/core_ext/module/qualified_const.rb +0 -70
  263. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  264. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -26
  265. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  266. data/lib/active_support/core_ext/struct.rb +0 -3
  267. data/lib/active_support/core_ext/time/marshal.rb +0 -3
  268. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,48 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/redefine_method"
4
+ require "active_support/core_ext/time/calculations"
5
+ require "concurrent/map"
6
+
1
7
  module ActiveSupport
2
8
  module Testing
9
+ # Manages stubs for TimeHelpers
3
10
  class SimpleStubs # :nodoc:
4
11
  Stub = Struct.new(:object, :method_name, :original_method)
5
12
 
6
13
  def initialize
7
- @stubs = {}
14
+ @stubs = Concurrent::Map.new { |h, k| h[k] = {} }
8
15
  end
9
16
 
10
- def stub_object(object, method_name, return_value)
11
- key = [object.object_id, method_name]
12
-
13
- if stub = @stubs[key]
17
+ # Stubs object.method_name with the given block
18
+ # If the method is already stubbed, remove that stub
19
+ # so that removing this stub will restore the original implementation.
20
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
21
+ # target = Time.zone.local(2004, 11, 24, 1, 4, 44)
22
+ # simple_stubs.stub_object(Time, :now) { at(target.to_i) }
23
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
24
+ def stub_object(object, method_name, &block)
25
+ if stub = stubbing(object, method_name)
14
26
  unstub_object(stub)
15
27
  end
16
28
 
17
29
  new_name = "__simple_stub__#{method_name}"
18
30
 
19
- @stubs[key] = Stub.new(object, method_name, new_name)
31
+ @stubs[object.object_id][method_name] = Stub.new(object, method_name, new_name)
20
32
 
21
- object.singleton_class.send :alias_method, new_name, method_name
22
- object.define_singleton_method(method_name) { return_value }
33
+ object.singleton_class.alias_method new_name, method_name
34
+ object.define_singleton_method(method_name, &block)
23
35
  end
24
36
 
37
+ # Remove all object-method stubs held by this instance
25
38
  def unstub_all!
26
- @stubs.each_value do |stub|
27
- unstub_object(stub)
39
+ @stubs.each_value do |object_stubs|
40
+ object_stubs.each_value do |stub|
41
+ unstub_object(stub)
42
+ end
28
43
  end
29
- @stubs = {}
44
+ @stubs.clear
30
45
  end
31
46
 
32
- private
47
+ # Returns the Stub for object#method_name
48
+ # (nil if it is not stubbed)
49
+ def stubbing(object, method_name)
50
+ @stubs[object.object_id][method_name]
51
+ end
52
+
53
+ # Returns true if any stubs are set, false if there are none
54
+ def stubbed?
55
+ !@stubs.empty?
56
+ end
33
57
 
58
+ private
59
+ # Restores the original object.method described by the Stub
34
60
  def unstub_object(stub)
35
61
  singleton_class = stub.object.singleton_class
36
- singleton_class.send :undef_method, stub.method_name
37
- singleton_class.send :alias_method, stub.method_name, stub.original_method
38
- singleton_class.send :undef_method, stub.original_method
62
+ singleton_class.silence_redefinition_of_method stub.method_name
63
+ singleton_class.alias_method stub.method_name, stub.original_method
64
+ singleton_class.undef_method stub.original_method
39
65
  end
40
66
  end
41
67
 
42
68
  # Contains helpers that help you test passage of time.
43
69
  module TimeHelpers
70
+ def after_teardown
71
+ travel_back
72
+ super
73
+ end
74
+
44
75
  # Changes current time to the time in the future or in the past by a given time difference by
45
- # stubbing +Time.now+, +Date.today+, and +DateTime.now+.
76
+ # stubbing +Time.now+, +Date.today+, and +DateTime.now+. The stubs are automatically removed
77
+ # at the end of the test.
46
78
  #
47
79
  # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
48
80
  # travel 1.day
@@ -64,9 +96,10 @@ module ActiveSupport
64
96
 
65
97
  # Changes current time to the given time by stubbing +Time.now+,
66
98
  # +Date.today+, and +DateTime.now+ to return the time or date passed into this method.
99
+ # The stubs are automatically removed at the end of the test.
67
100
  #
68
101
  # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
69
- # travel_to Time.new(2004, 11, 24, 01, 04, 44)
102
+ # travel_to Time.zone.local(2004, 11, 24, 1, 4, 44)
70
103
  # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
71
104
  # Date.current # => Wed, 24 Nov 2004
72
105
  # DateTime.current # => Wed, 24 Nov 2004 01:04:44 -0500
@@ -88,20 +121,48 @@ module ActiveSupport
88
121
  # state at the end of the block:
89
122
  #
90
123
  # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
91
- # travel_to Time.new(2004, 11, 24, 01, 04, 44) do
124
+ # travel_to Time.zone.local(2004, 11, 24, 1, 4, 44) do
92
125
  # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
93
126
  # end
94
127
  # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
95
128
  def travel_to(date_or_time)
129
+ if block_given? && simple_stubs.stubbing(Time, :now)
130
+ travel_to_nested_block_call = <<~MSG
131
+
132
+ Calling `travel_to` with a block, when we have previously already made a call to `travel_to`, can lead to confusing time stubbing.
133
+
134
+ Instead of:
135
+
136
+ travel_to 2.days.from_now do
137
+ # 2 days from today
138
+ travel_to 3.days.from_now do
139
+ # 5 days from today
140
+ end
141
+ end
142
+
143
+ preferred way to achieve above is:
144
+
145
+ travel 2.days do
146
+ # 2 days from today
147
+ end
148
+
149
+ travel 5.days do
150
+ # 5 days from today
151
+ end
152
+
153
+ MSG
154
+ raise travel_to_nested_block_call
155
+ end
156
+
96
157
  if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime)
97
158
  now = date_or_time.midnight.to_time
98
159
  else
99
160
  now = date_or_time.to_time.change(usec: 0)
100
161
  end
101
162
 
102
- simple_stubs.stub_object(Time, :now, now)
103
- simple_stubs.stub_object(Date, :today, now.to_date)
104
- simple_stubs.stub_object(DateTime, :now, now.to_datetime)
163
+ simple_stubs.stub_object(Time, :now) { at(now.to_i) }
164
+ simple_stubs.stub_object(Date, :today) { jd(now.to_date.jd) }
165
+ simple_stubs.stub_object(DateTime, :now) { jd(now.to_date.jd, now.hour, now.min, now.sec, Rational(now.utc_offset, 86400)) }
105
166
 
106
167
  if block_given?
107
168
  begin
@@ -113,19 +174,59 @@ module ActiveSupport
113
174
  end
114
175
 
115
176
  # Returns the current time back to its original state, by removing the stubs added by
116
- # `travel` and `travel_to`.
177
+ # +travel+, +travel_to+, and +freeze_time+.
117
178
  #
118
179
  # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
119
- # travel_to Time.new(2004, 11, 24, 01, 04, 44)
180
+ #
181
+ # travel_to Time.zone.local(2004, 11, 24, 1, 4, 44)
120
182
  # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
183
+ #
121
184
  # travel_back
122
185
  # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
186
+ #
187
+ # This method also accepts a block, which brings the stubs back at the end of the block:
188
+ #
189
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
190
+ #
191
+ # travel_to Time.zone.local(2004, 11, 24, 1, 4, 44)
192
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
193
+ #
194
+ # travel_back do
195
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
196
+ # end
197
+ #
198
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
123
199
  def travel_back
200
+ stubbed_time = Time.current if block_given? && simple_stubs.stubbed?
201
+
124
202
  simple_stubs.unstub_all!
203
+ yield if block_given?
204
+ ensure
205
+ travel_to stubbed_time if stubbed_time
125
206
  end
207
+ alias_method :unfreeze_time, :travel_back
126
208
 
127
- private
209
+ # Calls +travel_to+ with +Time.now+.
210
+ #
211
+ # Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00
212
+ # freeze_time
213
+ # sleep(1)
214
+ # Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00
215
+ #
216
+ # This method also accepts a block, which will return the current time back to its original
217
+ # state at the end of the block:
218
+ #
219
+ # Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00
220
+ # freeze_time do
221
+ # sleep(1)
222
+ # User.create.created_at # => Sun, 09 Jul 2017 15:34:49 EST -05:00
223
+ # end
224
+ # Time.current # => Sun, 09 Jul 2017 15:34:50 EST -05:00
225
+ def freeze_time(&block)
226
+ travel_to Time.now, &block
227
+ end
128
228
 
229
+ private
129
230
  def simple_stubs
130
231
  @simple_stubs ||= SimpleStubs.new
131
232
  end
@@ -1,18 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveSupport
2
- autoload :Duration, 'active_support/duration'
3
- autoload :TimeWithZone, 'active_support/time_with_zone'
4
- autoload :TimeZone, 'active_support/values/time_zone'
4
+ autoload :Duration, "active_support/duration"
5
+ autoload :TimeWithZone, "active_support/time_with_zone"
6
+ autoload :TimeZone, "active_support/values/time_zone"
5
7
  end
6
8
 
7
- require 'date'
8
- require 'time'
9
+ require "date"
10
+ require "time"
9
11
 
10
- require 'active_support/core_ext/time'
11
- require 'active_support/core_ext/date'
12
- require 'active_support/core_ext/date_time'
12
+ require "active_support/core_ext/time"
13
+ require "active_support/core_ext/date"
14
+ require "active_support/core_ext/date_time"
13
15
 
14
- require 'active_support/core_ext/integer/time'
15
- require 'active_support/core_ext/numeric/time'
16
+ require "active_support/core_ext/integer/time"
17
+ require "active_support/core_ext/numeric/time"
16
18
 
17
- require 'active_support/core_ext/string/conversions'
18
- require 'active_support/core_ext/string/zones'
19
+ require "active_support/core_ext/string/conversions"
20
+ require "active_support/core_ext/string/zones"
@@ -1,7 +1,9 @@
1
- require 'active_support/duration'
2
- require 'active_support/values/time_zone'
3
- require 'active_support/core_ext/object/acts_like'
4
- require 'active_support/core_ext/date_and_time/compatibility'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/duration"
4
+ require "active_support/values/time_zone"
5
+ require "active_support/core_ext/object/acts_like"
6
+ require "active_support/core_ext/date_and_time/compatibility"
5
7
 
6
8
  module ActiveSupport
7
9
  # A Time-like class that can represent a time in any time zone. Necessary
@@ -13,37 +15,36 @@ module ActiveSupport
13
15
  # and +in_time_zone+ on Time and DateTime instances.
14
16
  #
15
17
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
16
- # Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
17
- # Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45 EST -05:00
18
- # Time.zone.at(1171139445) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
19
- # Time.zone.now # => Sun, 18 May 2008 13:07:55 EDT -04:00
20
- # Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45 EST -05:00
18
+ # Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45.000000000 EST -05:00
19
+ # Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45.000000000 EST -05:00
20
+ # Time.zone.at(1171139445) # => Sat, 10 Feb 2007 15:30:45.000000000 EST -05:00
21
+ # Time.zone.now # => Sun, 18 May 2008 13:07:55.754107581 EDT -04:00
22
+ # Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45.000000000 EST -05:00
21
23
  #
22
24
  # See Time and TimeZone for further documentation of these methods.
23
25
  #
24
26
  # TimeWithZone instances implement the same API as Ruby Time instances, so
25
27
  # that Time and TimeWithZone instances are interchangeable.
26
28
  #
27
- # t = Time.zone.now # => Sun, 18 May 2008 13:27:25 EDT -04:00
29
+ # t = Time.zone.now # => Sun, 18 May 2008 13:27:25.031505668 EDT -04:00
28
30
  # t.hour # => 13
29
31
  # t.dst? # => true
30
32
  # t.utc_offset # => -14400
31
33
  # t.zone # => "EDT"
32
34
  # t.to_s(:rfc822) # => "Sun, 18 May 2008 13:27:25 -0400"
33
- # t + 1.day # => Mon, 19 May 2008 13:27:25 EDT -04:00
34
- # t.beginning_of_year # => Tue, 01 Jan 2008 00:00:00 EST -05:00
35
+ # t + 1.day # => Mon, 19 May 2008 13:27:25.031505668 EDT -04:00
36
+ # t.beginning_of_year # => Tue, 01 Jan 2008 00:00:00.000000000 EST -05:00
35
37
  # t > Time.utc(1999) # => true
36
38
  # t.is_a?(Time) # => true
37
39
  # t.is_a?(ActiveSupport::TimeWithZone) # => true
38
40
  class TimeWithZone
39
-
40
41
  # Report class name as 'Time' to thwart type checking.
41
42
  def self.name
42
- 'Time'
43
+ "Time"
43
44
  end
44
45
 
45
- PRECISIONS = Hash.new { |h, n| h[n] = "%FT%T.%#{n}N".freeze }
46
- PRECISIONS[0] = '%FT%T'.freeze
46
+ PRECISIONS = Hash.new { |h, n| h[n] = "%FT%T.%#{n}N" }
47
+ PRECISIONS[0] = "%FT%T"
47
48
 
48
49
  include Comparable, DateAndTime::Compatibility
49
50
  attr_reader :time_zone
@@ -56,12 +57,12 @@ module ActiveSupport
56
57
 
57
58
  # Returns a <tt>Time</tt> instance that represents the time in +time_zone+.
58
59
  def time
59
- @time ||= period.to_local(@utc)
60
+ @time ||= incorporate_utc_offset(@utc, utc_offset)
60
61
  end
61
62
 
62
63
  # Returns a <tt>Time</tt> instance of the simultaneous time in the UTC timezone.
63
64
  def utc
64
- @utc ||= period.to_utc(@time)
65
+ @utc ||= incorporate_utc_offset(@time, -utc_offset)
65
66
  end
66
67
  alias_method :comparable_time, :utc
67
68
  alias_method :getgm, :utc
@@ -103,13 +104,13 @@ module ActiveSupport
103
104
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
104
105
  # Time.zone.now.utc? # => false
105
106
  def utc?
106
- period.offset.abbreviation == :UTC || period.offset.abbreviation == :UCT
107
+ zone == "UTC" || zone == "UCT"
107
108
  end
108
109
  alias_method :gmt?, :utc?
109
110
 
110
111
  # Returns the offset from current time to UTC time in seconds.
111
112
  def utc_offset
112
- period.utc_total_offset
113
+ period.observed_utc_offset
113
114
  end
114
115
  alias_method :gmt_offset, :utc_offset
115
116
  alias_method :gmtoff, :utc_offset
@@ -131,14 +132,14 @@ module ActiveSupport
131
132
  # Time.zone = 'Eastern Time (US & Canada)' # => "Eastern Time (US & Canada)"
132
133
  # Time.zone.now.zone # => "EST"
133
134
  def zone
134
- period.zone_identifier.to_s
135
+ period.abbreviation
135
136
  end
136
137
 
137
- # Returns a string of the object's date, time, zone and offset from UTC.
138
+ # Returns a string of the object's date, time, zone, and offset from UTC.
138
139
  #
139
- # Time.zone.now.inspect # => "Thu, 04 Dec 2014 11:00:25 EST -05:00"
140
+ # Time.zone.now.inspect # => "Thu, 04 Dec 2014 11:00:25.624541392 EST -05:00"
140
141
  def inspect
141
- "#{time.strftime('%a, %d %b %Y %H:%M:%S')} #{zone} #{formatted_offset}"
142
+ "#{time.strftime('%a, %d %b %Y %H:%M:%S.%9N')} #{zone} #{formatted_offset}"
142
143
  end
143
144
 
144
145
  # Returns a string of the object's date and time in the ISO 8601 standard
@@ -146,9 +147,10 @@ module ActiveSupport
146
147
  #
147
148
  # Time.zone.now.xmlschema # => "2014-12-04T11:02:37-05:00"
148
149
  def xmlschema(fraction_digits = 0)
149
- "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z'.freeze)}"
150
+ "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z')}"
150
151
  end
151
152
  alias_method :iso8601, :xmlschema
153
+ alias_method :rfc3339, :xmlschema
152
154
 
153
155
  # Coerces time to a string for JSON encoding. The default format is ISO 8601.
154
156
  # You can get %Y/%m/%d %H:%M:%S +offset style by setting
@@ -171,12 +173,12 @@ module ActiveSupport
171
173
  end
172
174
 
173
175
  def init_with(coder) #:nodoc:
174
- initialize(coder['utc'], coder['zone'], coder['time'])
176
+ initialize(coder["utc"], coder["zone"], coder["time"])
175
177
  end
176
178
 
177
179
  def encode_with(coder) #:nodoc:
178
- coder.tag = '!ruby/object:ActiveSupport::TimeWithZone'
179
- coder.map = { 'utc' => utc, 'zone' => time_zone, 'time' => time }
180
+ coder.tag = "!ruby/object:ActiveSupport::TimeWithZone"
181
+ coder.map = { "utc" => utc, "zone" => time_zone, "time" => time }
180
182
  end
181
183
 
182
184
  # Returns a string of the object's date and time in the format used by
@@ -223,6 +225,8 @@ module ActiveSupport
223
225
  def <=>(other)
224
226
  utc <=> other
225
227
  end
228
+ alias_method :before?, :<
229
+ alias_method :after?, :>
226
230
 
227
231
  # Returns true if the current object's time is within the specified
228
232
  # +min+ and +max+ time.
@@ -241,6 +245,20 @@ module ActiveSupport
241
245
  time.today?
242
246
  end
243
247
 
248
+ # Returns true if the current object's time falls within
249
+ # the next day (tomorrow).
250
+ def tomorrow?
251
+ time.tomorrow?
252
+ end
253
+ alias :next_day? :tomorrow?
254
+
255
+ # Returns true if the current object's time falls within
256
+ # the previous day (yesterday).
257
+ def yesterday?
258
+ time.yesterday?
259
+ end
260
+ alias :prev_day? :yesterday?
261
+
244
262
  # Returns true if the current object's time is in the future.
245
263
  def future?
246
264
  utc.future?
@@ -259,8 +277,8 @@ module ActiveSupport
259
277
  # value as a new TimeWithZone object.
260
278
  #
261
279
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
262
- # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
263
- # now + 1000 # => Sun, 02 Nov 2014 01:43:08 EDT -04:00
280
+ # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28.725182881 EDT -04:00
281
+ # now + 1000 # => Sun, 02 Nov 2014 01:43:08.725182881 EDT -04:00
264
282
  #
265
283
  # If we're adding a Duration of variable length (i.e., years, months, days),
266
284
  # move forward from #time, otherwise move forward from #utc, for accuracy
@@ -269,8 +287,8 @@ module ActiveSupport
269
287
  # For instance, a time + 24.hours will advance exactly 24 hours, while a
270
288
  # time + 1.day will advance 23-25 hours, depending on the day.
271
289
  #
272
- # now + 24.hours # => Mon, 03 Nov 2014 00:26:28 EST -05:00
273
- # now + 1.day # => Mon, 03 Nov 2014 01:26:28 EST -05:00
290
+ # now + 24.hours # => Mon, 03 Nov 2014 00:26:28.725182881 EST -05:00
291
+ # now + 1.day # => Mon, 03 Nov 2014 01:26:28.725182881 EST -05:00
274
292
  def +(other)
275
293
  if duration_of_variable_length?(other)
276
294
  method_missing(:+, other)
@@ -280,13 +298,16 @@ module ActiveSupport
280
298
  end
281
299
  end
282
300
  alias_method :since, :+
301
+ alias_method :in, :+
283
302
 
284
- # Returns a new TimeWithZone object that represents the difference between
285
- # the current object's time and the +other+ time.
303
+ # Subtracts an interval of time and returns a new TimeWithZone object unless
304
+ # the other value `acts_like?` time. Then it will return a Float of the difference
305
+ # between the two times that represents the difference between the current
306
+ # object's time and the +other+ time.
286
307
  #
287
308
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
288
- # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28 EST -05:00
289
- # now - 1000 # => Mon, 03 Nov 2014 00:09:48 EST -05:00
309
+ # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28.725182881 EST -05:00
310
+ # now - 1000 # => Mon, 03 Nov 2014 00:09:48.725182881 EST -05:00
290
311
  #
291
312
  # If subtracting a Duration of variable length (i.e., years, months, days),
292
313
  # move backward from #time, otherwise move backward from #utc, for accuracy
@@ -295,8 +316,14 @@ module ActiveSupport
295
316
  # For instance, a time - 24.hours will go subtract exactly 24 hours, while a
296
317
  # time - 1.day will subtract 23-25 hours, depending on the day.
297
318
  #
298
- # now - 24.hours # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
299
- # now - 1.day # => Sun, 02 Nov 2014 00:26:28 EDT -04:00
319
+ # now - 24.hours # => Sun, 02 Nov 2014 01:26:28.725182881 EDT -04:00
320
+ # now - 1.day # => Sun, 02 Nov 2014 00:26:28.725182881 EDT -04:00
321
+ #
322
+ # If both the TimeWithZone object and the other value act like Time, a Float
323
+ # will be returned.
324
+ #
325
+ # Time.zone.now - 1.day.ago # => 86399.999967
326
+ #
300
327
  def -(other)
301
328
  if other.acts_like?(:time)
302
329
  to_time - other.to_time
@@ -312,8 +339,8 @@ module ActiveSupport
312
339
  # the result as a new TimeWithZone object.
313
340
  #
314
341
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
315
- # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28 EST -05:00
316
- # now.ago(1000) # => Mon, 03 Nov 2014 00:09:48 EST -05:00
342
+ # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28.725182881 EST -05:00
343
+ # now.ago(1000) # => Mon, 03 Nov 2014 00:09:48.725182881 EST -05:00
317
344
  #
318
345
  # If we're subtracting a Duration of variable length (i.e., years, months,
319
346
  # days), move backward from #time, otherwise move backward from #utc, for
@@ -323,12 +350,48 @@ module ActiveSupport
323
350
  # while <tt>time.ago(1.day)</tt> will move back 23-25 hours, depending on
324
351
  # the day.
325
352
  #
326
- # now.ago(24.hours) # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
327
- # now.ago(1.day) # => Sun, 02 Nov 2014 00:26:28 EDT -04:00
353
+ # now.ago(24.hours) # => Sun, 02 Nov 2014 01:26:28.725182881 EDT -04:00
354
+ # now.ago(1.day) # => Sun, 02 Nov 2014 00:26:28.725182881 EDT -04:00
328
355
  def ago(other)
329
356
  since(-other)
330
357
  end
331
358
 
359
+ # Returns a new +ActiveSupport::TimeWithZone+ where one or more of the elements have
360
+ # been changed according to the +options+ parameter. The time options (<tt>:hour</tt>,
361
+ # <tt>:min</tt>, <tt>:sec</tt>, <tt>:usec</tt>, <tt>:nsec</tt>) reset cascadingly,
362
+ # so if only the hour is passed, then minute, sec, usec and nsec is set to 0. If the
363
+ # hour and minute is passed, then sec, usec and nsec is set to 0. The +options+
364
+ # parameter takes a hash with any of these keys: <tt>:year</tt>, <tt>:month</tt>,
365
+ # <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>, <tt>:sec</tt>, <tt>:usec</tt>,
366
+ # <tt>:nsec</tt>, <tt>:offset</tt>, <tt>:zone</tt>. Pass either <tt>:usec</tt>
367
+ # or <tt>:nsec</tt>, not both. Similarly, pass either <tt>:zone</tt> or
368
+ # <tt>:offset</tt>, not both.
369
+ #
370
+ # t = Time.zone.now # => Fri, 14 Apr 2017 11:45:15.116992711 EST -05:00
371
+ # t.change(year: 2020) # => Tue, 14 Apr 2020 11:45:15.116992711 EST -05:00
372
+ # t.change(hour: 12) # => Fri, 14 Apr 2017 12:00:00.116992711 EST -05:00
373
+ # t.change(min: 30) # => Fri, 14 Apr 2017 11:30:00.116992711 EST -05:00
374
+ # t.change(offset: "-10:00") # => Fri, 14 Apr 2017 11:45:15.116992711 HST -10:00
375
+ # t.change(zone: "Hawaii") # => Fri, 14 Apr 2017 11:45:15.116992711 HST -10:00
376
+ def change(options)
377
+ if options[:zone] && options[:offset]
378
+ raise ArgumentError, "Can't change both :offset and :zone at the same time: #{options.inspect}"
379
+ end
380
+
381
+ new_time = time.change(options)
382
+
383
+ if options[:zone]
384
+ new_zone = ::Time.find_zone(options[:zone])
385
+ elsif options[:offset]
386
+ new_zone = ::Time.find_zone(new_time.utc_offset)
387
+ end
388
+
389
+ new_zone ||= time_zone
390
+ periods = new_zone.periods_for_local(new_time)
391
+
392
+ self.class.new(nil, new_zone, new_time, periods.include?(period) ? period : nil)
393
+ end
394
+
332
395
  # Uses Date to provide precise Time calculations for years, months, and days
333
396
  # according to the proleptic Gregorian calendar. The result is returned as a
334
397
  # new TimeWithZone object.
@@ -342,14 +405,14 @@ module ActiveSupport
342
405
  # accuracy when moving across DST boundaries.
343
406
  #
344
407
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
345
- # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
346
- # now.advance(seconds: 1) # => Sun, 02 Nov 2014 01:26:29 EDT -04:00
347
- # now.advance(minutes: 1) # => Sun, 02 Nov 2014 01:27:28 EDT -04:00
348
- # now.advance(hours: 1) # => Sun, 02 Nov 2014 01:26:28 EST -05:00
349
- # now.advance(days: 1) # => Mon, 03 Nov 2014 01:26:28 EST -05:00
350
- # now.advance(weeks: 1) # => Sun, 09 Nov 2014 01:26:28 EST -05:00
351
- # now.advance(months: 1) # => Tue, 02 Dec 2014 01:26:28 EST -05:00
352
- # now.advance(years: 1) # => Mon, 02 Nov 2015 01:26:28 EST -05:00
408
+ # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28.558049687 EDT -04:00
409
+ # now.advance(seconds: 1) # => Sun, 02 Nov 2014 01:26:29.558049687 EDT -04:00
410
+ # now.advance(minutes: 1) # => Sun, 02 Nov 2014 01:27:28.558049687 EDT -04:00
411
+ # now.advance(hours: 1) # => Sun, 02 Nov 2014 01:26:28.558049687 EST -05:00
412
+ # now.advance(days: 1) # => Mon, 03 Nov 2014 01:26:28.558049687 EST -05:00
413
+ # now.advance(weeks: 1) # => Sun, 09 Nov 2014 01:26:28.558049687 EST -05:00
414
+ # now.advance(months: 1) # => Tue, 02 Dec 2014 01:26:28.558049687 EST -05:00
415
+ # now.advance(years: 1) # => Mon, 02 Nov 2015 01:26:28.558049687 EST -05:00
353
416
  def advance(options)
354
417
  # If we're advancing a value of variable length (i.e., years, weeks, months, days), advance from #time,
355
418
  # otherwise advance from #utc, for accuracy when moving across DST boundaries
@@ -371,7 +434,7 @@ module ActiveSupport
371
434
  # Returns Array of parts of Time in sequence of
372
435
  # [seconds, minutes, hours, day, month, year, weekday, yearday, dst?, zone].
373
436
  #
374
- # now = Time.zone.now # => Tue, 18 Aug 2015 02:29:27 UTC +00:00
437
+ # now = Time.zone.now # => Tue, 18 Aug 2015 02:29:27.485278555 UTC +00:00
375
438
  # now.to_a # => [27, 29, 2, 18, 8, 2015, 2, 230, false, "UTC"]
376
439
  def to_a
377
440
  [time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone]
@@ -407,7 +470,18 @@ module ActiveSupport
407
470
  # Time.zone.now.to_datetime # => Tue, 18 Aug 2015 02:32:20 +0000
408
471
  # Time.current.in_time_zone('Hawaii').to_datetime # => Mon, 17 Aug 2015 16:32:20 -1000
409
472
  def to_datetime
410
- utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
473
+ @to_datetime ||= utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
474
+ end
475
+
476
+ # Returns an instance of +Time+, either with the same UTC offset
477
+ # as +self+ or in the local system timezone depending on the setting
478
+ # of +ActiveSupport.to_time_preserves_timezone+.
479
+ def to_time
480
+ if preserve_timezone
481
+ @to_time_with_instance_offset ||= getlocal(utc_offset)
482
+ else
483
+ @to_time_with_system_offset ||= getlocal
484
+ end
411
485
  end
412
486
 
413
487
  # So that +self+ <tt>acts_like?(:time)</tt>.
@@ -427,7 +501,8 @@ module ActiveSupport
427
501
  end
428
502
 
429
503
  def freeze
430
- period; utc; time # preload instance variables before freezing
504
+ # preload instance variables before freezing
505
+ period; utc; time; to_datetime; to_time
431
506
  super
432
507
  end
433
508
 
@@ -459,10 +534,20 @@ module ActiveSupport
459
534
  def method_missing(sym, *args, &block)
460
535
  wrap_with_time_zone time.__send__(sym, *args, &block)
461
536
  rescue NoMethodError => e
462
- raise e, e.message.sub(time.inspect, self.inspect), e.backtrace
537
+ raise e, e.message.sub(time.inspect, inspect), e.backtrace
463
538
  end
464
539
 
465
540
  private
541
+ SECONDS_PER_DAY = 86400
542
+
543
+ def incorporate_utc_offset(time, offset)
544
+ if time.kind_of?(Date)
545
+ time + Rational(offset, SECONDS_PER_DAY)
546
+ else
547
+ time + offset
548
+ end
549
+ end
550
+
466
551
  def get_period_and_ensure_valid_local_time(period)
467
552
  # we don't want a Time.local instance enforcing its own DST rules as well,
468
553
  # so transfer time values to a utc constructor if necessary
@@ -477,11 +562,13 @@ module ActiveSupport
477
562
  end
478
563
 
479
564
  def transfer_time_values_to_utc_constructor(time)
565
+ # avoid creating another Time object if possible
566
+ return time if time.instance_of?(::Time) && time.utc?
480
567
  ::Time.utc(time.year, time.month, time.day, time.hour, time.min, time.sec + time.subsec)
481
568
  end
482
569
 
483
570
  def duration_of_variable_length?(obj)
484
- ActiveSupport::Duration === obj && obj.parts.any? {|p| [:years, :months, :days].include?(p[0]) }
571
+ ActiveSupport::Duration === obj && obj.parts.any? { |p| [:years, :months, :weeks, :days].include?(p[0]) }
485
572
  end
486
573
 
487
574
  def wrap_with_time_zone(time)