activesupport 1.2.4 → 8.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +505 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +40 -0
  5. data/lib/active_support/actionable_error.rb +50 -0
  6. data/lib/active_support/all.rb +5 -0
  7. data/lib/active_support/array_inquirer.rb +50 -0
  8. data/lib/active_support/backtrace_cleaner.rb +234 -0
  9. data/lib/active_support/benchmark.rb +21 -0
  10. data/lib/active_support/benchmarkable.rb +53 -0
  11. data/lib/active_support/broadcast_logger.rb +238 -0
  12. data/lib/active_support/builder.rb +8 -0
  13. data/lib/active_support/cache/coder.rb +153 -0
  14. data/lib/active_support/cache/entry.rb +134 -0
  15. data/lib/active_support/cache/file_store.rb +244 -0
  16. data/lib/active_support/cache/mem_cache_store.rb +288 -0
  17. data/lib/active_support/cache/memory_store.rb +264 -0
  18. data/lib/active_support/cache/null_store.rb +62 -0
  19. data/lib/active_support/cache/redis_cache_store.rb +498 -0
  20. data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
  21. data/lib/active_support/cache/strategy/local_cache.rb +246 -0
  22. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  23. data/lib/active_support/cache.rb +1170 -0
  24. data/lib/active_support/callbacks.rb +960 -0
  25. data/lib/active_support/class_attribute.rb +33 -0
  26. data/lib/active_support/code_generator.rb +79 -0
  27. data/lib/active_support/concern.rb +217 -0
  28. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  29. data/lib/active_support/concurrency/null_lock.rb +13 -0
  30. data/lib/active_support/concurrency/share_lock.rb +225 -0
  31. data/lib/active_support/concurrency/thread_monitor.rb +55 -0
  32. data/lib/active_support/configurable.rb +193 -0
  33. data/lib/active_support/configuration_file.rb +60 -0
  34. data/lib/active_support/continuous_integration.rb +145 -0
  35. data/lib/active_support/core_ext/array/access.rb +100 -0
  36. data/lib/active_support/core_ext/array/conversions.rb +209 -26
  37. data/lib/active_support/core_ext/array/extract.rb +21 -0
  38. data/lib/active_support/core_ext/array/extract_options.rb +31 -0
  39. data/lib/active_support/core_ext/array/grouping.rb +109 -0
  40. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  41. data/lib/active_support/core_ext/array/wrap.rb +48 -0
  42. data/lib/active_support/core_ext/array.rb +8 -4
  43. data/lib/active_support/core_ext/benchmark.rb +6 -0
  44. data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
  45. data/lib/active_support/core_ext/big_decimal.rb +3 -0
  46. data/lib/active_support/core_ext/class/attribute.rb +137 -0
  47. data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
  48. data/lib/active_support/core_ext/class/subclasses.rb +24 -0
  49. data/lib/active_support/core_ext/class.rb +4 -0
  50. data/lib/active_support/core_ext/date/acts_like.rb +10 -0
  51. data/lib/active_support/core_ext/date/blank.rb +18 -0
  52. data/lib/active_support/core_ext/date/calculations.rb +161 -0
  53. data/lib/active_support/core_ext/date/conversions.rb +95 -28
  54. data/lib/active_support/core_ext/date/zones.rb +8 -0
  55. data/lib/active_support/core_ext/date.rb +6 -5
  56. data/lib/active_support/core_ext/date_and_time/calculations.rb +374 -0
  57. data/lib/active_support/core_ext/date_and_time/compatibility.rb +23 -0
  58. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  59. data/lib/active_support/core_ext/date_time/acts_like.rb +16 -0
  60. data/lib/active_support/core_ext/date_time/blank.rb +18 -0
  61. data/lib/active_support/core_ext/date_time/calculations.rb +215 -0
  62. data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
  63. data/lib/active_support/core_ext/date_time/conversions.rb +108 -0
  64. data/lib/active_support/core_ext/date_time.rb +7 -0
  65. data/lib/active_support/core_ext/digest/uuid.rb +76 -0
  66. data/lib/active_support/core_ext/digest.rb +3 -0
  67. data/lib/active_support/core_ext/enumerable.rb +277 -7
  68. data/lib/active_support/core_ext/erb/util.rb +201 -0
  69. data/lib/active_support/core_ext/file/atomic.rb +72 -0
  70. data/lib/active_support/core_ext/file.rb +3 -0
  71. data/lib/active_support/core_ext/hash/conversions.rb +262 -0
  72. data/lib/active_support/core_ext/hash/deep_merge.rb +43 -0
  73. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  74. data/lib/active_support/core_ext/hash/except.rb +12 -0
  75. data/lib/active_support/core_ext/hash/indifferent_access.rb +19 -55
  76. data/lib/active_support/core_ext/hash/keys.rb +134 -44
  77. data/lib/active_support/core_ext/hash/reverse_merge.rb +22 -22
  78. data/lib/active_support/core_ext/hash/slice.rb +27 -0
  79. data/lib/active_support/core_ext/hash.rb +9 -8
  80. data/lib/active_support/core_ext/integer/inflections.rb +29 -13
  81. data/lib/active_support/core_ext/integer/multiple.rb +12 -0
  82. data/lib/active_support/core_ext/integer/time.rb +22 -0
  83. data/lib/active_support/core_ext/integer.rb +4 -6
  84. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  85. data/lib/active_support/core_ext/kernel/reporting.rb +45 -0
  86. data/lib/active_support/core_ext/kernel/singleton_class.rb +8 -0
  87. data/lib/active_support/core_ext/kernel.rb +4 -78
  88. data/lib/active_support/core_ext/load_error.rb +6 -35
  89. data/lib/active_support/core_ext/module/aliasing.rb +31 -0
  90. data/lib/active_support/core_ext/module/anonymous.rb +30 -0
  91. data/lib/active_support/core_ext/module/attr_internal.rb +48 -0
  92. data/lib/active_support/core_ext/module/attribute_accessors.rb +214 -0
  93. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +175 -0
  94. data/lib/active_support/core_ext/module/concerning.rb +140 -0
  95. data/lib/active_support/core_ext/module/delegation.rb +225 -0
  96. data/lib/active_support/core_ext/module/deprecation.rb +25 -0
  97. data/lib/active_support/core_ext/module/introspection.rb +65 -0
  98. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  99. data/lib/active_support/core_ext/module/remove_method.rb +17 -0
  100. data/lib/active_support/core_ext/module.rb +13 -0
  101. data/lib/active_support/core_ext/name_error.rb +59 -0
  102. data/lib/active_support/core_ext/numeric/bytes.rb +73 -42
  103. data/lib/active_support/core_ext/numeric/conversions.rb +145 -0
  104. data/lib/active_support/core_ext/numeric/time.rb +64 -57
  105. data/lib/active_support/core_ext/numeric.rb +4 -6
  106. data/lib/active_support/core_ext/object/acts_like.rb +45 -0
  107. data/lib/active_support/core_ext/object/blank.rb +199 -0
  108. data/lib/active_support/core_ext/object/conversions.rb +6 -0
  109. data/lib/active_support/core_ext/object/deep_dup.rb +71 -0
  110. data/lib/active_support/core_ext/object/duplicable.rb +69 -0
  111. data/lib/active_support/core_ext/object/inclusion.rb +37 -0
  112. data/lib/active_support/core_ext/object/instance_variables.rb +32 -0
  113. data/lib/active_support/core_ext/object/json.rb +267 -0
  114. data/lib/active_support/core_ext/object/to_param.rb +3 -0
  115. data/lib/active_support/core_ext/object/to_query.rb +93 -0
  116. data/lib/active_support/core_ext/object/try.rb +158 -0
  117. data/lib/active_support/core_ext/object/with.rb +46 -0
  118. data/lib/active_support/core_ext/object/with_options.rb +101 -0
  119. data/lib/active_support/core_ext/object.rb +17 -0
  120. data/lib/active_support/core_ext/pathname/blank.rb +20 -0
  121. data/lib/active_support/core_ext/pathname/existence.rb +23 -0
  122. data/lib/active_support/core_ext/pathname.rb +4 -0
  123. data/lib/active_support/core_ext/range/compare_range.rb +57 -0
  124. data/lib/active_support/core_ext/range/conversions.rb +58 -17
  125. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  126. data/lib/active_support/core_ext/range/sole.rb +17 -0
  127. data/lib/active_support/core_ext/range.rb +5 -4
  128. data/lib/active_support/core_ext/regexp.rb +14 -0
  129. data/lib/active_support/core_ext/securerandom.rb +57 -0
  130. data/lib/active_support/core_ext/string/access.rb +93 -56
  131. data/lib/active_support/core_ext/string/behavior.rb +8 -0
  132. data/lib/active_support/core_ext/string/conversions.rb +57 -16
  133. data/lib/active_support/core_ext/string/exclude.rb +13 -0
  134. data/lib/active_support/core_ext/string/filters.rb +151 -0
  135. data/lib/active_support/core_ext/string/indent.rb +45 -0
  136. data/lib/active_support/core_ext/string/inflections.rb +297 -54
  137. data/lib/active_support/core_ext/string/inquiry.rb +16 -0
  138. data/lib/active_support/core_ext/string/multibyte.rb +67 -0
  139. data/lib/active_support/core_ext/string/output_safety.rb +235 -0
  140. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -18
  141. data/lib/active_support/core_ext/string/strip.rb +27 -0
  142. data/lib/active_support/core_ext/string/zones.rb +16 -0
  143. data/lib/active_support/core_ext/string.rb +14 -10
  144. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  145. data/lib/active_support/core_ext/symbol.rb +3 -0
  146. data/lib/active_support/core_ext/thread/backtrace/location.rb +7 -0
  147. data/lib/active_support/core_ext/time/acts_like.rb +10 -0
  148. data/lib/active_support/core_ext/time/calculations.rb +358 -153
  149. data/lib/active_support/core_ext/time/compatibility.rb +15 -0
  150. data/lib/active_support/core_ext/time/conversions.rb +69 -30
  151. data/lib/active_support/core_ext/time/zones.rb +97 -0
  152. data/lib/active_support/core_ext/time.rb +6 -6
  153. data/lib/active_support/core_ext.rb +5 -1
  154. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  155. data/lib/active_support/current_attributes.rb +243 -0
  156. data/lib/active_support/deep_mergeable.rb +53 -0
  157. data/lib/active_support/delegation.rb +183 -0
  158. data/lib/active_support/dependencies/autoload.rb +72 -0
  159. data/lib/active_support/dependencies/interlock.rb +55 -0
  160. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  161. data/lib/active_support/dependencies.rb +84 -222
  162. data/lib/active_support/deprecation/behaviors.rb +148 -0
  163. data/lib/active_support/deprecation/constant_accessor.rb +74 -0
  164. data/lib/active_support/deprecation/deprecators.rb +104 -0
  165. data/lib/active_support/deprecation/disallowed.rb +54 -0
  166. data/lib/active_support/deprecation/method_wrappers.rb +68 -0
  167. data/lib/active_support/deprecation/proxy_wrappers.rb +189 -0
  168. data/lib/active_support/deprecation/reporting.rb +162 -0
  169. data/lib/active_support/deprecation.rb +81 -0
  170. data/lib/active_support/deprecator.rb +7 -0
  171. data/lib/active_support/descendants_tracker.rb +112 -0
  172. data/lib/active_support/digest.rb +22 -0
  173. data/lib/active_support/duration/iso8601_parser.rb +123 -0
  174. data/lib/active_support/duration/iso8601_serializer.rb +64 -0
  175. data/lib/active_support/duration.rb +524 -0
  176. data/lib/active_support/editor.rb +70 -0
  177. data/lib/active_support/encrypted_configuration.rb +126 -0
  178. data/lib/active_support/encrypted_file.rb +133 -0
  179. data/lib/active_support/environment_inquirer.rb +40 -0
  180. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  181. data/lib/active_support/error_reporter.rb +318 -0
  182. data/lib/active_support/event_reporter/test_helper.rb +32 -0
  183. data/lib/active_support/event_reporter.rb +592 -0
  184. data/lib/active_support/evented_file_update_checker.rb +185 -0
  185. data/lib/active_support/execution_context/test_helper.rb +13 -0
  186. data/lib/active_support/execution_context.rb +110 -0
  187. data/lib/active_support/execution_wrapper.rb +150 -0
  188. data/lib/active_support/executor/test_helper.rb +7 -0
  189. data/lib/active_support/executor.rb +8 -0
  190. data/lib/active_support/file_update_checker.rb +166 -0
  191. data/lib/active_support/fork_tracker.rb +43 -0
  192. data/lib/active_support/gem_version.rb +17 -0
  193. data/lib/active_support/gzip.rb +41 -0
  194. data/lib/active_support/hash_with_indifferent_access.rb +464 -0
  195. data/lib/active_support/html_safe_translation.rb +56 -0
  196. data/lib/active_support/i18n.rb +17 -0
  197. data/lib/active_support/i18n_railtie.rb +140 -0
  198. data/lib/active_support/inflections.rb +68 -49
  199. data/lib/active_support/inflector/inflections.rb +290 -0
  200. data/lib/active_support/inflector/methods.rb +387 -0
  201. data/lib/active_support/inflector/transliterate.rb +147 -0
  202. data/lib/active_support/inflector.rb +7 -164
  203. data/lib/active_support/isolated_execution_state.rb +76 -0
  204. data/lib/active_support/json/decoding.rb +78 -0
  205. data/lib/active_support/json/encoding.rb +256 -0
  206. data/lib/active_support/json.rb +4 -0
  207. data/lib/active_support/key_generator.rb +66 -0
  208. data/lib/active_support/lazy_load_hooks.rb +107 -0
  209. data/lib/active_support/locale/en.rb +33 -0
  210. data/lib/active_support/locale/en.yml +141 -0
  211. data/lib/active_support/log_subscriber/test_helper.rb +106 -0
  212. data/lib/active_support/log_subscriber.rb +188 -0
  213. data/lib/active_support/logger.rb +55 -0
  214. data/lib/active_support/logger_silence.rb +21 -0
  215. data/lib/active_support/logger_thread_safe_level.rb +50 -0
  216. data/lib/active_support/message_encryptor.rb +374 -0
  217. data/lib/active_support/message_encryptors.rb +193 -0
  218. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  219. data/lib/active_support/message_pack/extensions.rb +310 -0
  220. data/lib/active_support/message_pack/serializer.rb +63 -0
  221. data/lib/active_support/message_pack.rb +50 -0
  222. data/lib/active_support/message_verifier.rb +377 -0
  223. data/lib/active_support/message_verifiers.rb +189 -0
  224. data/lib/active_support/messages/codec.rb +65 -0
  225. data/lib/active_support/messages/metadata.rb +146 -0
  226. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  227. data/lib/active_support/messages/rotation_coordinator.rb +102 -0
  228. data/lib/active_support/messages/rotator.rb +69 -0
  229. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  230. data/lib/active_support/multibyte/chars.rb +188 -0
  231. data/lib/active_support/multibyte/unicode.rb +42 -0
  232. data/lib/active_support/multibyte.rb +27 -0
  233. data/lib/active_support/notifications/fanout.rb +467 -0
  234. data/lib/active_support/notifications/instrumenter.rb +240 -0
  235. data/lib/active_support/notifications.rb +281 -0
  236. data/lib/active_support/number_helper/number_converter.rb +190 -0
  237. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  238. data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
  239. data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
  240. data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
  241. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  242. data/lib/active_support/number_helper/number_to_phone_converter.rb +60 -0
  243. data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
  244. data/lib/active_support/number_helper/rounding_helper.rb +46 -0
  245. data/lib/active_support/number_helper.rb +479 -0
  246. data/lib/active_support/option_merger.rb +38 -0
  247. data/lib/active_support/ordered_hash.rb +50 -0
  248. data/lib/active_support/ordered_options.rb +141 -25
  249. data/lib/active_support/parameter_filter.rb +157 -0
  250. data/lib/active_support/rails.rb +26 -0
  251. data/lib/active_support/railtie.rb +180 -0
  252. data/lib/active_support/reloader.rb +138 -0
  253. data/lib/active_support/rescuable.rb +176 -0
  254. data/lib/active_support/secure_compare_rotator.rb +58 -0
  255. data/lib/active_support/security_utils.rb +38 -0
  256. data/lib/active_support/string_inquirer.rb +35 -0
  257. data/lib/active_support/structured_event_subscriber.rb +99 -0
  258. data/lib/active_support/subscriber.rb +141 -0
  259. data/lib/active_support/syntax_error_proxy.rb +67 -0
  260. data/lib/active_support/tagged_logging.rb +157 -0
  261. data/lib/active_support/test_case.rb +365 -0
  262. data/lib/active_support/testing/assertions.rb +369 -0
  263. data/lib/active_support/testing/autorun.rb +10 -0
  264. data/lib/active_support/testing/constant_lookup.rb +51 -0
  265. data/lib/active_support/testing/constant_stubbing.rb +54 -0
  266. data/lib/active_support/testing/declarative.rb +28 -0
  267. data/lib/active_support/testing/deprecation.rb +82 -0
  268. data/lib/active_support/testing/error_reporter_assertions.rb +124 -0
  269. data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
  270. data/lib/active_support/testing/file_fixtures.rb +38 -0
  271. data/lib/active_support/testing/isolation.rb +121 -0
  272. data/lib/active_support/testing/method_call_assertions.rb +69 -0
  273. data/lib/active_support/testing/notification_assertions.rb +92 -0
  274. data/lib/active_support/testing/parallelization/server.rb +98 -0
  275. data/lib/active_support/testing/parallelization/worker.rb +107 -0
  276. data/lib/active_support/testing/parallelization.rb +79 -0
  277. data/lib/active_support/testing/parallelize_executor.rb +81 -0
  278. data/lib/active_support/testing/setup_and_teardown.rb +57 -0
  279. data/lib/active_support/testing/stream.rb +41 -0
  280. data/lib/active_support/testing/tagged_logging.rb +27 -0
  281. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  282. data/lib/active_support/testing/time_helpers.rb +273 -0
  283. data/lib/active_support/time.rb +20 -0
  284. data/lib/active_support/time_with_zone.rb +613 -0
  285. data/lib/active_support/values/time_zone.rb +599 -158
  286. data/lib/active_support/version.rb +7 -6
  287. data/lib/active_support/xml_mini/jdom.rb +175 -0
  288. data/lib/active_support/xml_mini/libxml.rb +80 -0
  289. data/lib/active_support/xml_mini/libxmlsax.rb +83 -0
  290. data/lib/active_support/xml_mini/nokogiri.rb +83 -0
  291. data/lib/active_support/xml_mini/nokogirisax.rb +86 -0
  292. data/lib/active_support/xml_mini/rexml.rb +137 -0
  293. data/lib/active_support/xml_mini.rb +212 -0
  294. data/lib/active_support.rb +122 -10
  295. metadata +524 -93
  296. data/CHANGELOG +0 -283
  297. data/lib/active_support/binding_of_caller.rb +0 -84
  298. data/lib/active_support/breakpoint.rb +0 -523
  299. data/lib/active_support/class_attribute_accessors.rb +0 -57
  300. data/lib/active_support/class_inheritable_attributes.rb +0 -117
  301. data/lib/active_support/clean_logger.rb +0 -36
  302. data/lib/active_support/core_ext/blank.rb +0 -38
  303. data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +0 -14
  304. data/lib/active_support/core_ext/cgi.rb +0 -5
  305. data/lib/active_support/core_ext/exception.rb +0 -29
  306. data/lib/active_support/core_ext/integer/even_odd.rb +0 -24
  307. data/lib/active_support/core_ext/object_and_class.rb +0 -44
  308. data/lib/active_support/module_attribute_accessors.rb +0 -57
  309. data/lib/active_support/whiny_nil.rb +0 -38
