activesupport 2.0.5 → 2.1.0

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

Potentially problematic release.


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

Files changed (236) hide show
  1. data/CHANGELOG +183 -5
  2. data/lib/active_support.rb +6 -2
  3. data/lib/active_support/base64.rb +5 -0
  4. data/lib/active_support/basic_object.rb +23 -4
  5. data/lib/active_support/buffered_logger.rb +17 -3
  6. data/lib/active_support/cache.rb +145 -0
  7. data/lib/active_support/cache/compressed_mem_cache_store.rb +15 -0
  8. data/lib/active_support/cache/drb_store.rb +15 -0
  9. data/lib/active_support/cache/file_store.rb +70 -0
  10. data/lib/active_support/cache/mem_cache_store.rb +100 -0
  11. data/lib/active_support/cache/memory_store.rb +38 -0
  12. data/lib/active_support/callbacks.rb +275 -0
  13. data/lib/active_support/core_ext/array/access.rb +2 -4
  14. data/lib/active_support/core_ext/array/conversions.rb +89 -5
  15. data/lib/active_support/core_ext/array/extract_options.rb +2 -1
  16. data/lib/active_support/core_ext/array/grouping.rb +3 -8
  17. data/lib/active_support/core_ext/array/random_access.rb +1 -1
  18. data/lib/active_support/core_ext/base64.rb +4 -0
  19. data/lib/active_support/core_ext/base64/encoding.rb +13 -0
  20. data/lib/active_support/core_ext/benchmark.rb +12 -0
  21. data/lib/active_support/core_ext/bigdecimal.rb +4 -0
  22. data/lib/active_support/core_ext/bigdecimal/conversions.rb +39 -4
  23. data/lib/active_support/core_ext/blank.rb +5 -2
  24. data/lib/active_support/core_ext/class/attribute_accessors.rb +7 -1
  25. data/lib/active_support/core_ext/class/delegating_attributes.rb +7 -1
  26. data/lib/active_support/core_ext/class/inheritable_attributes.rb +1 -1
  27. data/lib/active_support/core_ext/class/removal.rb +26 -0
  28. data/lib/active_support/core_ext/date/calculations.rb +28 -1
  29. data/lib/active_support/core_ext/date/conversions.rb +1 -0
  30. data/lib/active_support/core_ext/date_time.rb +2 -0
  31. data/lib/active_support/core_ext/date_time/calculations.rb +37 -2
  32. data/lib/active_support/core_ext/date_time/conversions.rb +27 -14
  33. data/lib/active_support/core_ext/enumerable.rb +16 -9
  34. data/lib/active_support/core_ext/exception.rb +8 -0
  35. data/lib/active_support/core_ext/file.rb +6 -6
  36. data/lib/active_support/core_ext/hash/conversions.rb +26 -8
  37. data/lib/active_support/core_ext/hash/indifferent_access.rb +35 -0
  38. data/lib/active_support/core_ext/hash/reverse_merge.rb +4 -1
  39. data/lib/active_support/core_ext/integer/even_odd.rb +10 -5
  40. data/lib/active_support/core_ext/integer/inflections.rb +0 -1
  41. data/lib/active_support/core_ext/kernel/daemonizing.rb +2 -10
  42. data/lib/active_support/core_ext/kernel/reporting.rb +8 -0
  43. data/lib/active_support/core_ext/module/attr_internal.rb +4 -3
  44. data/lib/active_support/core_ext/module/attribute_accessors.rb +11 -1
  45. data/lib/active_support/core_ext/module/delegation.rb +5 -3
  46. data/lib/active_support/core_ext/module/inclusion.rb +19 -0
  47. data/lib/active_support/core_ext/module/introspection.rb +50 -16
  48. data/lib/active_support/core_ext/module/loading.rb +10 -0
  49. data/lib/active_support/core_ext/numeric.rb +3 -1
  50. data/lib/active_support/core_ext/numeric/conversions.rb +19 -0
  51. data/lib/active_support/core_ext/object/instance_variables.rb +52 -0
  52. data/lib/active_support/core_ext/object/misc.rb +1 -1
  53. data/lib/active_support/core_ext/process.rb +1 -0
  54. data/lib/active_support/core_ext/process/daemon.rb +25 -0
  55. data/lib/active_support/core_ext/range/conversions.rb +5 -1
  56. data/lib/active_support/core_ext/range/include_range.rb +8 -0
  57. data/lib/active_support/core_ext/range/overlaps.rb +3 -0
  58. data/lib/active_support/core_ext/string.rb +5 -10
  59. data/lib/active_support/core_ext/string/access.rb +72 -48
  60. data/lib/active_support/core_ext/string/conversions.rb +4 -4
  61. data/lib/active_support/core_ext/string/filters.rb +26 -0
  62. data/lib/active_support/core_ext/string/inflections.rb +56 -64
  63. data/lib/active_support/core_ext/string/iterators.rb +4 -0
  64. data/lib/active_support/core_ext/string/starts_ends_with.rb +12 -4
  65. data/lib/active_support/core_ext/string/unicode.rb +14 -7
  66. data/lib/active_support/core_ext/symbol.rb +1 -1
  67. data/lib/active_support/core_ext/time.rb +2 -0
  68. data/lib/active_support/core_ext/time/calculations.rb +75 -23
  69. data/lib/active_support/core_ext/time/conversions.rb +22 -35
  70. data/lib/active_support/core_ext/time/zones.rb +86 -0
  71. data/lib/active_support/dependencies.rb +92 -80
  72. data/lib/active_support/deprecation.rb +2 -16
  73. data/lib/active_support/duration.rb +4 -4
  74. data/lib/active_support/gzip.rb +25 -0
  75. data/lib/active_support/inflector.rb +92 -69
  76. data/lib/active_support/json.rb +17 -25
  77. data/lib/active_support/json/decoding.rb +1 -1
  78. data/lib/active_support/json/encoders/date.rb +11 -2
  79. data/lib/active_support/json/encoders/date_time.rb +11 -2
  80. data/lib/active_support/json/encoders/enumerable.rb +2 -2
  81. data/lib/active_support/json/encoders/hash.rb +3 -6
  82. data/lib/active_support/json/encoders/object.rb +1 -1
  83. data/lib/active_support/json/encoders/string.rb +8 -2
  84. data/lib/active_support/json/encoders/time.rb +11 -2
  85. data/lib/active_support/json/encoding.rb +0 -1
  86. data/lib/active_support/multibyte/chars.rb +8 -6
  87. data/lib/active_support/multibyte/handlers/utf8_handler.rb +11 -11
  88. data/lib/active_support/ordered_hash.rb +43 -0
  89. data/lib/active_support/ordered_options.rb +0 -38
  90. data/lib/active_support/test_case.rb +10 -2
  91. data/lib/active_support/testing/default.rb +3 -6
  92. data/lib/active_support/testing/setup_and_teardown.rb +93 -0
  93. data/lib/active_support/time_with_zone.rb +283 -0
  94. data/lib/active_support/values/time_zone.rb +336 -123
  95. data/lib/active_support/vendor.rb +12 -0
  96. data/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb +849 -0
  97. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo.rb +33 -0
  98. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/data_timezone.rb +47 -0
  99. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/data_timezone_info.rb +226 -0
  100. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Africa/Algiers.rb +55 -0
  101. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Africa/Cairo.rb +219 -0
  102. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Africa/Casablanca.rb +38 -0
  103. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Africa/Harare.rb +18 -0
  104. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Africa/Johannesburg.rb +25 -0
  105. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Africa/Monrovia.rb +22 -0
  106. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Africa/Nairobi.rb +23 -0
  107. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Argentina/Buenos_Aires.rb +166 -0
  108. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Argentina/San_Juan.rb +170 -0
  109. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Bogota.rb +23 -0
  110. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Caracas.rb +23 -0
  111. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Chicago.rb +283 -0
  112. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Chihuahua.rb +136 -0
  113. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Denver.rb +204 -0
  114. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Godthab.rb +161 -0
  115. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Guatemala.rb +27 -0
  116. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Halifax.rb +274 -0
  117. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Indiana/Indianapolis.rb +149 -0
  118. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Juneau.rb +194 -0
  119. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/La_Paz.rb +22 -0
  120. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Lima.rb +35 -0
  121. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Los_Angeles.rb +232 -0
  122. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Mazatlan.rb +139 -0
  123. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Mexico_City.rb +144 -0
  124. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Monterrey.rb +131 -0
  125. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/New_York.rb +282 -0
  126. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Phoenix.rb +30 -0
  127. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Regina.rb +74 -0
  128. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Santiago.rb +205 -0
  129. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/St_Johns.rb +288 -0
  130. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Tijuana.rb +196 -0
  131. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Almaty.rb +67 -0
  132. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Baghdad.rb +73 -0
  133. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Baku.rb +161 -0
  134. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Bangkok.rb +20 -0
  135. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Chongqing.rb +33 -0
  136. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Dhaka.rb +27 -0
  137. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Hong_Kong.rb +87 -0
  138. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Irkutsk.rb +165 -0
  139. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Jakarta.rb +30 -0
  140. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Jerusalem.rb +163 -0
  141. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Kabul.rb +20 -0
  142. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Kamchatka.rb +163 -0
  143. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Karachi.rb +28 -0
  144. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Katmandu.rb +20 -0
  145. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Kolkata.rb +25 -0
  146. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Krasnoyarsk.rb +163 -0
  147. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Kuala_Lumpur.rb +31 -0
  148. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Kuwait.rb +18 -0
  149. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Magadan.rb +163 -0
  150. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Muscat.rb +18 -0
  151. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Novosibirsk.rb +164 -0
  152. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Rangoon.rb +24 -0
  153. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Riyadh.rb +18 -0
  154. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Seoul.rb +34 -0
  155. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Shanghai.rb +35 -0
  156. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Singapore.rb +33 -0
  157. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Taipei.rb +59 -0
  158. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Tashkent.rb +47 -0
  159. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Tbilisi.rb +78 -0
  160. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Tehran.rb +121 -0
  161. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Tokyo.rb +30 -0
  162. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Ulaanbaatar.rb +65 -0
  163. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Urumqi.rb +33 -0
  164. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Vladivostok.rb +164 -0
  165. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Yakutsk.rb +163 -0
  166. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Yekaterinburg.rb +165 -0
  167. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Yerevan.rb +165 -0
  168. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Atlantic/Azores.rb +270 -0
  169. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Atlantic/Cape_Verde.rb +23 -0
  170. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Atlantic/South_Georgia.rb +18 -0
  171. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Australia/Adelaide.rb +187 -0
  172. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Australia/Brisbane.rb +35 -0
  173. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Australia/Darwin.rb +29 -0
  174. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Australia/Hobart.rb +193 -0
  175. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Australia/Melbourne.rb +185 -0
  176. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Australia/Perth.rb +37 -0
  177. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Australia/Sydney.rb +185 -0
  178. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Etc/UTC.rb +16 -0
  179. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Amsterdam.rb +228 -0
  180. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Athens.rb +185 -0
  181. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Belgrade.rb +163 -0
  182. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Berlin.rb +188 -0
  183. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Bratislava.rb +13 -0
  184. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Brussels.rb +232 -0
  185. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Bucharest.rb +181 -0
  186. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Budapest.rb +197 -0
  187. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Copenhagen.rb +179 -0
  188. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Dublin.rb +276 -0
  189. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Helsinki.rb +163 -0
  190. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Istanbul.rb +218 -0
  191. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Kiev.rb +168 -0
  192. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Lisbon.rb +268 -0
  193. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Ljubljana.rb +13 -0
  194. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/London.rb +288 -0
  195. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Madrid.rb +211 -0
  196. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Minsk.rb +170 -0
  197. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Moscow.rb +181 -0
  198. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Paris.rb +232 -0
  199. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Prague.rb +187 -0
  200. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Riga.rb +176 -0
  201. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Rome.rb +215 -0
  202. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Sarajevo.rb +13 -0
  203. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Skopje.rb +13 -0
  204. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Sofia.rb +173 -0
  205. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Stockholm.rb +165 -0
  206. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Tallinn.rb +172 -0
  207. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Vienna.rb +183 -0
  208. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Vilnius.rb +170 -0
  209. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Warsaw.rb +212 -0
  210. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Zagreb.rb +13 -0
  211. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Auckland.rb +202 -0
  212. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Fiji.rb +23 -0
  213. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Guam.rb +22 -0
  214. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Honolulu.rb +28 -0
  215. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Majuro.rb +20 -0
  216. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Midway.rb +25 -0
  217. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Noumea.rb +25 -0
  218. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Pago_Pago.rb +26 -0
  219. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Port_Moresby.rb +20 -0
  220. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Tongatapu.rb +27 -0
  221. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/info_timezone.rb +52 -0
  222. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/linked_timezone.rb +51 -0
  223. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/linked_timezone_info.rb +44 -0
  224. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/offset_rationals.rb +95 -0
  225. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/time_or_datetime.rb +292 -0
  226. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/timezone.rb +508 -0
  227. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/timezone_definition.rb +56 -0
  228. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/timezone_info.rb +40 -0
  229. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/timezone_offset_info.rb +94 -0
  230. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/timezone_period.rb +198 -0
  231. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/timezone_transition_info.rb +138 -0
  232. data/lib/active_support/version.rb +2 -2
  233. data/lib/active_support/whiny_nil.rb +30 -10
  234. metadata +175 -5
  235. data/lib/active_support/core_ext/rexml.rb +0 -38
  236. data/lib/active_support/testing.rb +0 -1
