activesupport 4.0.12 → 7.0.2.4

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 (295) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +249 -501
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -5
  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 +48 -0
  8. data/lib/active_support/backtrace_cleaner.rb +41 -13
  9. data/lib/active_support/benchmarkable.rb +7 -15
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache/file_store.rb +96 -74
  12. data/lib/active_support/cache/mem_cache_store.rb +211 -103
  13. data/lib/active_support/cache/memory_store.rb +90 -58
  14. data/lib/active_support/cache/null_store.rb +19 -7
  15. data/lib/active_support/cache/redis_cache_store.rb +468 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +86 -83
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  18. data/lib/active_support/cache.rb +580 -241
  19. data/lib/active_support/callbacks.rb +812 -425
  20. data/lib/active_support/code_generator.rb +65 -0
  21. data/lib/active_support/concern.rb +103 -14
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +33 -0
  23. data/lib/active_support/concurrency/share_lock.rb +226 -0
  24. data/lib/active_support/configurable.rb +21 -19
  25. data/lib/active_support/configuration_file.rb +51 -0
  26. data/lib/active_support/core_ext/array/access.rb +47 -1
  27. data/lib/active_support/core_ext/array/conversions.rb +35 -44
  28. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  29. data/lib/active_support/core_ext/array/extract.rb +21 -0
  30. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  31. data/lib/active_support/core_ext/array/grouping.rb +26 -16
  32. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  33. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  34. data/lib/active_support/core_ext/array.rb +10 -7
  35. data/lib/active_support/core_ext/benchmark.rb +5 -3
  36. data/lib/active_support/core_ext/big_decimal/conversions.rb +9 -26
  37. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  38. data/lib/active_support/core_ext/class/attribute.rb +52 -49
  39. data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
  40. data/lib/active_support/core_ext/class/subclasses.rb +25 -26
  41. data/lib/active_support/core_ext/class.rb +4 -4
  42. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  43. data/lib/active_support/core_ext/date/blank.rb +14 -0
  44. data/lib/active_support/core_ext/date/calculations.rb +31 -18
  45. data/lib/active_support/core_ext/date/conversions.rb +43 -32
  46. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  47. data/lib/active_support/core_ext/date/zones.rb +5 -34
  48. data/lib/active_support/core_ext/date.rb +7 -4
  49. data/lib/active_support/core_ext/date_and_time/calculations.rb +198 -66
  50. data/lib/active_support/core_ext/date_and_time/compatibility.rb +31 -0
  51. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  52. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  53. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  54. data/lib/active_support/core_ext/date_time/calculations.rb +79 -38
  55. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  56. data/lib/active_support/core_ext/date_time/conversions.rb +31 -26
  57. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  58. data/lib/active_support/core_ext/date_time.rb +8 -4
  59. data/lib/active_support/core_ext/digest/uuid.rb +79 -0
  60. data/lib/active_support/core_ext/digest.rb +3 -0
  61. data/lib/active_support/core_ext/enumerable.rb +249 -17
  62. data/lib/active_support/core_ext/file/atomic.rb +41 -32
  63. data/lib/active_support/core_ext/file.rb +3 -1
  64. data/lib/active_support/core_ext/hash/conversions.rb +71 -49
  65. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  66. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  67. data/lib/active_support/core_ext/hash/except.rb +14 -5
  68. data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
  69. data/lib/active_support/core_ext/hash/keys.rb +39 -56
  70. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  71. data/lib/active_support/core_ext/hash/slice.rb +8 -23
  72. data/lib/active_support/core_ext/hash.rb +10 -8
  73. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  74. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  75. data/lib/active_support/core_ext/integer/time.rb +11 -33
  76. data/lib/active_support/core_ext/integer.rb +5 -3
  77. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  78. data/lib/active_support/core_ext/kernel/reporting.rb +9 -78
  79. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  80. data/lib/active_support/core_ext/kernel.rb +5 -4
  81. data/lib/active_support/core_ext/load_error.rb +5 -21
  82. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  83. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  84. data/lib/active_support/core_ext/module/attr_internal.rb +8 -8
  85. data/lib/active_support/core_ext/module/attribute_accessors.rb +186 -44
  86. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +157 -0
  87. data/lib/active_support/core_ext/module/concerning.rb +140 -0
  88. data/lib/active_support/core_ext/module/delegation.rb +172 -45
  89. data/lib/active_support/core_ext/module/deprecation.rb +3 -3
  90. data/lib/active_support/core_ext/module/introspection.rb +23 -38
  91. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  93. data/lib/active_support/core_ext/module.rb +13 -10
  94. data/lib/active_support/core_ext/name_error.rb +45 -4
  95. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  96. data/lib/active_support/core_ext/numeric/conversions.rb +135 -127
  97. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  98. data/lib/active_support/core_ext/numeric/time.rb +37 -50
  99. data/lib/active_support/core_ext/numeric.rb +6 -3
  100. data/lib/active_support/core_ext/object/acts_like.rb +41 -6
  101. data/lib/active_support/core_ext/object/blank.rb +70 -20
  102. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  103. data/lib/active_support/core_ext/object/deep_dup.rb +19 -10
  104. data/lib/active_support/core_ext/object/duplicable.rb +17 -47
  105. data/lib/active_support/core_ext/object/inclusion.rb +18 -15
  106. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  107. data/lib/active_support/core_ext/object/json.rb +244 -0
  108. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  109. data/lib/active_support/core_ext/object/to_query.rb +21 -8
  110. data/lib/active_support/core_ext/object/try.rb +106 -26
  111. data/lib/active_support/core_ext/object/with_options.rb +64 -5
  112. data/lib/active_support/core_ext/object.rb +14 -12
  113. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  114. data/lib/active_support/core_ext/pathname.rb +3 -0
  115. data/lib/active_support/core_ext/range/compare_range.rb +57 -0
  116. data/lib/active_support/core_ext/range/conversions.rb +37 -15
  117. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  118. data/lib/active_support/core_ext/range/each.rb +18 -17
  119. data/lib/active_support/core_ext/range/include_time_with_zone.rb +7 -0
  120. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  121. data/lib/active_support/core_ext/range.rb +7 -4
  122. data/lib/active_support/core_ext/regexp.rb +10 -1
  123. data/lib/active_support/core_ext/securerandom.rb +45 -0
  124. data/lib/active_support/core_ext/string/access.rb +42 -51
  125. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  126. data/lib/active_support/core_ext/string/conversions.rb +18 -13
  127. data/lib/active_support/core_ext/string/exclude.rb +5 -3
  128. data/lib/active_support/core_ext/string/filters.rb +97 -7
  129. data/lib/active_support/core_ext/string/indent.rb +6 -4
  130. data/lib/active_support/core_ext/string/inflections.rb +106 -25
  131. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  132. data/lib/active_support/core_ext/string/multibyte.rb +18 -9
  133. data/lib/active_support/core_ext/string/output_safety.rb +227 -54
  134. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  135. data/lib/active_support/core_ext/string/strip.rb +6 -5
  136. data/lib/active_support/core_ext/string/zones.rb +4 -1
  137. data/lib/active_support/core_ext/string.rb +15 -13
  138. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  139. data/lib/active_support/core_ext/symbol.rb +3 -0
  140. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  141. data/lib/active_support/core_ext/time/calculations.rb +178 -116
  142. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  143. data/lib/active_support/core_ext/time/conversions.rb +37 -25
  144. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  145. data/lib/active_support/core_ext/time/zones.rb +44 -42
  146. data/lib/active_support/core_ext/time.rb +8 -5
  147. data/lib/active_support/core_ext/uri.rb +4 -25
  148. data/lib/active_support/core_ext.rb +4 -2
  149. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  150. data/lib/active_support/current_attributes.rb +226 -0
  151. data/lib/active_support/dependencies/autoload.rb +3 -1
  152. data/lib/active_support/dependencies/interlock.rb +49 -0
  153. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  154. data/lib/active_support/dependencies.rb +71 -696
  155. data/lib/active_support/deprecation/behaviors.rb +65 -16
  156. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  157. data/lib/active_support/deprecation/disallowed.rb +56 -0
  158. data/lib/active_support/deprecation/instance_delegator.rb +16 -2
  159. data/lib/active_support/deprecation/method_wrappers.rb +62 -21
  160. data/lib/active_support/deprecation/proxy_wrappers.rb +82 -31
  161. data/lib/active_support/deprecation/reporting.rb +81 -18
  162. data/lib/active_support/deprecation.rb +19 -11
  163. data/lib/active_support/descendants_tracker.rb +192 -34
  164. data/lib/active_support/digest.rb +22 -0
  165. data/lib/active_support/duration/iso8601_parser.rb +123 -0
  166. data/lib/active_support/duration/iso8601_serializer.rb +67 -0
  167. data/lib/active_support/duration.rb +437 -39
  168. data/lib/active_support/encrypted_configuration.rb +56 -0
  169. data/lib/active_support/encrypted_file.rb +117 -0
  170. data/lib/active_support/environment_inquirer.rb +20 -0
  171. data/lib/active_support/error_reporter.rb +117 -0
  172. data/lib/active_support/evented_file_update_checker.rb +170 -0
  173. data/lib/active_support/execution_context/test_helper.rb +13 -0
  174. data/lib/active_support/execution_context.rb +53 -0
  175. data/lib/active_support/execution_wrapper.rb +151 -0
  176. data/lib/active_support/executor/test_helper.rb +7 -0
  177. data/lib/active_support/executor.rb +8 -0
  178. data/lib/active_support/file_update_checker.rb +62 -37
  179. data/lib/active_support/fork_tracker.rb +71 -0
  180. data/lib/active_support/gem_version.rb +17 -0
  181. data/lib/active_support/gzip.rb +7 -5
  182. data/lib/active_support/hash_with_indifferent_access.rb +207 -54
  183. data/lib/active_support/html_safe_translation.rb +43 -0
  184. data/lib/active_support/i18n.rb +10 -6
  185. data/lib/active_support/i18n_railtie.rb +48 -19
  186. data/lib/active_support/inflections.rb +19 -12
  187. data/lib/active_support/inflector/inflections.rb +97 -37
  188. data/lib/active_support/inflector/methods.rb +192 -157
  189. data/lib/active_support/inflector/transliterate.rb +83 -33
  190. data/lib/active_support/inflector.rb +7 -5
  191. data/lib/active_support/isolated_execution_state.rb +64 -0
  192. data/lib/active_support/json/decoding.rb +37 -42
  193. data/lib/active_support/json/encoding.rb +93 -293
  194. data/lib/active_support/json.rb +4 -2
  195. data/lib/active_support/key_generator.rb +30 -47
  196. data/lib/active_support/lazy_load_hooks.rb +54 -21
  197. data/lib/active_support/locale/en.rb +33 -0
  198. data/lib/active_support/locale/en.yml +10 -4
  199. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  200. data/lib/active_support/log_subscriber.rb +61 -18
  201. data/lib/active_support/logger.rb +40 -4
  202. data/lib/active_support/logger_silence.rb +17 -20
  203. data/lib/active_support/logger_thread_safe_level.rb +69 -0
  204. data/lib/active_support/message_encryptor.rb +178 -55
  205. data/lib/active_support/message_verifier.rb +195 -26
  206. data/lib/active_support/messages/metadata.rb +80 -0
  207. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  208. data/lib/active_support/messages/rotator.rb +57 -0
  209. data/lib/active_support/multibyte/chars.rb +45 -92
  210. data/lib/active_support/multibyte/unicode.rb +44 -377
  211. data/lib/active_support/multibyte.rb +5 -3
  212. data/lib/active_support/notifications/fanout.rb +177 -44
  213. data/lib/active_support/notifications/instrumenter.rb +117 -17
  214. data/lib/active_support/notifications.rb +106 -39
  215. data/lib/active_support/number_helper/number_converter.rb +181 -0
  216. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  217. data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
  218. data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
  219. data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
  220. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  221. data/lib/active_support/number_helper/number_to_phone_converter.rb +59 -0
  222. data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
  223. data/lib/active_support/number_helper/rounding_helper.rb +46 -0
  224. data/lib/active_support/number_helper.rb +152 -394
  225. data/lib/active_support/option_merger.rb +18 -5
  226. data/lib/active_support/ordered_hash.rb +8 -6
  227. data/lib/active_support/ordered_options.rb +43 -7
  228. data/lib/active_support/parameter_filter.rb +138 -0
  229. data/lib/active_support/per_thread_registry.rb +24 -11
  230. data/lib/active_support/proxy_object.rb +2 -0
  231. data/lib/active_support/rails.rb +10 -11
  232. data/lib/active_support/railtie.rb +118 -12
  233. data/lib/active_support/reloader.rb +130 -0
  234. data/lib/active_support/rescuable.rb +112 -57
  235. data/lib/active_support/ruby_features.rb +7 -0
  236. data/lib/active_support/secure_compare_rotator.rb +51 -0
  237. data/lib/active_support/security_utils.rb +38 -0
  238. data/lib/active_support/string_inquirer.rb +11 -4
  239. data/lib/active_support/subscriber.rb +109 -39
  240. data/lib/active_support/tagged_logging.rb +54 -17
  241. data/lib/active_support/test_case.rb +121 -37
  242. data/lib/active_support/testing/assertions.rb +177 -39
  243. data/lib/active_support/testing/autorun.rb +5 -3
  244. data/lib/active_support/testing/constant_lookup.rb +3 -6
  245. data/lib/active_support/testing/declarative.rb +10 -22
  246. data/lib/active_support/testing/deprecation.rb +65 -11
  247. data/lib/active_support/testing/file_fixtures.rb +38 -0
  248. data/lib/active_support/testing/isolation.rb +56 -87
  249. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  250. data/lib/active_support/testing/parallelization/server.rb +82 -0
  251. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  252. data/lib/active_support/testing/parallelization.rb +55 -0
  253. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  254. data/lib/active_support/testing/setup_and_teardown.rb +30 -10
  255. data/lib/active_support/testing/stream.rb +41 -0
  256. data/lib/active_support/testing/tagged_logging.rb +6 -4
  257. data/lib/active_support/testing/time_helpers.rb +246 -0
  258. data/lib/active_support/time.rb +13 -13
  259. data/lib/active_support/time_with_zone.rb +315 -90
  260. data/lib/active_support/values/time_zone.rb +306 -135
  261. data/lib/active_support/version.rb +6 -7
  262. data/lib/active_support/xml_mini/jdom.rb +117 -115
  263. data/lib/active_support/xml_mini/libxml.rb +22 -21
  264. data/lib/active_support/xml_mini/libxmlsax.rb +17 -19
  265. data/lib/active_support/xml_mini/nokogiri.rb +19 -19
  266. data/lib/active_support/xml_mini/nokogirisax.rb +16 -17
  267. data/lib/active_support/xml_mini/rexml.rb +25 -17
  268. data/lib/active_support/xml_mini.rb +67 -56
  269. data/lib/active_support.rb +58 -3
  270. metadata +125 -66
  271. data/lib/active_support/basic_object.rb +0 -11
  272. data/lib/active_support/buffered_logger.rb +0 -21
  273. data/lib/active_support/concurrency/latch.rb +0 -27
  274. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  275. data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
  276. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -40
  277. data/lib/active_support/core_ext/date_time/zones.rb +0 -24
  278. data/lib/active_support/core_ext/hash/diff.rb +0 -14
  279. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  280. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  281. data/lib/active_support/core_ext/logger.rb +0 -67
  282. data/lib/active_support/core_ext/marshal.rb +0 -21
  283. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  284. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  285. data/lib/active_support/core_ext/object/to_json.rb +0 -27
  286. data/lib/active_support/core_ext/proc.rb +0 -17
  287. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  288. data/lib/active_support/core_ext/string/encoding.rb +0 -8
  289. data/lib/active_support/core_ext/struct.rb +0 -6
  290. data/lib/active_support/core_ext/thread.rb +0 -79
  291. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  292. data/lib/active_support/file_watcher.rb +0 -36
  293. data/lib/active_support/json/variable.rb +0 -18
  294. data/lib/active_support/testing/pending.rb +0 -14
  295. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,5 +1,11 @@