@@ -1,180 +1,621 @@
1
- # A value object representing a time zone. A time zone is simply a named
2
- # offset (in seconds) from GMT. Note that two time zone objects are only
3
- # equivalent if they have both the same offset, and the same name.
4
- #
5
- # A TimeZone instance may be used to convert a Time value to the corresponding
6
- # time zone.
7
- #
8
- # The class also includes #all, which returns a list of all TimeZone objects.
9
- class TimeZone
10
- include Comparable
11
-
12
- attr_reader :name, :utc_offset
13
-
14
- # Create a new TimeZone object with the given name and offset. The offset is
15
- # the number of seconds that this time zone is offset from UTC (GMT). Seconds
16
- # were chosen as the offset unit because that is the unit that Ruby uses
17
- # to represent time zone offsets (see Time#utc_offset).
18
- def initialize(name, utc_offset)
19
- @name = name
20
- @utc_offset = utc_offset
21
- end
1
+ # frozen_string_literal: true
22
2
 
23
- # Returns the offset of this time zone as a formatted string, of the
24
- # format "+HH:MM". If the offset is zero, this returns the empty
25
- # string. If +colon+ is false, a colon will not be inserted into the
26
- # result.
27
- def formatted_offset( colon=true )
28
- return "" if utc_offset == 0
29
- sign = (utc_offset < 0 ? -1 : 1)
30
- hours = utc_offset.abs / 3600
31
- minutes = (utc_offset.abs % 3600) / 60
32
- "%+03d%s%02d" % [ hours * sign, colon ? ":" : "", minutes ]
33
- end
3
+ require "tzinfo"
4
+ require "concurrent/map"
34
5
 