@@ -0,0 +1,292 @@
1
+ #--
2
+ # Copyright (c) 2006 Philip Ross
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #++
22
+
23
+ require 'date'
24
+ require 'time'
25
+ require 'tzinfo/offset_rationals'
26
+
27
+ module TZInfo
28
+ # Used by TZInfo internally to represent either a Time, DateTime or integer
29
+ # timestamp (seconds since 1970-01-01 00:00:00).
30
+ class TimeOrDateTime #:nodoc:
31
+ include Comparable
32
+
33
+ # Constructs a new TimeOrDateTime. timeOrDateTime can be a Time, DateTime
34
+ # or an integer. If using a Time or DateTime, any time zone information is
35
+ # ignored.
36
+ def initialize(timeOrDateTime)
37
+ @time = nil
38
+ @datetime = nil
39
+ @timestamp = nil
40
+
41
+ if timeOrDateTime.is_a?(Time)
42
+ @time = timeOrDateTime
43
+ @time = Time.utc(@time.year, @time.mon, @time.mday, @time.hour, @time.min, @time.sec) unless @time.zone == 'UTC'
44
+ @orig = @time
45
+ elsif timeOrDateTime.is_a?(DateTime)
46
+ @datetime = timeOrDateTime
47
+ @datetime = @datetime.new_offset(0) unless @datetime.offset == 0
48
+ @orig = @datetime
49
+ else
50
+ @timestamp = timeOrDateTime.to_i
51
+ @orig = @timestamp
52
+ end
53
+ end
54
+
55
+ # Returns the time as a Time.
56
+ def to_time
57
+ unless @time
58
+ if @timestamp
59
+ @time = Time.at(@timestamp).utc
60
+ else
61
+ @time = Time.utc(year, mon, mday, hour, min, sec)
62
+ end
63
+ end
64
+
65
+ @time
66
+ end
67
+
68
+ # Returns the time as a DateTime.
69
+ def to_datetime
70
+ unless @datetime
71
+ @datetime = DateTime.new(year, mon, mday, hour, min, sec)
72
+ end
73
+
74
+ @datetime
75
+ end
76
+
77
+ # Returns the time as an integer timestamp.
78
+ def to_i
79
+ unless @timestamp
80
+ @timestamp = to_time.to_i
81
+ end
82
+
83
+ @timestamp
84
+ end
85
+
86
+ # Returns the time as the original time passed to new.
87
+ def to_orig
88
+ @orig
89
+ end
90
+
91
+ # Returns a string representation of the TimeOrDateTime.
92
+ def to_s
93
+ if @orig.is_a?(Time)
94
+ "Time: #{@orig.to_s}"
95
+ elsif @orig.is_a?(DateTime)
96
+ "DateTime: #{@orig.to_s}"
97
+ else
98
+ "Timestamp: #{@orig.to_s}"
99
+ end
100
+ end
101
+
102
+ # Returns internal object state as a programmer-readable string.
103
+ def inspect
104
+ "#<#{self.class}: #{@orig.inspect}>"
105
+ end
106
+
107
+ # Returns the year.
108
+ def year
109
+ if @time
110
+ @time.year
111
+ elsif @datetime
112
+ @datetime.year
113
+ else
114
+ to_time.year
115
+ end
116
+ end
117
+
118
+ # Returns the month of the year (1..12).
119
+ def mon
120
+ if @time
121
+ @time.mon
122
+ elsif @datetime
123
+ @datetime.mon
124
+ else
125
+ to_time.mon
126
+ end
127
+ end
128
+ alias :month :mon
129
+
130
+ # Returns the day of the month (1..n).
131
+ def mday
132
+ if @time
133
+ @time.mday
134
+ elsif @datetime
135
+ @datetime.mday
136
+ else
137
+ to_time.mday
138
+ end
139
+ end
140
+ alias :day :mday
141
+
142
+ # Returns the hour of the day (0..23).
143
+ def hour
144
+ if @time
145
+ @time.hour
146
+ elsif @datetime
147
+ @datetime.hour
148
+ else
149
+ to_time.hour
150
+ end
151
+ end
152
+
153
+ # Returns the minute of the hour (0..59).
154
+ def min
155
+ if @time
156
+ @time.min
157
+ elsif @datetime
158
+ @datetime.min
159
+ else
160
+ to_time.min
161
+ end
162
+ end
163
+
164
+ # Returns the second of the minute (0..60). (60 for a leap second).
165
+ def sec
166
+ if @time
167
+ @time.sec
168
+ elsif @datetime
169
+ @datetime.sec
170
+ else
171
+ to_time.sec
172
+ end
173
+ end
174
+
175
+ # Compares this TimeOrDateTime with another Time, DateTime, integer
176
+ # timestamp or TimeOrDateTime. Returns -1, 0 or +1 depending whether the
177
+ # receiver is less than, equal to, or greater than timeOrDateTime.
178
+ #
179
+ # Milliseconds and smaller units are ignored in the comparison.
180
+ def <=>(timeOrDateTime)
181
+ if timeOrDateTime.is_a?(TimeOrDateTime)
182
+ orig = timeOrDateTime.to_orig
183
+
184
+ if @orig.is_a?(DateTime) || orig.is_a?(DateTime)
185
+ # If either is a DateTime, assume it is there for a reason
186
+ # (i.e. for range).
187
+ to_datetime <=> timeOrDateTime.to_datetime
188
+ elsif orig.is_a?(Time)
189
+ to_time <=> timeOrDateTime.to_time
190
+ else
191
+ to_i <=> timeOrDateTime.to_i
192
+ end
193
+ elsif @orig.is_a?(DateTime) || timeOrDateTime.is_a?(DateTime)
194
+ # If either is a DateTime, assume it is there for a reason
195
+ # (i.e. for range).
196
+ to_datetime <=> TimeOrDateTime.wrap(timeOrDateTime).to_datetime
197
+ elsif timeOrDateTime.is_a?(Time)
198
+ to_time <=> timeOrDateTime
199
+ else
200
+ to_i <=> timeOrDateTime.to_i
201
+ end
202
+ end
203
+
204
+ # Adds a number of seconds to the TimeOrDateTime. Returns a new
205
+ # TimeOrDateTime, preserving what the original constructed type was.
206
+ # If the original type is a Time and the resulting calculation goes out of
207
+ # range for Times, then an exception will be raised by the Time class.
208
+ def +(seconds)
209
+ if seconds == 0
210
+ self
211
+ else
212
+ if @orig.is_a?(DateTime)
213
+ TimeOrDateTime.new(@orig + OffsetRationals.rational_for_offset(seconds))
214
+ else
215
+ # + defined for Time and integer timestamps
216
+ TimeOrDateTime.new(@orig + seconds)
217
+ end
218
+ end
219
+ end
220
+
221
+ # Subtracts a number of seconds from the TimeOrDateTime. Returns a new
222
+ # TimeOrDateTime, preserving what the original constructed type was.
223
+ # If the original type is a Time and the resulting calculation goes out of
224
+ # range for Times, then an exception will be raised by the Time class.
225
+ def -(seconds)
226
+ self + (-seconds)
227
+ end
228
+
229
+ # Similar to the + operator, but for cases where adding would cause a
230
+ # timestamp or time to go out of the allowed range, converts to a DateTime
231
+ # based TimeOrDateTime.
232
+ def add_with_convert(seconds)
233
+ if seconds == 0
234
+ self
235
+ else
236
+ if @orig.is_a?(DateTime)
237
+ TimeOrDateTime.new(@orig + OffsetRationals.rational_for_offset(seconds))
238
+ else
239
+ # A Time or timestamp.
240
+ result = to_i + seconds
241
+
242
+ if result < 0 || result > 2147483647
243
+ result = TimeOrDateTime.new(to_datetime + OffsetRationals.rational_for_offset(seconds))
244
+ else
245
+ result = TimeOrDateTime.new(@orig + seconds)
246
+ end
247
+ end
248
+ end
249
+ end
250
+
251
+ # Returns true if todt represents the same time and was originally
252
+ # constructed with the same type (DateTime, Time or timestamp) as this
253
+ # TimeOrDateTime.
254
+ def eql?(todt)
255
+ todt.respond_to?(:to_orig) && to_orig.eql?(todt.to_orig)
256
+ end
257
+
258
+ # Returns a hash of this TimeOrDateTime.
259
+ def hash
260
+ @orig.hash
261
+ end
262
+
263
+ # If no block is given, returns a TimeOrDateTime wrapping the given
264
+ # timeOrDateTime. If a block is specified, a TimeOrDateTime is constructed
265
+ # and passed to the block. The result of the block must be a TimeOrDateTime.
266
+ # to_orig will be called on the result and the result of to_orig will be
267
+ # returned.
268
+ #
269
+ # timeOrDateTime can be a Time, DateTime, integer timestamp or TimeOrDateTime.
270
+ # If a TimeOrDateTime is passed in, no new TimeOrDateTime will be constructed,
271
+ # the passed in value will be used.
272
+ def self.wrap(timeOrDateTime)
273
+ t = timeOrDateTime.is_a?(TimeOrDateTime) ? timeOrDateTime : TimeOrDateTime.new(timeOrDateTime)
274
+
275
+ if block_given?
276
+ t = yield t
277
+
278
+ if timeOrDateTime.is_a?(TimeOrDateTime)
279
+ t
280
+ elsif timeOrDateTime.is_a?(Time)
281
+ t.to_time
282
+ elsif timeOrDateTime.is_a?(DateTime)
283
+ t.to_datetime
284
+ else
285
+ t.to_i
286
+ end
287
+ else
288
+ t
289
+ end
290
+ end
291
+ end
292
+ end
@@ -0,0 +1,508 @@
1
+ #--
2
+ # Copyright (c) 2005-2006 Philip Ross
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #++
22
+
23
+ require 'date'
24
+ # require 'tzinfo/country'
25
+ require 'tzinfo/time_or_datetime'
26
+ require 'tzinfo/timezone_period'
27
+
28
+ module TZInfo
29
+ # Indicate a specified time in a local timezone has more than one
30
+ # possible time in UTC. This happens when switching from daylight savings time
31
+ # to normal time where the clocks are rolled back. Thrown by period_for_local
32
+ # and local_to_utc when using an ambiguous time and not specifying any
33
+ # means to resolve the ambiguity.
34
+ class AmbiguousTime < StandardError
35
+ end
36
+
37
+ # Thrown to indicate that no TimezonePeriod matching a given time could be found.
38
+ class PeriodNotFound < StandardError
39
+ end
40
+
41
+ # Thrown by Timezone#get if the identifier given is not valid.
42
+ class InvalidTimezoneIdentifier < StandardError
43
+ end
44
+
45
+ # Thrown if an attempt is made to use a timezone created with Timezone.new(nil).
46
+ class UnknownTimezone < StandardError
47
+ end
48
+
49
+ # Timezone is the base class of all timezones. It provides a factory method
50
+ # get to access timezones by identifier. Once a specific Timezone has been
51
+ # retrieved, DateTimes, Times and timestamps can be converted between the UTC
52
+ # and the local time for the zone. For example:
53
+ #
54
+ # tz = TZInfo::Timezone.get('America/New_York')
55
+ # puts tz.utc_to_local(DateTime.new(2005,8,29,15,35,0)).to_s
56
+ # puts tz.local_to_utc(Time.utc(2005,8,29,11,35,0)).to_s
57
+ # puts tz.utc_to_local(1125315300).to_s
58
+ #
59
+ # Each time conversion method returns an object of the same type it was
60
+ # passed.
61
+ #
62
+ # The timezone information all comes from the tz database
63
+ # (see http://www.twinsun.com/tz/tz-link.htm)
64
+ class Timezone
65
+ include Comparable
66
+
67
+ # Cache of loaded zones by identifier to avoid using require if a zone
68
+ # has already been loaded.
69
+ @@loaded_zones = {}
70
+
71
+ # Whether the timezones index has been loaded yet.
72
+ @@index_loaded = false
73
+
74
+ # Returns a timezone by its identifier (e.g. "Europe/London",
75
+ # "America/Chicago" or "UTC").
76
+ #
77
+ # Raises InvalidTimezoneIdentifier if the timezone couldn't be found.
78
+ def self.get(identifier)
79
+ instance = @@loaded_zones[identifier]
80
+ unless instance
81
+ raise InvalidTimezoneIdentifier, 'Invalid identifier' if identifier !~ /^[A-z0-9\+\-_]+(\/[A-z0-9\+\-_]+)*$/
82
+ identifier = identifier.gsub(/-/, '__m__').gsub(/\+/, '__p__')
83
+ begin
84
+ # Use a temporary variable to avoid an rdoc warning
85
+ file = "tzinfo/definitions/#{identifier}"
86
+ require file
87
+
88
+ m = Definitions
89
+ identifier.split(/\//).each {|part|
90
+ m = m.const_get(part)
91
+ }
92
+
93
+ info = m.get
94
+
95
+ # Could make Timezone subclasses register an interest in an info
96
+ # type. Since there are currently only two however, there isn't
97
+ # much point.
98
+ if info.kind_of?(DataTimezoneInfo)
99
+ instance = DataTimezone.new(info)
100
+ elsif info.kind_of?(LinkedTimezoneInfo)
101
+ instance = LinkedTimezone.new(info)
102
+ else
103
+ raise InvalidTimezoneIdentifier, "No handler for info type #{info.class}"
104
+ end
105
+
106
+ @@loaded_zones[instance.identifier] = instance
107
+ rescue LoadError, NameError => e
108
+ raise InvalidTimezoneIdentifier, e.message
109
+ end
110
+ end
111
+
112
+ instance
113
+ end
114
+
115
+ # Returns a proxy for the Timezone with the given identifier. The proxy
116
+ # will cause the real timezone to be loaded when an attempt is made to
117
+ # find a period or convert a time. get_proxy will not validate the
118
+ # identifier. If an invalid identifier is specified, no exception will be
119
+ # raised until the proxy is used.
120
+ def self.get_proxy(identifier)
121
+ TimezoneProxy.new(identifier)
122
+ end
123
+
124
+ # If identifier is nil calls super(), otherwise calls get. An identfier
125
+ # should always be passed in when called externally.
126
+ def self.new(identifier = nil)
127
+ if identifier
128
+ get(identifier)
129
+ else
130
+ super()
131
+ end
132
+ end
133
+
134
+ # Returns an array containing all the available Timezones.
135
+ #
136
+ # Returns TimezoneProxy objects to avoid the overhead of loading Timezone
137
+ # definitions until a conversion is actually required.
138
+ def self.all
139
+ get_proxies(all_identifiers)
140
+ end
141
+
142
+ # Returns an array containing the identifiers of all the available
143
+ # Timezones.
144
+ def self.all_identifiers
145
+ load_index
146
+ Indexes::Timezones.timezones
147
+ end
148
+
149
+ # Returns an array containing all the available Timezones that are based
150
+ # on data (are not links to other Timezones).
151
+ #
152
+ # Returns TimezoneProxy objects to avoid the overhead of loading Timezone
153
+ # definitions until a conversion is actually required.
154
+ def self.all_data_zones
155
+ get_proxies(all_data_zone_identifiers)
156
+ end
157
+
158
+ # Returns an array containing the identifiers of all the available
159
+ # Timezones that are based on data (are not links to other Timezones)..
160
+ def self.all_data_zone_identifiers
161
+ load_index
162
+ Indexes::Timezones.data_timezones
163
+ end
164
+
165
+ # Returns an array containing all the available Timezones that are links
166
+ # to other Timezones.
167
+ #
168
+ # Returns TimezoneProxy objects to avoid the overhead of loading Timezone
169
+ # definitions until a conversion is actually required.
170
+ def self.all_linked_zones
171
+ get_proxies(all_linked_zone_identifiers)
172
+ end
173
+
174
+ # Returns an array containing the identifiers of all the available
175
+ # Timezones that are links to other Timezones.
176
+ def self.all_linked_zone_identifiers
177
+ load_index
178
+ Indexes::Timezones.linked_timezones
179
+ end
180
+
181
+ # Returns all the Timezones defined for all Countries. This is not the
182
+ # complete set of Timezones as some are not country specific (e.g.
183
+ # 'Etc/GMT').
184
+ #
185
+ # Returns TimezoneProxy objects to avoid the overhead of loading Timezone
186
+ # definitions until a conversion is actually required.
187
+ def self.all_country_zones
188
+ Country.all_codes.inject([]) {|zones,country|
189
+ zones += Country.get(country).zones
190
+ }
191
+ end
192
+
193
+ # Returns all the zone identifiers defined for all Countries. This is not the
194
+ # complete set of zone identifiers as some are not country specific (e.g.
195
+ # 'Etc/GMT'). You can obtain a Timezone instance for a given identifier
196
+ # with the get method.
197
+ def self.all_country_zone_identifiers
198
+ Country.all_codes.inject([]) {|zones,country|
199
+ zones += Country.get(country).zone_identifiers
200
+ }
201
+ end
202
+
203
+ # Returns all US Timezone instances. A shortcut for
204
+ # TZInfo::Country.get('US').zones.
205
+ #
206
+ # Returns TimezoneProxy objects to avoid the overhead of loading Timezone
207
+ # definitions until a conversion is actually required.
208
+ def self.us_zones
209
+ Country.get('US').zones
210
+ end
211
+
212
+ # Returns all US zone identifiers. A shortcut for
213
+ # TZInfo::Country.get('US').zone_identifiers.
214
+ def self.us_zone_identifiers
215
+ Country.get('US').zone_identifiers
216
+ end
217
+
218
+ # The identifier of the timezone, e.g. "Europe/Paris".
219
+ def identifier
220
+ raise UnknownTimezone, 'TZInfo::Timezone constructed directly'
221
+ end
222
+
223
+ # An alias for identifier.
224
+ def name
225
+ # Don't use alias, as identifier gets overridden.
226
+ identifier
227
+ end
228
+
229
+ # Returns a friendlier version of the identifier.
230
+ def to_s
231
+ friendly_identifier
232
+ end
233
+
234
+ # Returns internal object state as a programmer-readable string.
235
+ def inspect
236
+ "#<#{self.class}: #{identifier}>"
237
+ end
238
+
239
+ # Returns a friendlier version of the identifier. Set skip_first_part to
240
+ # omit the first part of the identifier (typically a region name) where
241
+ # there is more than one part.
242
+ #
243
+ # For example:
244
+ #
245
+ # Timezone.get('Europe/Paris').friendly_identifier(false) #=> "Europe - Paris"
246
+ # Timezone.get('Europe/Paris').friendly_identifier(true) #=> "Paris"
247
+ # Timezone.get('America/Indiana/Knox').friendly_identifier(false) #=> "America - Knox, Indiana"
248
+ # Timezone.get('America/Indiana/Knox').friendly_identifier(true) #=> "Knox, Indiana"
249
+ def friendly_identifier(skip_first_part = false)
250
+ parts = identifier.split('/')
251
+ if parts.empty?
252
+ # shouldn't happen
253
+ identifier
254
+ elsif parts.length == 1
255
+ parts[0]
256
+ else
257
+ if skip_first_part
258
+ result = ''
259
+ else
260
+ result = parts[0] + ' - '
261
+ end
262
+
263
+ parts[1, parts.length - 1].reverse_each {|part|
264
+ part.gsub!(/_/, ' ')
265
+
266
+ if part.index(/[a-z]/)
267
+ # Missing a space if a lower case followed by an upper case and the
268
+ # name isn't McXxxx.
269
+ part.gsub!(/([^M][a-z])([A-Z])/, '\1 \2')
270
+ part.gsub!(/([M][a-bd-z])([A-Z])/, '\1 \2')
271
+
272
+ # Missing an apostrophe if two consecutive upper case characters.
273
+ part.gsub!(/([A-Z])([A-Z])/, '\1\'\2')
274
+ end
275
+
276
+ result << part
277
+ result << ', '
278
+ }
279
+
280
+ result.slice!(result.length - 2, 2)
281
+ result
282
+ end
283
+ end
284
+
285
+ # Returns the TimezonePeriod for the given UTC time. utc can either be
286
+ # a DateTime, Time or integer timestamp (Time.to_i). Any timezone
287
+ # information in utc is ignored (it is treated as a UTC time).
288
+ def period_for_utc(utc)
289
+ raise UnknownTimezone, 'TZInfo::Timezone constructed directly'
290
+ end
291
+
292
+ # Returns the set of TimezonePeriod instances that are valid for the given
293
+ # local time as an array. If you just want a single period, use
294
+ # period_for_local instead and specify how ambiguities should be resolved.
295
+ # Returns an empty array if no periods are found for the given time.
296
+ def periods_for_local(local)
297
+ raise UnknownTimezone, 'TZInfo::Timezone constructed directly'
298
+ end
299
+
300
+ # Returns the TimezonePeriod for the given local time. local can either be
301
+ # a DateTime, Time or integer timestamp (Time.to_i). Any timezone
302
+ # information in local is ignored (it is treated as a time in the current
303
+ # timezone).
304
+ #
305
+ # Warning: There are local times that have no equivalent UTC times (e.g.
306
+ # in the transition from standard time to daylight savings time). There are
307
+ # also local times that have more than one UTC equivalent (e.g. in the
308
+ # transition from daylight savings time to standard time).
309
+ #
310
+ # In the first case (no equivalent UTC time), a PeriodNotFound exception
311
+ # will be raised.
312
+ #
313
+ # In the second case (more than one equivalent UTC time), an AmbiguousTime
314
+ # exception will be raised unless the optional dst parameter or block
315
+ # handles the ambiguity.
316
+ #
317
+ # If the ambiguity is due to a transition from daylight savings time to
318
+ # standard time, the dst parameter can be used to select whether the
319
+ # daylight savings time or local time is used. For example,
320
+ #
321
+ # Timezone.get('America/New_York').period_for_local(DateTime.new(2004,10,31,1,30,0))
322
+ #
323
+ # would raise an AmbiguousTime exception.
324
+ #
325
+ # Specifying dst=true would the daylight savings period from April to
326
+ # October 2004. Specifying dst=false would return the standard period
327
+ # from October 2004 to April 2005.
328
+ #
329
+ # If the dst parameter does not resolve the ambiguity, and a block is
330
+ # specified, it is called. The block must take a single parameter - an
331
+ # array of the periods that need to be resolved. The block can select and
332
+ # return a single period or return nil or an empty array
333
+ # to cause an AmbiguousTime exception to be raised.
334
+ def period_for_local(local, dst = nil)
335
+ results = periods_for_local(local)
336
+
337
+ if results.empty?
338
+ raise PeriodNotFound
339
+ elsif results.size < 2
340
+ results.first
341
+ else
342
+ # ambiguous result try to resolve
343
+
344
+ if !dst.nil?
345
+ matches = results.find_all {|period| period.dst? == dst}
346
+ results = matches if !matches.empty?
347
+ end
348
+
349
+ if results.size < 2
350
+ results.first
351
+ else
352
+ # still ambiguous, try the block
353
+
354
+ if block_given?
355
+ results = yield results
356
+ end
357
+
358
+ if results.is_a?(TimezonePeriod)
359
+ results
360
+ elsif results && results.size == 1
361
+ results.first
362
+ else
363
+ raise AmbiguousTime, "#{local} is an ambiguous local time."
364
+ end
365
+ end
366
+ end
367
+ end
368
+
369
+ # Converts a time in UTC to the local timezone. utc can either be
370
+ # a DateTime, Time or timestamp (Time.to_i). The returned time has the same
371
+ # type as utc. Any timezone information in utc is ignored (it is treated as
372
+ # a UTC time).
373
+ def utc_to_local(utc)
374
+ TimeOrDateTime.wrap(utc) {|utc|
375
+ period_for_utc(utc).to_local(utc)
376
+ }
377
+ end
378
+
379
+ # Converts a time in the local timezone to UTC. local can either be
380
+ # a DateTime, Time or timestamp (Time.to_i). The returned time has the same
381
+ # type as local. Any timezone information in local is ignored (it is treated
382
+ # as a local time).
383
+ #
384
+ # Warning: There are local times that have no equivalent UTC times (e.g.
385
+ # in the transition from standard time to daylight savings time). There are
386
+ # also local times that have more than one UTC equivalent (e.g. in the
387
+ # transition from daylight savings time to standard time).
388
+ #
389
+ # In the first case (no equivalent UTC time), a PeriodNotFound exception
390
+ # will be raised.
391
+ #
392
+ # In the second case (more than one equivalent UTC time), an AmbiguousTime
393
+ # exception will be raised unless the optional dst parameter or block
394
+ # handles the ambiguity.
395
+ #
396
+ # If the ambiguity is due to a transition from daylight savings time to
397
+ # standard time, the dst parameter can be used to select whether the
398
+ # daylight savings time or local time is used. For example,
399
+ #
400
+ # Timezone.get('America/New_York').local_to_utc(DateTime.new(2004,10,31,1,30,0))
401
+ #
402
+ # would raise an AmbiguousTime exception.
403
+ #
404
+ # Specifying dst=true would return 2004-10-31 5:30:00. Specifying dst=false
405
+ # would return 2004-10-31 6:30:00.
406
+ #
407
+ # If the dst parameter does not resolve the ambiguity, and a block is
408
+ # specified, it is called. The block must take a single parameter - an
409
+ # array of the periods that need to be resolved. The block can return a
410
+ # single period to use to convert the time or return nil or an empty array
411
+ # to cause an AmbiguousTime exception to be raised.
412
+ def local_to_utc(local, dst = nil)
413
+ TimeOrDateTime.wrap(local) {|local|
414
+ if block_given?
415
+ period = period_for_local(local, dst) {|periods| yield periods }
416
+ else
417
+ period = period_for_local(local, dst)
418
+ end
419
+
420
+ period.to_utc(local)
421
+ }
422
+ end
423
+
424
+ # Returns the current time in the timezone as a Time.
425
+ def now
426
+ utc_to_local(Time.now.utc)
427
+ end
428
+
429
+ # Returns the TimezonePeriod for the current time.
430
+ def current_period
431
+ period_for_utc(Time.now.utc)
432
+ end
433
+
434
+ # Returns the current Time and TimezonePeriod as an array. The first element
435
+ # is the time, the second element is the period.
436
+ def current_period_and_time
437
+ utc = Time.now.utc
438
+ period = period_for_utc(utc)
439
+ [period.to_local(utc), period]
440
+ end
441
+
442
+ alias :current_time_and_period :current_period_and_time
443
+
444
+ # Converts a time in UTC to local time and returns it as a string
445
+ # according to the given format. The formatting is identical to
446
+ # Time.strftime and DateTime.strftime, except %Z is replaced with the
447
+ # timezone abbreviation for the specified time (for example, EST or EDT).
448
+ def strftime(format, utc = Time.now.utc)
449
+ period = period_for_utc(utc)
450
+ local = period.to_local(utc)
451
+ local = Time.at(local).utc unless local.kind_of?(Time) || local.kind_of?(DateTime)
452
+ abbreviation = period.abbreviation.to_s.gsub(/%/, '%%')
453
+
454
+ format = format.gsub(/(.?)%Z/) do
455
+ if $1 == '%'
456
+ # return %%Z so the real strftime treats it as a literal %Z too
457
+ '%%Z'
458
+ else
459
+ "#$1#{abbreviation}"
460
+ end
461
+ end
462
+
463
+ local.strftime(format)
464
+ end
465
+
466
+ # Compares two Timezones based on their identifier. Returns -1 if tz is less
467
+ # than self, 0 if tz is equal to self and +1 if tz is greater than self.
468
+ def <=>(tz)
469
+ identifier <=> tz.identifier
470
+ end
471
+
472
+ # Returns true if and only if the identifier of tz is equal to the
473
+ # identifier of this Timezone.
474
+ def eql?(tz)
475
+ self == tz
476
+ end
477
+
478
+ # Returns a hash of this Timezone.
479
+ def hash
480
+ identifier.hash
481
+ end
482
+
483
+ # Dumps this Timezone for marshalling.
484
+ def _dump(limit)
485
+ identifier
486
+ end
487
+
488
+ # Loads a marshalled Timezone.
489
+ def self._load(data)
490
+ Timezone.get(data)
491
+ end
492
+
493
+ private
494
+ # Loads in the index of timezones if it hasn't already been loaded.
495
+ def self.load_index
496
+ unless @@index_loaded
497
+ require 'tzinfo/indexes/timezones'
498
+ @@index_loaded = true
499
+ end
500
+ end
501
+
502
+ # Returns an array of proxies corresponding to the given array of
503
+ # identifiers.
504
+ def self.get_proxies(identifiers)
505
+ identifiers.collect {|identifier| get_proxy(identifier)}
506
+ end
507
+ end
508
+ end