1
- require 'active_support/values/time_zone'
2
- require 'active_support/core_ext/object/acts_like'
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ require "active_support/duration"
6
+ require "active_support/values/time_zone"
7
+ require "active_support/core_ext/object/acts_like"
8
+ require "active_support/core_ext/date_and_time/compatibility"
3
9
 
4
10
  module ActiveSupport
5
11
  # A Time-like class that can represent a time in any time zone. Necessary
@@ -11,51 +17,61 @@ module ActiveSupport
11
17
  # and +in_time_zone+ on Time and DateTime instances.
12
18
  #
13
19
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
14
- # Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
15
- # Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45 EST -05:00
16
- # Time.zone.at(1170361845) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
17
- # Time.zone.now # => Sun, 18 May 2008 13:07:55 EDT -04:00
18
- # Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45 EST -05:00
20
+ # Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45.000000000 EST -05:00
21
+ # Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45.000000000 EST -05:00
22
+ # Time.zone.at(1171139445) # => Sat, 10 Feb 2007 15:30:45.000000000 EST -05:00
23
+ # Time.zone.now # => Sun, 18 May 2008 13:07:55.754107581 EDT -04:00
24
+ # Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45.000000000 EST -05:00
19
25
  #
20
26
  # See Time and TimeZone for further documentation of these methods.