35
- # Compute and return the current time, in the time zone represented by
36
- # +self+.
37
- def now
38
- adjust(Time.now)
39
- end
6
+ module ActiveSupport
7
+ # = Active Support \Time Zone
8
+ #
9
+ # The TimeZone class serves as a wrapper around +TZInfo::Timezone+ instances.
10
+ # It allows us to do the following:
11
+ #
12
+ # * Limit the set of zones provided by TZInfo to a meaningful subset of 134
13
+ # zones.
14
+ # * Retrieve and display zones with a friendlier name
15
+ # (e.g., "Eastern \Time (US & Canada)" instead of "America/New_York").
16
+ # * Lazily load +TZInfo::Timezone+ instances only when they're needed.
17
+ # * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+,
18
+ # +parse+, +at+, and +now+ methods.
19
+ #
20
+ # If you set <tt>config.time_zone</tt> in the \Rails Application, you can
21
+ # access this TimeZone object via <tt>Time.zone</tt>:
22
+ #
23
+ # # application.rb:
24
+ # class Application < Rails::Application
25
+ # config.time_zone = 'Eastern Time (US & Canada)'
26
+ # end
27
+ #
28
+ # Time.zone # => #<ActiveSupport::TimeZone:0x514834...>
29
+ # Time.zone.name # => "Eastern Time (US & Canada)"
30
+ # Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00
31
+ class TimeZone
32
+ # Keys are \Rails TimeZone names, values are TZInfo identifiers.
33
+ MAPPING = {
34
+ "International Date Line West" => "Etc/GMT+12",
35
+ "Midway Island" => "Pacific/Midway",
36
+ "American Samoa" => "Pacific/Pago_Pago",
37
+ "Hawaii" => "Pacific/Honolulu",
38
+ "Alaska" => "America/Juneau",
39
+ "Pacific Time (US & Canada)" => "America/Los_Angeles",
40
+ "Tijuana" => "America/Tijuana",
41
+ "Mountain Time (US & Canada)" => "America/Denver",
42
+ "Arizona" => "America/Phoenix",
43
+ "Chihuahua" => "America/Chihuahua",
44
+ "Mazatlan" => "America/Mazatlan",
45
+ "Central Time (US & Canada)" => "America/Chicago",
46
+ "Saskatchewan" => "America/Regina",
47
+ "Guadalajara" => "America/Mexico_City",
48
+ "Mexico City" => "America/Mexico_City",
49
+ "Monterrey" => "America/Monterrey",
50
+ "Central America" => "America/Guatemala",
51
+ "Eastern Time (US & Canada)" => "America/New_York",
52
+ "Indiana (East)" => "America/Indiana/Indianapolis",
53
+ "Bogota" => "America/Bogota",
54
+ "Lima" => "America/Lima",
55
+ "Quito" => "America/Lima",
56
+ "Atlantic Time (Canada)" => "America/Halifax",
57
+ "Caracas" => "America/Caracas",
58
+ "La Paz" => "America/La_Paz",
59
+ "Santiago" => "America/Santiago",
60
+ "Asuncion" => "America/Asuncion",
61
+ "Newfoundland" => "America/St_Johns",
62
+ "Brasilia" => "America/Sao_Paulo",
63
+ "Buenos Aires" => "America/Argentina/Buenos_Aires",
64
+ "Montevideo" => "America/Montevideo",
65
+ "Georgetown" => "America/Guyana",
66
+ "Puerto Rico" => "America/Puerto_Rico",
67
+ "Greenland" => "America/Nuuk",
68
+ "Mid-Atlantic" => "Atlantic/South_Georgia",
69
+ "Azores" => "Atlantic/Azores",
70
+ "Cape Verde Is." => "Atlantic/Cape_Verde",
71
+ "Dublin" => "Europe/Dublin",
72
+ "Edinburgh" => "Europe/London",
73
+ "Lisbon" => "Europe/Lisbon",
74
+ "London" => "Europe/London",
75
+ "Casablanca" => "Africa/Casablanca",
76
+ "Monrovia" => "Africa/Monrovia",
77
+ "UTC" => "Etc/UTC",
78
+ "Belgrade" => "Europe/Belgrade",
79
+ "Bratislava" => "Europe/Bratislava",
80
+ "Budapest" => "Europe/Budapest",
81
+ "Ljubljana" => "Europe/Ljubljana",
82
+ "Prague" => "Europe/Prague",
83
+ "Sarajevo" => "Europe/Sarajevo",
84
+ "Skopje" => "Europe/Skopje",
85
+ "Warsaw" => "Europe/Warsaw",
86
+ "Zagreb" => "Europe/Zagreb",
87
+ "Brussels" => "Europe/Brussels",
88
+ "Copenhagen" => "Europe/Copenhagen",
89
+ "Madrid" => "Europe/Madrid",
90
+ "Paris" => "Europe/Paris",
91
+ "Amsterdam" => "Europe/Amsterdam",
92
+ "Berlin" => "Europe/Berlin",
93
+ "Bern" => "Europe/Zurich",
94
+ "Zurich" => "Europe/Zurich",
95
+ "Rome" => "Europe/Rome",
96
+ "Stockholm" => "Europe/Stockholm",
97
+ "Vienna" => "Europe/Vienna",
98
+ "West Central Africa" => "Africa/Algiers",
99
+ "Bucharest" => "Europe/Bucharest",
100
+ "Cairo" => "Africa/Cairo",
101
+ "Helsinki" => "Europe/Helsinki",
102
+ "Kyiv" => "Europe/Kiev",
103
+ "Riga" => "Europe/Riga",
104
+ "Sofia" => "Europe/Sofia",
105
+ "Tallinn" => "Europe/Tallinn",
106
+ "Vilnius" => "Europe/Vilnius",
107
+ "Athens" => "Europe/Athens",
108
+ "Istanbul" => "Europe/Istanbul",
109
+ "Minsk" => "Europe/Minsk",
110
+ "Jerusalem" => "Asia/Jerusalem",
111
+ "Harare" => "Africa/Harare",
112
+ "Pretoria" => "Africa/Johannesburg",
113
+ "Kaliningrad" => "Europe/Kaliningrad",
114
+ "Moscow" => "Europe/Moscow",
115
+ "St. Petersburg" => "Europe/Moscow",
116
+ "Volgograd" => "Europe/Volgograd",
117
+ "Samara" => "Europe/Samara",
118
+ "Kuwait" => "Asia/Kuwait",
119
+ "Riyadh" => "Asia/Riyadh",
120
+ "Nairobi" => "Africa/Nairobi",
121
+ "Baghdad" => "Asia/Baghdad",
122
+ "Tehran" => "Asia/Tehran",
123
+ "Abu Dhabi" => "Asia/Muscat",
124
+ "Muscat" => "Asia/Muscat",
125
+ "Baku" => "Asia/Baku",
126
+ "Tbilisi" => "Asia/Tbilisi",
127
+ "Yerevan" => "Asia/Yerevan",
128
+ "Kabul" => "Asia/Kabul",
129
+ "Ekaterinburg" => "Asia/Yekaterinburg",
130
+ "Islamabad" => "Asia/Karachi",
131
+ "Karachi" => "Asia/Karachi",
132
+ "Tashkent" => "Asia/Tashkent",
133
+ "Chennai" => "Asia/Kolkata",
134
+ "Kolkata" => "Asia/Kolkata",
135
+ "Mumbai" => "Asia/Kolkata",
136
+ "New Delhi" => "Asia/Kolkata",
137
+ "Kathmandu" => "Asia/Kathmandu",
138
+ "Dhaka" => "Asia/Dhaka",
139
+ "Sri Jayawardenepura" => "Asia/Colombo",
140
+ "Almaty" => "Asia/Almaty",
141
+ "Astana" => "Asia/Almaty",
142
+ "Novosibirsk" => "Asia/Novosibirsk",
143
+ "Rangoon" => "Asia/Rangoon",
144
+ "Bangkok" => "Asia/Bangkok",
145
+ "Hanoi" => "Asia/Bangkok",
146
+ "Jakarta" => "Asia/Jakarta",
147
+ "Krasnoyarsk" => "Asia/Krasnoyarsk",
148
+ "Beijing" => "Asia/Shanghai",
149
+ "Chongqing" => "Asia/Chongqing",
150
+ "Hong Kong" => "Asia/Hong_Kong",
151
+ "Urumqi" => "Asia/Urumqi",
152
+ "Kuala Lumpur" => "Asia/Kuala_Lumpur",
153
+ "Singapore" => "Asia/Singapore",
154
+ "Taipei" => "Asia/Taipei",
155
+ "Perth" => "Australia/Perth",
156
+ "Irkutsk" => "Asia/Irkutsk",
157
+ "Ulaanbaatar" => "Asia/Ulaanbaatar",
158
+ "Seoul" => "Asia/Seoul",
159
+ "Osaka" => "Asia/Tokyo",
160
+ "Sapporo" => "Asia/Tokyo",
161
+ "Tokyo" => "Asia/Tokyo",
162
+ "Yakutsk" => "Asia/Yakutsk",
163
+ "Darwin" => "Australia/Darwin",
164
+ "Adelaide" => "Australia/Adelaide",
165
+ "Canberra" => "Australia/Canberra",
166
+ "Melbourne" => "Australia/Melbourne",
167
+ "Sydney" => "Australia/Sydney",
168
+ "Brisbane" => "Australia/Brisbane",
169
+ "Hobart" => "Australia/Hobart",
170
+ "Vladivostok" => "Asia/Vladivostok",
171
+ "Guam" => "Pacific/Guam",
172
+ "Port Moresby" => "Pacific/Port_Moresby",
173
+ "Magadan" => "Asia/Magadan",
174
+ "Srednekolymsk" => "Asia/Srednekolymsk",
175
+ "Solomon Is." => "Pacific/Guadalcanal",
176
+ "New Caledonia" => "Pacific/Noumea",
177
+ "Fiji" => "Pacific/Fiji",
178
+ "Kamchatka" => "Asia/Kamchatka",
179
+ "Marshall Is." => "Pacific/Majuro",
180
+ "Auckland" => "Pacific/Auckland",
181
+ "Wellington" => "Pacific/Auckland",
182
+ "Nuku'alofa" => "Pacific/Tongatapu",
183
+ "Tokelau Is." => "Pacific/Fakaofo",
184
+ "Chatham Is." => "Pacific/Chatham",
185
+ "Samoa" => "Pacific/Apia"
186
+ }
40
187
 
41
- # Return the current date in this time zone.
42
- def today
43
- now.to_date
44
- end
188
+ UTC_OFFSET_WITH_COLON = "%s%02d:%02d" # :nodoc:
189
+ UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.tr(":", "") # :nodoc:
190
+ private_constant :UTC_OFFSET_WITH_COLON, :UTC_OFFSET_WITHOUT_COLON
45
191
 
46
- # Adjust the given time to the time zone represented by +self+.
47
- def adjust(time)
48
- time = time.to_time
49
- time + utc_offset - time.utc_offset
50
- end
192
+ @lazy_zones_map = Concurrent::Map.new
193
+ @country_zones = Concurrent::Map.new
51
194
 
52
- # Reinterprets the given time value as a time in the current time
53
- # zone, and then adjusts it to return the corresponding time in the
54
- # local time zone.
55
- def unadjust(time)
56
- time = Time.local(*time.to_time.to_a)
57
- time - utc_offset + time.utc_offset
58
- end
195
+ class << self
196
+ # Assumes self represents an offset from UTC in seconds (as returned from
197
+ # Time#utc_offset) and turns this into an +HH:MM formatted string.
198
+ #
199
+ # ActiveSupport::TimeZone.seconds_to_utc_offset(-21_600) # => "-06:00"
200
+ def seconds_to_utc_offset(seconds, colon = true)
201
+ format = colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON
202
+ sign = (seconds < 0 ? "-" : "+")
203
+ hours = seconds.abs / 3600
204
+ minutes = (seconds.abs % 3600) / 60
205
+ format % [sign, hours, minutes]
206
+ end
59
207
 