21
27
  #
22
28
  # TimeWithZone instances implement the same API as Ruby Time instances, so
23
29
  # that Time and TimeWithZone instances are interchangeable.
24
30
  #
25
- # t = Time.zone.now # => Sun, 18 May 2008 13:27:25 EDT -04:00
31
+ # t = Time.zone.now # => Sun, 18 May 2008 13:27:25.031505668 EDT -04:00
26
32
  # t.hour # => 13
27
33
  # t.dst? # => true
28
34
  # t.utc_offset # => -14400
29
35
  # t.zone # => "EDT"
30
- # t.to_s(:rfc822) # => "Sun, 18 May 2008 13:27:25 -0400"
31
- # t + 1.day # => Mon, 19 May 2008 13:27:25 EDT -04:00
32
- # t.beginning_of_year # => Tue, 01 Jan 2008 00:00:00 EST -05:00
36
+ # t.to_fs(:rfc822) # => "Sun, 18 May 2008 13:27:25 -0400"
37
+ # t + 1.day # => Mon, 19 May 2008 13:27:25.031505668 EDT -04:00
38
+ # t.beginning_of_year # => Tue, 01 Jan 2008 00:00:00.000000000 EST -05:00
33
39
  # t > Time.utc(1999) # => true
34
40
  # t.is_a?(Time) # => true