60
- # Compare this time zone to the parameter. The two are comapred first on
61
- # their offsets, and then by name.
62
- def <=>(zone)
63
- result = (utc_offset <=> zone.utc_offset)
64
- result = (name <=> zone.name) if result == 0
65
- result
66
- end
208
+ def find_tzinfo(name)
209
+ TZInfo::Timezone.get(MAPPING[name] || name)
210
+ end
67
211
 
68
- # Returns a textual representation of this time zone.
69
- def to_s
70
- "(GMT#{formatted_offset}) #{name}"
71
- end
212
+ alias_method :create, :new # :nodoc:
72
213
 
73
- @@zones = nil
74
-
75
- class << self
76
- # Create a new TimeZone instance with the given name and offset.
77
- def create(name, offset)
78
- zone = allocate
79
- zone.send :initialize, name, offset
80
- zone
81
- end
82
-
83
- # Return a TimeZone instance with the given name, or +nil+ if no
84
- # such TimeZone instance exists. (This exists to support the use of
85
- # this class with the #composed_of macro.)
86
- def new(name)
87
- self[name]
88
- end
89
-
90
- # Return an array of all TimeZone objects. There are multiple TimeZone
91
- # objects per time zone, in many cases, to make it easier for users to
92
- # find their own time zone.
93
- def all
94
- unless @@zones
95
- @@zones = []
96
- [[-43_200, "International Date Line West" ],
97
- [-39_600, "Midway Island", "Samoa" ],
98
- [-36_000, "Hawaii" ],
99
- [-32_400, "Alaska" ],
100
- [-28_800, "Pacific Time (US & Canada)", "Tijuana" ],
101
- [-25_200, "Mountain Time (US & Canada)", "Chihuahua", "La Paz",
102
- "Mazatlan", "Arizona" ],
103
- [-21_600, "Central Time (US & Canada)", "Saskatchewan", "Guadalajara",
104
- "Mexico City", "Monterrey", "Central America" ],
105
- [-18_000, "Eastern Time (US & Canada)", "Indiana (East)", "Bogota",
106
- "Lima", "Quito" ],
107
- [-14_400, "Atlantic Time (Canada)", "Caracas", "La Paz", "Santiago" ],
108
- [-12_600, "Newfoundland" ],
109
- [-10_800, "Brasilia", "Buenos Aires", "Georgetown", "Greenland" ],
110
- [ -7_200, "Mid-Atlantic" ],
111
- [ -3_600, "Azores", "Cape Verde Is." ],
112
- [ 0, "Dublin", "Edinburgh", "Lisbon", "London", "Casablanca",
113
- "Monrovia" ],
114
- [ 3_600, "Belgrade", "Bratislava", "Budapest", "Ljubljana", "Prague",
115
- "Sarajevo", "Skopje", "Warsaw", "Zagreb", "Brussels",
116
- "Copenhagen", "Madrid", "Paris", "Amsterdam", "Berlin",
117
- "Bern", "Rome", "Stockholm", "Vienna",
118
- "West Central Africa" ],
119
- [ 7_200, "Bucharest", "Cairo", "Helsinki", "Kyev", "Riga", "Sofia",
120
- "Tallinn", "Vilnius", "Athens", "Istanbul", "Minsk",
121
- "Jerusalem", "Harare", "Pretoria" ],
122
- [ 10_800, "Moscow", "St. Petersburg", "Volgograd", "Kuwait", "Riyadh",
123
- "Nairobi", "Baghdad" ],
124
- [ 12_600, "Tehran" ],
125
- [ 14_400, "Abu Dhabi", "Muscat", "Baku", "Tbilisi", "Yerevan" ],
126
- [ 16_200, "Kabul" ],
127
- [ 18_000, "Ekaterinburg", "Islamabad", "Karachi", "Tashkent" ],
128
- [ 19_800, "Chennai", "Kolkata", "Mumbai", "New Delhi" ],
129
- [ 20_700, "Kathmandu" ],
130
- [ 21_600, "Astana", "Dhaka", "Sri Jayawardenepura", "Almaty",
131
- "Novosibirsk" ],
132
- [ 23_400, "Rangoon" ],
133
- [ 25_200, "Bangkok", "Hanoi", "Jakarta", "Krasnoyarsk" ],
134
- [ 28_800, "Beijing", "Chongqing", "Hong Kong", "Urumqi",
135
- "Kuala Lumpur", "Singapore", "Taipei", "Perth", "Irkutsk",
136
- "Ulaan Bataar" ],
137
- [ 32_400, "Seoul", "Osaka", "Sapporo", "Tokyo", "Yakutsk" ],
138
- [ 34_200, "Darwin", "Adelaide" ],
139
- [ 36_000, "Canberra", "Melbourne", "Sydney", "Brisbane", "Hobart",
140
- "Vladivostok", "Guam", "Port Moresby" ],
141
- [ 39_600, "Magadan", "Solomon Is.", "New Caledonia" ],
142
- [ 43_200, "Fiji", "Kamchatka", "Marshall Is.", "Auckland",
143
- "Wellington" ],
144
- [ 46_800, "Nuku'alofa" ]].
145
- each do |offset, *places|
146
- places.each { |place| @@zones << create(place, offset).freeze }
147
- end
148
- @@zones.sort!
214
+ # Returns a TimeZone instance with the given name, or +nil+ if no
215
+ # such TimeZone instance exists. (This exists to support the use of
216
+ # this class with the +composed_of+ macro.)
217
+ def new(name)
218
+ self[name]
149
219
  end
150
- @@zones
151
- end
152
220
 
153
- # Locate a specific time zone object. If the argument is a string, it
154
- # is interpreted to mean the name of the timezone to locate. If it is a
155
- # numeric value it is either the hour offset, or the second offset, of the
156
- # timezone to find. (The first one with that offset will be returned.)
157
- # Returns +nil+ if no such time zone is known to the system.
158
- def [](arg)
159
- case arg
221
+ # Returns an array of all TimeZone objects. There are multiple
222
+ # TimeZone objects per time zone, in many cases, to make it easier
223
+ # for users to find their own time zone.
224
+ def all
225
+ @zones ||= zones_map.values.sort
226
+ end
227
+
228
+ # Locate a specific time zone object. If the argument is a string, it
229
+ # is interpreted to mean the name of the timezone to locate. If it is a
230
+ # numeric value it is either the hour offset, or the second offset, of the
231
+ # timezone to find. (The first one with that offset will be returned.)
232
+ # Returns +nil+ if no such time zone is known to the system.
233
+ def [](arg)
234
+ case arg
235
+ when self
236
+ arg
160
237
  when String
161
- all.find { |z| z.name == arg }
162
- when Numeric
238
+ begin
239
+ @lazy_zones_map[arg] ||= create(arg)
240
+ rescue TZInfo::InvalidTimezoneIdentifier
241
+ nil
242
+ end
243
+ when TZInfo::Timezone
244
+ @lazy_zones_map[arg.name] ||= create(arg.name, nil, arg)
245
+ when Numeric, ActiveSupport::Duration
163
246
  arg *= 3600 if arg.abs <= 13
164
247
  all.find { |z| z.utc_offset == arg.to_i }
165
248
  else
166
249
  raise ArgumentError, "invalid argument to TimeZone[]: #{arg.inspect}"