35
41
  # t.is_a?(ActiveSupport::TimeWithZone) # => true
36
42
  class TimeWithZone
37
-
38
43
  # Report class name as 'Time' to thwart type checking.
39
44
  def self.name
40
- 'Time'
45
+ ActiveSupport::Deprecation.warn(<<~EOM)
46
+ ActiveSupport::TimeWithZone.name has been deprecated and
47
+ from Rails 7.1 will use the default Ruby implementation.
48
+ You can set `config.active_support.remove_deprecated_time_with_zone_name = true`
49
+ to enable the new behavior now.
50
+ EOM
51
+
52
+ "Time"
41
53
  end
42
54
 
43
- include Comparable
55
+ PRECISIONS = Hash.new { |h, n| h[n] = "%FT%T.%#{n}N" }
56
+ PRECISIONS[0] = "%FT%T"
57
+
58
+ include Comparable, DateAndTime::Compatibility
44
59
  attr_reader :time_zone
45
60
 
46
61
  def initialize(utc_time, time_zone, local_time = nil, period = nil)
47
- @utc, @time_zone, @time = utc_time, time_zone, local_time
48
- @period = @utc ? period : get_period_and_ensure_valid_local_time
62
+ @utc = utc_time ? transfer_time_values_to_utc_constructor(utc_time) : nil
63
+ @time_zone, @time = time_zone, local_time
64
+ @period = @utc ? period : get_period_and_ensure_valid_local_time(period)
49
65
  end
50
66
 
51
- # Returns a Time or DateTime instance that represents the time in +time_zone+.
67
+ # Returns a <tt>Time</tt> instance that represents the time in +time_zone+.
52
68
  def time
53
- @time ||= period.to_local(@utc)
69
+ @time ||= incorporate_utc_offset(@utc, utc_offset)
54
70
  end
55
71
 
56
- # Returns a Time or DateTime instance that represents the time in UTC.
72
+ # Returns a <tt>Time</tt> instance of the simultaneous time in the UTC timezone.
57
73
  def utc
58
- @utc ||= period.to_utc(@time)
74
+ @utc ||= incorporate_utc_offset(@time, -utc_offset)
59
75
  end
60
76
  alias_method :comparable_time, :utc
61
77
  alias_method :getgm, :utc
@@ -73,10 +89,9 @@ module ActiveSupport
73
89
  utc.in_time_zone(new_zone)
74
90
  end
75
91
 
76
- # Returns a <tt>Time.local()</tt> instance of the simultaneous time in your
77
- # system's <tt>ENV['TZ']</tt> zone.
78
- def localtime
79
- utc.respond_to?(:getlocal) ? utc.getlocal : utc.to_time.getlocal
92
+ # Returns a <tt>Time</tt> instance of the simultaneous time in the system timezone.
93
+ def localtime(utc_offset = nil)
94
+ utc.getlocal(utc_offset)
80
95
  end
81
96
  alias_method :getlocal, :localtime
82
97
 
@@ -98,13 +113,13 @@ module ActiveSupport
98
113
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
99
114
  # Time.zone.now.utc? # => false
100
115
  def utc?
101
- time_zone.name == 'UTC'
116
+ zone == "UTC" || zone == "UCT"
102
117
  end
103
118
  alias_method :gmt?, :utc?
104
119
 
105
120
  # Returns the offset from current time to UTC time in seconds.
106
121
  def utc_offset
107
- period.utc_total_offset
122
+ period.observed_utc_offset
108
123
  end
109
124
  alias_method :gmt_offset, :utc_offset
110
125
  alias_method :gmtoff, :utc_offset
@@ -121,24 +136,30 @@ module ActiveSupport
121
136
  utc? && alternate_utc_string || TimeZone.seconds_to_utc_offset(utc_offset, colon)
122
137
  end
123
138
 
124
- # Time uses +zone+ to display the time zone abbreviation, so we're
125
- # duck-typing it.
139
+ # Returns the time zone abbreviation.
140
+ #
141
+ # Time.zone = 'Eastern Time (US & Canada)' # => "Eastern Time (US & Canada)"
142
+ # Time.zone.now.zone # => "EST"
126
143
  def zone
127
- period.zone_identifier.to_s
144
+ period.abbreviation
128
145
  end
129
146
 
147
+ # Returns a string of the object's date, time, zone, and offset from UTC.
148
+ #
149
+ # Time.zone.now.inspect # => "Thu, 04 Dec 2014 11:00:25.624541392 EST -05:00"
130
150
  def inspect
131
- "#{time.strftime('%a, %d %b %Y %H:%M:%S')} #{zone} #{formatted_offset}"
151
+ "#{time.strftime('%a, %d %b %Y %H:%M:%S.%9N')} #{zone} #{formatted_offset}"
132
152
  end