250
+ end
251
+ end
252
+
253
+ # A convenience method for returning a collection of TimeZone objects
254
+ # for time zones in the USA.
255
+ def us_zones
256
+ country_zones(:us)
257
+ end
258
+
259
+ # A convenience method for returning a collection of TimeZone objects
260
+ # for time zones in the country specified by its ISO 3166-1 Alpha2 code.
261
+ def country_zones(country_code)
262
+ code = country_code.to_s.upcase
263
+ @country_zones[code] ||= load_country_zones(code)
264
+ end
265
+
266
+ def clear # :nodoc:
267
+ @lazy_zones_map = Concurrent::Map.new
268
+ @country_zones = Concurrent::Map.new
269
+ @zones = nil
270
+ @zones_map = nil
271
+ end
272
+
273
+ private
274
+ def load_country_zones(code)
275
+ country = TZInfo::Country.get(code)
276
+ country.zone_identifiers.flat_map do |tz_id|
277
+ if MAPPING.value?(tz_id)
278
+ MAPPING.inject([]) do |memo, (key, value)|
279
+ memo << self[key] if value == tz_id
280
+ memo
281
+ end
282
+ else
283
+ create(tz_id, nil, TZInfo::Timezone.get(tz_id))
284
+ end
285
+ end.sort!
286
+ end
287
+
288
+ def zones_map
289
+ @zones_map ||= MAPPING.each_with_object({}) do |(name, _), zones|
290
+ timezone = self[name]
291
+ zones[name] = timezone if timezone
292
+ end
293
+ end
294
+ end
295
+
296
+ include Comparable
297
+ attr_reader :name
298
+ attr_reader :tzinfo
299
+
300
+ ##
301
+ # :singleton-method: create
302
+ # :call-seq: create(name, utc_offset = nil, tzinfo = nil)
303
+ #
304
+ # Create a new TimeZone object with the given name and offset. The
305
+ # offset is the number of seconds that this time zone is offset from UTC
306
+ # (GMT). Seconds were chosen as the offset unit because that is the unit
307
+ # that Ruby uses to represent time zone offsets (see Time#utc_offset).
308
+
309
+ # :stopdoc:
310
+ def initialize(name, utc_offset = nil, tzinfo = nil)
311
+ @name = name
312
+ @utc_offset = utc_offset
313
+ @tzinfo = tzinfo || TimeZone.find_tzinfo(name)
314
+ end
315
+ # :startdoc:
316
+
317
+ # Returns a standard time zone name defined by IANA
318
+ # https://www.iana.org/time-zones
319
+ def standard_name
320
+ MAPPING[name] || name
321
+ end
322
+
323
+ # Returns the offset of this time zone from UTC in seconds.
324
+ def utc_offset
325
+ @utc_offset || tzinfo&.current_period&.base_utc_offset
326
+ end
327
+
328
+ # Returns a formatted string of the offset from UTC, or an alternative
329
+ # string if the time zone is already UTC.
330
+ #
331
+ # zone = ActiveSupport::TimeZone['Central Time (US & Canada)']
332
+ # zone.formatted_offset # => "-06:00"
333
+ # zone.formatted_offset(false) # => "-0600"
334
+ def formatted_offset(colon = true, alternate_utc_string = nil)
335
+ utc_offset == 0 && alternate_utc_string || self.class.seconds_to_utc_offset(utc_offset, colon)
336
+ end
337
+
338
+ # Compare this time zone to the parameter. The two are compared first on
339
+ # their offsets, and then by name.
340
+ def <=>(zone)
341
+ return unless zone.respond_to? :utc_offset
342
+ result = (utc_offset <=> zone.utc_offset)
343
+ result = (name <=> zone.name) if result == 0
344
+ result
345
+ end
346
+
347
+ # Compare #name and TZInfo identifier to a supplied regexp, returning +true+
348
+ # if a match is found.
349
+ def =~(re)
350
+ re === name || re === MAPPING[name]
351
+ end
352
+
353
+ # Compare #name and TZInfo identifier to a supplied regexp, returning +true+
354
+ # if a match is found.
355
+ def match?(re)
356
+ (re == name) || (re == MAPPING[name]) ||
357
+ ((Regexp === re) && (re.match?(name) || re.match?(MAPPING[name])))
358
+ end
359
+
360
+ # Returns a textual representation of this time zone.
361
+ def to_s
362
+ "(GMT#{formatted_offset}) #{name}"
363
+ end
364
+
365
+ # \Method for creating new ActiveSupport::TimeWithZone instance in time zone
366
+ # of +self+ from given values.
367
+ #
368
+ # Time.zone = 'Hawaii' # => "Hawaii"
369
+ # Time.zone.local(2007, 2, 1, 15, 30, 45) # => Thu, 01 Feb 2007 15:30:45 HST -10:00
370
+ def local(*args)
371
+ time = Time.utc(*args)
372
+ ActiveSupport::TimeWithZone.new(nil, self, time)
373
+ end
374
+
375
+ # \Method for creating new ActiveSupport::TimeWithZone instance in time zone
376
+ # of +self+ from number of seconds since the Unix epoch.
377
+ #
378
+ # Time.zone = 'Hawaii' # => "Hawaii"
379
+ # Time.utc(2000).to_f # => 946684800.0
380
+ # Time.zone.at(946684800.0) # => Fri, 31 Dec 1999 14:00:00 HST -10:00
381
+ #
382
+ # A second argument can be supplied to specify sub-second precision.
383
+ #
384
+ # Time.zone = 'Hawaii' # => "Hawaii"
385
+ # Time.at(946684800, 123456.789).nsec # => 123456789
386
+ def at(*args)
387
+ Time.at(*args).utc.in_time_zone(self)
388
+ end
389
+
390
+ # \Method for creating new ActiveSupport::TimeWithZone instance in time zone
391
+ # of +self+ from an ISO 8601 string.
392
+ #
393
+ # Time.zone = 'Hawaii' # => "Hawaii"
394
+ # Time.zone.iso8601('1999-12-31T14:00:00') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
395
+ #
396
+ # If the time components are missing then they will be set to zero.
397
+ #
398
+ # Time.zone = 'Hawaii' # => "Hawaii"
399
+ # Time.zone.iso8601('1999-12-31') # => Fri, 31 Dec 1999 00:00:00 HST -10:00
400
+ #
401
+ # If the string is invalid then an +ArgumentError+ will be raised unlike +parse+
402
+ # which usually returns +nil+ when given an invalid date string.
403
+ def iso8601(str)
404
+ # Historically `Date._iso8601(nil)` returns `{}`, but in the `date` gem versions `3.2.1`, `3.1.2`, `3.0.2`,
405
+ # and `2.0.1`, `Date._iso8601(nil)` raises `TypeError` https://github.com/ruby/date/issues/39
406
+ # Future `date` releases are expected to revert back to the original behavior.
407
+ raise ArgumentError, "invalid date" if str.nil?
408
+
409
+ parts = Date._iso8601(str)
410
+
411
+ year = parts.fetch(:year)
412
+
413
+ if parts.key?(:yday)
414
+ ordinal_date = Date.ordinal(year, parts.fetch(:yday))
415
+ month = ordinal_date.month
416
+ day = ordinal_date.day
417
+ else
418
+ month = parts.fetch(:mon)
419
+ day = parts.fetch(:mday)
420
+ end
421
+
422
+ time = Time.new(
423
+ year,
424
+ month,
425
+ day,
426
+ parts.fetch(:hour, 0),
427
+ parts.fetch(:min, 0),
428
+ parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
429
+ parts.fetch(:offset, 0)
430
+ )
431
+
432
+ if parts[:offset]
433
+ TimeWithZone.new(time.utc, self)
434
+ else
435
+ TimeWithZone.new(nil, self, time)
436
+ end
437
+
438
+ rescue Date::Error, KeyError
439
+ raise ArgumentError, "invalid date"
440
+ end
441
+
442
+ # \Method for creating new ActiveSupport::TimeWithZone instance in time zone
443
+ # of +self+ from parsed string.
444
+ #
445
+ # Time.zone = 'Hawaii' # => "Hawaii"
446
+ # Time.zone.parse('1999-12-31 14:00:00') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
447
+ #
448
+ # If upper components are missing from the string, they are supplied from
449
+ # TimeZone#now:
450
+ #
451
+ # Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00
452
+ # Time.zone.parse('22:30:00') # => Fri, 31 Dec 1999 22:30:00 HST -10:00
453
+ #
454
+ # However, if the date component is not provided, but any other upper
455
+ # components are supplied, then the day of the month defaults to 1:
456
+ #
457
+ # Time.zone.parse('Mar 2000') # => Wed, 01 Mar 2000 00:00:00 HST -10:00
458
+ #
459
+ # If the string is invalid then an +ArgumentError+ could be raised.
460
+ def parse(str, now = now())
461
+ parts_to_time(Date._parse(str, false), now)
462
+ end
463
+
464
+ # \Method for creating new ActiveSupport::TimeWithZone instance in time zone
465
+ # of +self+ from an RFC 3339 string.
466
+ #
467
+ # Time.zone = 'Hawaii' # => "Hawaii"
468
+ # Time.zone.rfc3339('2000-01-01T00:00:00Z') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
469
+ #
470
+ # If the time or zone components are missing then an +ArgumentError+ will
471
+ # be raised. This is much stricter than either +parse+ or +iso8601+ which
472
+ # allow for missing components.
473
+ #
474
+ # Time.zone = 'Hawaii' # => "Hawaii"
475
+ # Time.zone.rfc3339('1999-12-31') # => ArgumentError: invalid date
476
+ def rfc3339(str)
477
+ parts = Date._rfc3339(str)
478
+
479
+ raise ArgumentError, "invalid date" if parts.empty?
480
+
481
+ time = Time.new(
482
+ parts.fetch(:year),
483
+ parts.fetch(:mon),
484
+ parts.fetch(:mday),
485
+ parts.fetch(:hour),
486
+ parts.fetch(:min),
487
+ parts.fetch(:sec) + parts.fetch(:sec_fraction, 0),
488
+ parts.fetch(:offset)
489
+ )
490
+
491
+ TimeWithZone.new(time.utc, self)
492
+ end
493
+
494
+ # Parses +str+ according to +format+ and returns an ActiveSupport::TimeWithZone.
495
+ #
496
+ # Assumes that +str+ is a time in the time zone +self+,
497
+ # unless +format+ includes an explicit time zone.
498
+ # (This is the same behavior as +parse+.)
499
+ # In either case, the returned TimeWithZone has the timezone of +self+.
500
+ #
501
+ # Time.zone = 'Hawaii' # => "Hawaii"
502
+ # Time.zone.strptime('1999-12-31 14:00:00', '%Y-%m-%d %H:%M:%S') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
503
+ #
504
+ # If upper components are missing from the string, they are supplied from
505
+ # TimeZone#now:
506
+ #
507
+ # Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00
508
+ # Time.zone.strptime('22:30:00', '%H:%M:%S') # => Fri, 31 Dec 1999 22:30:00 HST -10:00
509
+ #
510
+ # However, if the date component is not provided, but any other upper
511
+ # components are supplied, then the day of the month defaults to 1:
512
+ #
513
+ # Time.zone.strptime('Mar 2000', '%b %Y') # => Wed, 01 Mar 2000 00:00:00 HST -10:00
514
+ def strptime(str, format, now = now())
515
+ parts_to_time(DateTime._strptime(str, format), now)
516
+ end
517
+
518
+ # Returns an ActiveSupport::TimeWithZone instance representing the current
519
+ # time in the time zone represented by +self+.
520
+ #
521
+ # Time.zone = 'Hawaii' # => "Hawaii"
522
+ # Time.zone.now # => Wed, 23 Jan 2008 20:24:27 HST -10:00
523
+ def now
524
+ time_now.utc.in_time_zone(self)
525
+ end
526
+
527
+ # Returns the current date in this time zone.
528
+ def today
529
+ tzinfo.now.to_date
530
+ end
531
+
532
+ # Returns the next date in this time zone.
533
+ def tomorrow
534
+ today + 1
535
+ end
536
+
537
+ # Returns the previous date in this time zone.
538
+ def yesterday
539
+ today - 1
540
+ end
541
+
542
+ # Adjust the given time to the simultaneous time in the time zone
543
+ # represented by +self+. Returns a local time with the appropriate offset
544
+ # -- if you want an ActiveSupport::TimeWithZone instance, use
545
+ # Time#in_time_zone() instead.
546
+ #
547
+ # As of tzinfo 2, utc_to_local returns a Time with a non-zero utc_offset.
548
+ # See the +utc_to_local_returns_utc_offset_times+ config for more info.
549
+ def utc_to_local(time)
550
+ tzinfo.utc_to_local(time).yield_self do |t|
551
+ ActiveSupport.utc_to_local_returns_utc_offset_times ?
552
+ t : Time.utc(t.year, t.month, t.day, t.hour, t.min, t.sec, t.sec_fraction * 1_000_000)
167
553
  end