133
153
 
154
+ # Returns a string of the object's date and time in the ISO 8601 standard
155
+ # format.
156
+ #
157
+ # Time.zone.now.xmlschema # => "2014-12-04T11:02:37-05:00"
134
158
  def xmlschema(fraction_digits = 0)
135
- fraction = if fraction_digits > 0
136
- (".%06i" % time.usec)[0, fraction_digits + 1]
137
- end
138
-
139
- "#{time.strftime("%Y-%m-%dT%H:%M:%S")}#{fraction}#{formatted_offset(true, 'Z')}"
159
+ "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z')}"
140
160
  end
141
161
  alias_method :iso8601, :xmlschema
162
+ alias_method :rfc3339, :xmlschema
142
163
 
143
164
  # Coerces time to a string for JSON encoding. The default format is ISO 8601.
144
165
  # You can get %Y/%m/%d %H:%M:%S +offset style by setting
@@ -146,26 +167,26 @@ module ActiveSupport
146
167
  # to +false+.
147
168
  #
148
169
  # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
149
- # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
150
- # # => "2005-02-01T15:15:10.000Z"
170
+ # Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").to_json
171
+ # # => "2005-02-01T05:15:10.000-10:00"
151
172
  #
152
173
  # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
153
- # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
154
- # # => "2005/02/01 15:15:10 +0000"
174
+ # Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").to_json
175
+ # # => "2005/02/01 05:15:10 -1000"
155
176
  def as_json(options = nil)
156
177
  if ActiveSupport::JSON::Encoding.use_standard_json_time_format
157
- xmlschema(3)
178
+ xmlschema(ActiveSupport::JSON::Encoding.time_precision)
158
179
  else
159
180
  %(#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
160
181
  end
161
182
  end
162
183
 
163
- def encode_with(coder)
164
- if coder.respond_to?(:represent_object)
165
- coder.represent_object(nil, utc)
166
- else
167
- coder.represent_scalar(nil, utc.strftime("%Y-%m-%d %H:%M:%S.%9NZ"))
168
- end
184
+ def init_with(coder) # :nodoc:
185
+ initialize(coder["utc"], coder["zone"], coder["time"])
186
+ end
187
+
188
+ def encode_with(coder) # :nodoc:
189
+ coder.map = { "utc" => utc, "zone" => time_zone, "time" => time }
169
190
  end
170
191
 
171
192
  # Returns a string of the object's date and time in the format used by
@@ -181,38 +202,67 @@ module ActiveSupport
181
202
  #
182
203
  # Time.zone.now.rfc2822 # => "Tue, 01 Jan 2013 04:51:39 +0000"
183
204
  def rfc2822
184
- to_s(:rfc822)
205
+ to_fs(:rfc822)
185
206
  end
186
207
  alias_method :rfc822, :rfc2822
187
208
 
188
- # <tt>:db</tt> format outputs time in UTC; all others output time in local.
189
- # Uses TimeWithZone's +strftime+, so <tt>%Z</tt> and <tt>%z</tt> work correctly.
190
- def to_s(format = :default)
209
+ NOT_SET = Object.new # :nodoc:
210
+
211
+ # Returns a string of the object's date and time.
212
+ def to_s(format = NOT_SET)
191
213
  if format == :db
192
- utc.to_s(format)
214
+ ActiveSupport::Deprecation.warn(
215
+ "TimeWithZone#to_s(:db) is deprecated. Please use TimeWithZone#to_fs(:db) instead."
216
+ )
217
+ utc.to_fs(format)
193
218
  elsif formatter = ::Time::DATE_FORMATS[format]
219
+ ActiveSupport::Deprecation.warn(
220
+ "TimeWithZone#to_s(#{format.inspect}) is deprecated. Please use TimeWithZone#to_fs(#{format.inspect}) instead."
221
+ )
194
222
  formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
223
+ elsif format == NOT_SET
224
+ "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby Time#to_s format
195
225
  else
196
- "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby 1.9 Time#to_s format
226
+ ActiveSupport::Deprecation.warn(
227
+ "TimeWithZone#to_s(#{format.inspect}) is deprecated. Please use TimeWithZone#to_fs(#{format.inspect}) instead."
228
+ )
229
+ "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby Time#to_s format
197
230
  end
198
231
  end
199
- alias_method :to_formatted_s, :to_s
200
232
 
201
- # Replaces <tt>%Z</tt> and <tt>%z</tt> directives with +zone+ and
202
- # +formatted_offset+, respectively, before passing to Time#strftime, so
203
- # that zone information is correct
233
+ # Returns a string of the object's date and time.
234
+ #
235
+ # This method is aliased to <tt>to_formatted_s</tt>.
236
+ #
237
+ # Accepts an optional <tt>format</tt>:
238
+ # * <tt>:default</tt> - default value, mimics Ruby Time#to_s format.
239
+ # * <tt>:db</tt> - format outputs time in UTC :db time. See Time#to_fs(:db).
240
+ # * Any key in <tt>Time::DATE_FORMATS</tt> can be used. See active_support/core_ext/time/conversions.rb.
241
+ def to_fs(format = :default)
242
+ if format == :db
243
+ utc.to_fs(format)
244
+ elsif formatter = ::Time::DATE_FORMATS[format]
245
+ formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
246
+ else
247
+ # Change to to_s when deprecation is gone.
248
+ "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}"
249
+ end
250
+ end
251
+ alias_method :to_formatted_s, :to_fs
252
+
253
+ # Replaces <tt>%Z</tt> directive with +zone before passing to Time#strftime,
254
+ # so that zone information is correct.
204
255
  def strftime(format)
205
- format = format.gsub('%Z', zone)
206
- .gsub('%z', formatted_offset(false))
207
- .gsub('%:z', formatted_offset(true))
208
- .gsub('%::z', formatted_offset(true) + ":00")
209
- time.strftime(format)
256
+ format = format.gsub(/((?:\A|[^%])(?:%%)*)%Z/, "\\1#{zone}")
257
+ getlocal(utc_offset).strftime(format)
210
258
  end
211
259
 
212
260
  # Use the time in UTC for comparisons.
213
261
  def <=>(other)
214
262
  utc <=> other
215
263
  end
264
+ alias_method :before?, :<
265
+ alias_method :after?, :>
216
266
 
217
267
  # Returns true if the current object's time is within the specified
218
268
  # +min+ and +max+ time.
@@ -231,22 +281,51 @@ module ActiveSupport
231
281
  time.today?
232
282
  end
233
283
 
284
+ # Returns true if the current object's time falls within
285
+ # the next day (tomorrow).
286
+ def tomorrow?
287
+ time.tomorrow?
288
+ end
289
+ alias :next_day? :tomorrow?
290
+
291
+ # Returns true if the current object's time falls within
292
+ # the previous day (yesterday).
293
+ def yesterday?
294
+ time.yesterday?
295
+ end
296
+ alias :prev_day? :yesterday?
297
+
234
298
  # Returns true if the current object's time is in the future.
235
299
  def future?
236
300
  utc.future?
237
301
  end
238
302
 
303
+ # Returns +true+ if +other+ is equal to current object.
239
304
  def eql?(other)
240
- utc.eql?(other)
305
+ other.eql?(utc)
241
306
  end
242
307
 
243
308
  def hash
244
309
  utc.hash
245
310
  end
246
311
 
312
+ # Adds an interval of time to the current object's time and returns that
313
+ # value as a new TimeWithZone object.
314
+ #
315
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
316
+ # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28.725182881 EDT -04:00
317
+ # now + 1000 # => Sun, 02 Nov 2014 01:43:08.725182881 EDT -04:00
318
+ #
319
+ # If we're adding a Duration of variable length (i.e., years, months, days),
320
+ # move forward from #time, otherwise move forward from #utc, for accuracy
321
+ # when moving across DST boundaries.
322
+ #
323
+ # For instance, a time + 24.hours will advance exactly 24 hours, while a
324
+ # time + 1.day will advance 23-25 hours, depending on the day.
325
+ #
326
+ # now + 24.hours # => Mon, 03 Nov 2014 00:26:28.725182881 EST -05:00
327
+ # now + 1.day # => Mon, 03 Nov 2014 01:26:28.725182881 EST -05:00
247
328
  def +(other)
248
- # If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
249
- # otherwise move forward from #utc, for accuracy when moving across DST boundaries
250
329
  if duration_of_variable_length?(other)
251
330
  method_missing(:+, other)
252
331
  else
@@ -254,12 +333,36 @@ module ActiveSupport
254
333
  result.in_time_zone(time_zone)
255
334
  end
256
335
  end
336
+ alias_method :since, :+
337
+ alias_method :in, :+
257
338
 
339
+ # Subtracts an interval of time and returns a new TimeWithZone object unless
340
+ # the other value +acts_like?+ time. Then it will return a Float of the difference
341
+ # between the two times that represents the difference between the current
342
+ # object's time and the +other+ time.
343
+ #
344
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
345
+ # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28.725182881 EST -05:00
346
+ # now - 1000 # => Mon, 03 Nov 2014 00:09:48.725182881 EST -05:00
347
+ #
348
+ # If subtracting a Duration of variable length (i.e., years, months, days),
349
+ # move backward from #time, otherwise move backward from #utc, for accuracy
350
+ # when moving across DST boundaries.
351
+ #
352
+ # For instance, a time - 24.hours will go subtract exactly 24 hours, while a
353
+ # time - 1.day will subtract 23-25 hours, depending on the day.
354
+ #
355
+ # now - 24.hours # => Sun, 02 Nov 2014 01:26:28.725182881 EDT -04:00
356
+ # now - 1.day # => Sun, 02 Nov 2014 00:26:28.725182881 EDT -04:00
357
+ #
358
+ # If both the TimeWithZone object and the other value act like Time, a Float
359
+ # will be returned.
360
+ #
361
+ # Time.zone.now - 1.day.ago # => 86399.999967
362
+ #
258
363
  def -(other)
259
- # If we're subtracting a Duration of variable length (i.e., years, months, days), move backwards from #time,
260
- # otherwise move backwards #utc, for accuracy when moving across DST boundaries
261
364
  if other.acts_like?(:time)
262
- utc.to_f - other.to_f
365
+ to_time - other.to_time
263
366
  elsif duration_of_variable_length?(other)
264
367
  method_missing(:-, other)
265
368
  else
@@ -268,20 +371,84 @@ module ActiveSupport
268
371
  end
269
372
  end
270
373
 
271
- def since(other)
272
- # If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
273
- # otherwise move forward from #utc, for accuracy when moving across DST boundaries
274
- if duration_of_variable_length?(other)
275
- method_missing(:since, other)
276
- else
277
- utc.since(other).in_time_zone(time_zone)
278
- end
279
- end
280
-
374
+ # Subtracts an interval of time from the current object's time and returns
375
+ # the result as a new TimeWithZone object.
376
+ #
377
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
378
+ # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28.725182881 EST -05:00
379
+ # now.ago(1000) # => Mon, 03 Nov 2014 00:09:48.725182881 EST -05:00
380
+ #
381
+ # If we're subtracting a Duration of variable length (i.e., years, months,
382
+ # days), move backward from #time, otherwise move backward from #utc, for
383
+ # accuracy when moving across DST boundaries.
384
+ #
385
+ # For instance, <tt>time.ago(24.hours)</tt> will move back exactly 24 hours,
386
+ # while <tt>time.ago(1.day)</tt> will move back 23-25 hours, depending on
387
+ # the day.
388
+ #
389
+ # now.ago(24.hours) # => Sun, 02 Nov 2014 01:26:28.725182881 EDT -04:00
390
+ # now.ago(1.day) # => Sun, 02 Nov 2014 00:26:28.725182881 EDT -04:00
281
391
  def ago(other)
282
392
  since(-other)
283
393
  end
284
394
 
395
+ # Returns a new +ActiveSupport::TimeWithZone+ where one or more of the elements have
396
+ # been changed according to the +options+ parameter. The time options (<tt>:hour</tt>,
397
+ # <tt>:min</tt>, <tt>:sec</tt>, <tt>:usec</tt>, <tt>:nsec</tt>) reset cascadingly,
398
+ # so if only the hour is passed, then minute, sec, usec and nsec is set to 0. If the
399
+ # hour and minute is passed, then sec, usec and nsec is set to 0. The +options+
400
+ # parameter takes a hash with any of these keys: <tt>:year</tt>, <tt>:month</tt>,
401
+ # <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>, <tt>:sec</tt>, <tt>:usec</tt>,
402
+ # <tt>:nsec</tt>, <tt>:offset</tt>, <tt>:zone</tt>. Pass either <tt>:usec</tt>
403
+ # or <tt>:nsec</tt>, not both. Similarly, pass either <tt>:zone</tt> or
404
+ # <tt>:offset</tt>, not both.
405
+ #
406
+ # t = Time.zone.now # => Fri, 14 Apr 2017 11:45:15.116992711 EST -05:00
407
+ # t.change(year: 2020) # => Tue, 14 Apr 2020 11:45:15.116992711 EST -05:00
408
+ # t.change(hour: 12) # => Fri, 14 Apr 2017 12:00:00.116992711 EST -05:00
409
+ # t.change(min: 30) # => Fri, 14 Apr 2017 11:30:00.116992711 EST -05:00
410
+ # t.change(offset: "-10:00") # => Fri, 14 Apr 2017 11:45:15.116992711 HST -10:00
411
+ # t.change(zone: "Hawaii") # => Fri, 14 Apr 2017 11:45:15.116992711 HST -10:00
412
+ def change(options)
413
+ if options[:zone] && options[:offset]
414
+ raise ArgumentError, "Can't change both :offset and :zone at the same time: #{options.inspect}"
415
+ end
416
+
417
+ new_time = time.change(options)
418
+
419
+ if options[:zone]
420
+ new_zone = ::Time.find_zone(options[:zone])
421
+ elsif options[:offset]
422
+ new_zone = ::Time.find_zone(new_time.utc_offset)
423
+ end
424
+
425
+ new_zone ||= time_zone
426
+ periods = new_zone.periods_for_local(new_time)
427
+
428
+ self.class.new(nil, new_zone, new_time, periods.include?(period) ? period : nil)
429
+ end
430
+
431
+ # Uses Date to provide precise Time calculations for years, months, and days
432
+ # according to the proleptic Gregorian calendar. The result is returned as a
433
+ # new TimeWithZone object.
434
+ #
435
+ # The +options+ parameter takes a hash with any of these keys:
436
+ # <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>,
437
+ # <tt>:hours</tt>, <tt>:minutes</tt>, <tt>:seconds</tt>.
438
+ #
439
+ # If advancing by a value of variable length (i.e., years, weeks, months,
440
+ # days), move forward from #time, otherwise move forward from #utc, for
441
+ # accuracy when moving across DST boundaries.
442
+ #
443
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
444
+ # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28.558049687 EDT -04:00
445
+ # now.advance(seconds: 1) # => Sun, 02 Nov 2014 01:26:29.558049687 EDT -04:00
446
+ # now.advance(minutes: 1) # => Sun, 02 Nov 2014 01:27:28.558049687 EDT -04:00
447
+ # now.advance(hours: 1) # => Sun, 02 Nov 2014 01:26:28.558049687 EST -05:00
448
+ # now.advance(days: 1) # => Mon, 03 Nov 2014 01:26:28.558049687 EST -05:00
449
+ # now.advance(weeks: 1) # => Sun, 09 Nov 2014 01:26:28.558049687 EST -05:00
450
+ # now.advance(months: 1) # => Tue, 02 Dec 2014 01:26:28.558049687 EST -05:00
451
+ # now.advance(years: 1) # => Mon, 02 Nov 2015 01:26:28.558049687 EST -05:00
285
452
  def advance(options)
286
453
  # If we're advancing a value of variable length (i.e., years, weeks, months, days), advance from #time,
287
454
  # otherwise advance from #utc, for accuracy when moving across DST boundaries
@@ -300,30 +467,57 @@ module ActiveSupport
300
467
  EOV
301
468
  end
302
469
 
470
+ # Returns Array of parts of Time in sequence of
471
+ # [seconds, minutes, hours, day, month, year, weekday, yearday, dst?, zone].
472
+ #
473
+ # now = Time.zone.now # => Tue, 18 Aug 2015 02:29:27.485278555 UTC +00:00
474
+ # now.to_a # => [27, 29, 2, 18, 8, 2015, 2, 230, false, "UTC"]
303
475
  def to_a
304
476
  [time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone]
305
477
  end
306
478
 
479
+ # Returns the object's date and time as a floating-point number of seconds
480
+ # since the Epoch (January 1, 1970 00:00 UTC).
481
+ #
482
+ # Time.zone.now.to_f # => 1417709320.285418
307
483
  def to_f
308
484
  utc.to_f
309
485
  end
310
486
 
487
+ # Returns the object's date and time as an integer number of seconds
488
+ # since the Epoch (January 1, 1970 00:00 UTC).
489
+ #
490
+ # Time.zone.now.to_i # => 1417709320
311
491
  def to_i
312
492
  utc.to_i
313
493
  end
314
494
  alias_method :tv_sec, :to_i
315
495
 
496
+ # Returns the object's date and time as a rational number of seconds
497
+ # since the Epoch (January 1, 1970 00:00 UTC).
498
+ #
499
+ # Time.zone.now.to_r # => (708854548642709/500000)
316
500
  def to_r
317
501
  utc.to_r
318
502
  end
319
503
 
320
- # Return an instance of Time in the system timezone.
321
- def to_time
322
- utc.to_time
504
+ # Returns an instance of DateTime with the timezone's UTC offset
505
+ #
506
+ # Time.zone.now.to_datetime # => Tue, 18 Aug 2015 02:32:20 +0000
507
+ # Time.current.in_time_zone('Hawaii').to_datetime # => Mon, 17 Aug 2015 16:32:20 -1000
508
+ def to_datetime
509
+ @to_datetime ||= utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
323
510
  end
324
511
 
325
- def to_datetime
326
- utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
512
+ # Returns an instance of +Time+, either with the same UTC offset
513
+ # as +self+ or in the local system timezone depending on the setting
514
+ # of +ActiveSupport.to_time_preserves_timezone+.
515
+ def to_time
516
+ if preserve_timezone
517
+ @to_time_with_instance_offset ||= getlocal(utc_offset)
518
+ else
519
+ @to_time_with_system_offset ||= getlocal
520
+ end
327
521
  end
328
522
 
329
523
  # So that +self+ <tt>acts_like?(:time)</tt>.
@@ -337,8 +531,14 @@ module ActiveSupport
337
531
  end
338
532
  alias_method :kind_of?, :is_a?
339
533
 
534
+ # An instance of ActiveSupport::TimeWithZone is never blank
535
+ def blank?
536
+ false
537
+ end
538
+
340
539
  def freeze
341
- period; utc; time # preload instance variables before freezing
540
+ # preload instance variables before freezing
541
+ period; utc; time; to_datetime; to_time
342
542
  super
343
543
  end
344
544
 
@@ -350,10 +550,17 @@ module ActiveSupport
350
550
  initialize(variables[0].utc, ::Time.find_zone(variables[1]), variables[2].utc)
351
551
  end
352
552
 
553
+ # respond_to_missing? is not called in some cases, such as when type conversion is
554
+ # performed with Kernel#String
555
+ def respond_to?(sym, include_priv = false)
556
+ # ensure that we're not going to throw and rescue from NoMethodError in method_missing which is slow
557
+ return false if sym.to_sym == :to_str
558
+ super
559
+ end
560
+
353
561
  # Ensure proxy class responds to all methods that underlying time instance
354
562
  # responds to.
355
563
  def respond_to_missing?(sym, include_priv)
356
- # consistently respond false to acts_like?(:date), regardless of whether #time is a Time or DateTime
357
564
  return false if sym.to_sym == :acts_like_date?
358
565
  time.respond_to?(sym, include_priv)
359
566
  end
@@ -363,16 +570,26 @@ module ActiveSupport
363
570
  def method_missing(sym, *args, &block)
364
571
  wrap_with_time_zone time.__send__(sym, *args, &block)
365
572
  rescue NoMethodError => e
366
- raise e, e.message.sub(time.inspect, self.inspect), e.backtrace
573
+ raise e, e.message.sub(time.inspect, inspect).sub("Time", "ActiveSupport::TimeWithZone"), e.backtrace
367
574
  end
368
575
 
369
576
  private
370
- def get_period_and_ensure_valid_local_time
577
+ SECONDS_PER_DAY = 86400
578
+
579
+ def incorporate_utc_offset(time, offset)
580
+ if time.kind_of?(Date)
581
+ time + Rational(offset, SECONDS_PER_DAY)
582
+ else
583
+ time + offset
584
+ end
585
+ end
586
+
587
+ def get_period_and_ensure_valid_local_time(period)
371
588
  # we don't want a Time.local instance enforcing its own DST rules as well,
372
589
  # so transfer time values to a utc constructor if necessary
373
590
  @time = transfer_time_values_to_utc_constructor(@time) unless @time.utc?
374
591
  begin
375
- @time_zone.period_for_local(@time)
592
+ period || @time_zone.period_for_local(@time)
376
593
  rescue ::TZInfo::PeriodNotFound
377
594
  # time is in the "spring forward" hour gap, so we're moving the time forward one hour and trying again
378
595
  @time += 1.hour
@@ -381,16 +598,19 @@ module ActiveSupport
381
598
  end
382
599
 
383
600
  def transfer_time_values_to_utc_constructor(time)
384
- ::Time.utc(time.year, time.month, time.day, time.hour, time.min, time.sec, Rational(time.nsec, 1000))
601
+ # avoid creating another Time object if possible
602
+ return time if time.instance_of?(::Time) && time.utc?
603
+ ::Time.utc(time.year, time.month, time.day, time.hour, time.min, time.sec + time.subsec)
385
604
  end
386
605
 
387
606
  def duration_of_variable_length?(obj)
388
- ActiveSupport::Duration === obj && obj.parts.any? {|p| [:years, :months, :days].include?(p[0]) }
607
+ ActiveSupport::Duration === obj && obj.variable?
389
608
  end
390
609
 
391
610
  def wrap_with_time_zone(time)
392
611
  if time.acts_like?(:time)
393
- self.class.new(nil, time_zone, time)
612
+ periods = time_zone.periods_for_local(time)
613
+ self.class.new(nil, time_zone, time, periods.include?(period) ? period : nil)
394
614
  elsif time.is_a?(Range)
395
615
  wrap_with_time_zone(time.begin)..wrap_with_time_zone(time.end)
396
616
  else
@@ -399,3 +619,8 @@ module ActiveSupport
399
619
  end
400
620
  end
401
621
  end
622
+
623
+ # These prevent Psych from calling `ActiveSupport::TimeWithZone.name`
624
+ # and triggering the deprecation warning about the change in Rails 7.1.
625
+ YAML.load_tags["!ruby/object:ActiveSupport::TimeWithZone"] = "ActiveSupport::TimeWithZone"
626
+ YAML.dump_tags[ActiveSupport::TimeWithZone] = "!ruby/object:ActiveSupport::TimeWithZone"