168
554
  end
169
555
 
170
- # A regular expression that matches the names of all time zones in
171
- # the USA.
172
- US_ZONES = /US|Arizona|Indiana|Hawaii|Alaska/
556
+ # Adjust the given time to the simultaneous time in UTC. Returns a
557
+ # Time.utc() instance.
558
+ def local_to_utc(time, dst = true)
559
+ tzinfo.local_to_utc(time, dst)
560
+ end
561
+
562
+ def period_for_utc(time) # :nodoc:
563
+ tzinfo.period_for_utc(time)
564
+ end
565
+
566
+ def period_for_local(time, dst = true) # :nodoc:
567
+ tzinfo.period_for_local(time, dst) { |periods| periods.last }
568
+ end
569
+
570
+ def periods_for_local(time) # :nodoc:
571
+ tzinfo.periods_for_local(time)
572
+ end
573
+
574
+ def abbr(time) # :nodoc:
575
+ tzinfo.abbr(time)
576
+ end
577
+
578
+ def dst?(time) # :nodoc:
579
+ tzinfo.dst?(time)
580
+ end
173
581
 
174
- # A convenience method for returning a collection of TimeZone objects
175
- # for time zones in the USA.
176
- def us_zones
177
- all.find_all { |z| z.name =~ US_ZONES }
582
+ def init_with(coder) # :nodoc:
583
+ initialize(coder["name"])
178
584
  end
585
+
586
+ def encode_with(coder) # :nodoc:
587
+ coder.tag = "!ruby/object:#{self.class}"
588
+ coder.map = { "name" => tzinfo.name }
589
+ end
590
+
591
+ private
592
+ def parts_to_time(parts, now)
593
+ raise ArgumentError, "invalid date" if parts.nil?
594
+ return if parts.empty?
595
+
596
+ if parts[:seconds]
597
+ time = Time.at(parts[:seconds])
598
+ else
599
+ time = Time.new(
600
+ parts.fetch(:year, now.year),
601
+ parts.fetch(:mon, now.month),
602
+ parts.fetch(:mday, parts[:year] || parts[:mon] ? 1 : now.day),
603
+ parts.fetch(:hour, 0),
604
+ parts.fetch(:min, 0),
605
+ parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
606
+ parts.fetch(:offset, 0)
607
+ )
608
+ end
609
+
610
+ if parts[:offset] || parts[:seconds]
611
+ TimeWithZone.new(time.utc, self)
612
+ else
613
+ TimeWithZone.new(nil, self, time)
614
+ end
615
+ end
616
+
617
+ def time_now
618
+ Time.now
619
+ end
179
620
  end
180
621
  end