activesupport 4.2.0 → 5.0.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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +630 -220
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +2 -3
  5. data/lib/active_support/array_inquirer.rb +44 -0
  6. data/lib/active_support/backtrace_cleaner.rb +1 -1
  7. data/lib/active_support/benchmarkable.rb +1 -1
  8. data/lib/active_support/cache/file_store.rb +36 -22
  9. data/lib/active_support/cache/mem_cache_store.rb +63 -54
  10. data/lib/active_support/cache/memory_store.rb +16 -21
  11. data/lib/active_support/cache/null_store.rb +1 -4
  12. data/lib/active_support/cache/strategy/local_cache.rb +31 -20
  13. data/lib/active_support/cache.rb +73 -89
  14. data/lib/active_support/callbacks.rb +195 -155
  15. data/lib/active_support/concern.rb +2 -2
  16. data/lib/active_support/concurrency/latch.rb +7 -15
  17. data/lib/active_support/concurrency/share_lock.rb +186 -0
  18. data/lib/active_support/configurable.rb +1 -0
  19. data/lib/active_support/core_ext/array/access.rb +27 -1
  20. data/lib/active_support/core_ext/array/conversions.rb +6 -4
  21. data/lib/active_support/core_ext/array/grouping.rb +9 -18
  22. data/lib/active_support/core_ext/array/inquiry.rb +17 -0
  23. data/lib/active_support/core_ext/array/wrap.rb +5 -4
  24. data/lib/active_support/core_ext/array.rb +1 -0
  25. data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -10
  26. data/lib/active_support/core_ext/class/attribute.rb +10 -9
  27. data/lib/active_support/core_ext/class/subclasses.rb +3 -4
  28. data/lib/active_support/core_ext/class.rb +0 -1
  29. data/lib/active_support/core_ext/date/blank.rb +12 -0
  30. data/lib/active_support/core_ext/date/calculations.rb +1 -1
  31. data/lib/active_support/core_ext/date/conversions.rb +13 -6
  32. data/lib/active_support/core_ext/date.rb +1 -1
  33. data/lib/active_support/core_ext/date_and_time/calculations.rb +109 -25
  34. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -0
  35. data/lib/active_support/core_ext/date_and_time/zones.rb +3 -4
  36. data/lib/active_support/core_ext/date_time/blank.rb +12 -0
  37. data/lib/active_support/core_ext/date_time/calculations.rb +36 -10
  38. data/lib/active_support/core_ext/date_time/compatibility.rb +5 -0
  39. data/lib/active_support/core_ext/date_time/conversions.rb +2 -0
  40. data/lib/active_support/core_ext/date_time.rb +2 -1
  41. data/lib/active_support/core_ext/enumerable.rb +49 -5
  42. data/lib/active_support/core_ext/file/atomic.rb +30 -25
  43. data/lib/active_support/core_ext/hash/conversions.rb +23 -4
  44. data/lib/active_support/core_ext/hash/deep_merge.rb +1 -1
  45. data/lib/active_support/core_ext/hash/except.rb +9 -8
  46. data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -1
  47. data/lib/active_support/core_ext/hash/keys.rb +23 -19
  48. data/lib/active_support/core_ext/hash/slice.rb +1 -1
  49. data/lib/active_support/core_ext/hash/transform_values.rb +11 -5
  50. data/lib/active_support/core_ext/integer/time.rb +1 -16
  51. data/lib/active_support/core_ext/kernel/concern.rb +2 -0
  52. data/lib/active_support/core_ext/kernel/debugger.rb +3 -10
  53. data/lib/active_support/core_ext/kernel/reporting.rb +2 -83
  54. data/lib/active_support/core_ext/kernel.rb +0 -1
  55. data/lib/active_support/core_ext/load_error.rb +4 -2
  56. data/lib/active_support/core_ext/marshal.rb +12 -11
  57. data/lib/active_support/core_ext/module/aliasing.rb +6 -1
  58. data/lib/active_support/core_ext/module/anonymous.rb +10 -1
  59. data/lib/active_support/core_ext/module/attr_internal.rb +2 -5
  60. data/lib/active_support/core_ext/module/attribute_accessors.rb +15 -15
  61. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
  62. data/lib/active_support/core_ext/module/concerning.rb +4 -4
  63. data/lib/active_support/core_ext/module/delegation.rb +35 -25
  64. data/lib/active_support/core_ext/module/deprecation.rb +2 -2
  65. data/lib/active_support/core_ext/module/introspection.rb +4 -0
  66. data/lib/active_support/core_ext/module/method_transplanting.rb +3 -11
  67. data/lib/active_support/core_ext/module/qualified_const.rb +30 -12
  68. data/lib/active_support/core_ext/module/remove_method.rb +23 -0
  69. data/lib/active_support/core_ext/module.rb +1 -0
  70. data/lib/active_support/core_ext/name_error.rb +15 -2
  71. data/lib/active_support/core_ext/numeric/bytes.rb +20 -0
  72. data/lib/active_support/core_ext/numeric/conversions.rb +74 -64
  73. data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
  74. data/lib/active_support/core_ext/numeric/time.rb +24 -19
  75. data/lib/active_support/core_ext/numeric.rb +1 -0
  76. data/lib/active_support/core_ext/object/blank.rb +17 -5
  77. data/lib/active_support/core_ext/object/deep_dup.rb +10 -3
  78. data/lib/active_support/core_ext/object/duplicable.rb +8 -13
  79. data/lib/active_support/core_ext/object/inclusion.rb +2 -2
  80. data/lib/active_support/core_ext/object/instance_variables.rb +1 -1
  81. data/lib/active_support/core_ext/object/json.rb +15 -7
  82. data/lib/active_support/core_ext/object/to_query.rb +1 -1
  83. data/lib/active_support/core_ext/object/try.rb +68 -22
  84. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  85. data/lib/active_support/core_ext/object.rb +0 -1
  86. data/lib/active_support/core_ext/range/conversions.rb +18 -6
  87. data/lib/active_support/core_ext/range/each.rb +16 -18
  88. data/lib/active_support/core_ext/range/include_range.rb +20 -20
  89. data/lib/active_support/core_ext/securerandom.rb +23 -0
  90. data/lib/active_support/core_ext/string/access.rb +1 -1
  91. data/lib/active_support/core_ext/string/behavior.rb +1 -1
  92. data/lib/active_support/core_ext/string/conversions.rb +4 -3
  93. data/lib/active_support/core_ext/string/filters.rb +5 -5
  94. data/lib/active_support/core_ext/string/inflections.rb +32 -5
  95. data/lib/active_support/core_ext/string/multibyte.rb +11 -7
  96. data/lib/active_support/core_ext/string/output_safety.rb +18 -16
  97. data/lib/active_support/core_ext/string/strip.rb +3 -6
  98. data/lib/active_support/core_ext/struct.rb +3 -6
  99. data/lib/active_support/core_ext/time/calculations.rb +36 -11
  100. data/lib/active_support/core_ext/time/compatibility.rb +5 -0
  101. data/lib/active_support/core_ext/time/conversions.rb +4 -2
  102. data/lib/active_support/core_ext/time/marshal.rb +2 -29
  103. data/lib/active_support/core_ext/time/zones.rb +36 -4
  104. data/lib/active_support/core_ext/time.rb +1 -1
  105. data/lib/active_support/core_ext/uri.rb +1 -3
  106. data/lib/active_support/core_ext.rb +2 -1
  107. data/lib/active_support/dependencies/interlock.rb +51 -0
  108. data/lib/active_support/dependencies.rb +87 -95
  109. data/lib/active_support/deprecation/behaviors.rb +16 -2
  110. data/lib/active_support/deprecation/method_wrappers.rb +42 -16
  111. data/lib/active_support/deprecation/proxy_wrappers.rb +47 -24
  112. data/lib/active_support/deprecation/reporting.rb +23 -5
  113. data/lib/active_support/deprecation.rb +1 -1
  114. data/lib/active_support/duration/iso8601_parser.rb +122 -0
  115. data/lib/active_support/duration/iso8601_serializer.rb +51 -0
  116. data/lib/active_support/duration.rb +55 -10
  117. data/lib/active_support/evented_file_update_checker.rb +194 -0
  118. data/lib/active_support/execution_wrapper.rb +117 -0
  119. data/lib/active_support/executor.rb +6 -0
  120. data/lib/active_support/file_update_checker.rb +23 -3
  121. data/lib/active_support/gem_version.rb +3 -3
  122. data/lib/active_support/hash_with_indifferent_access.rb +46 -13
  123. data/lib/active_support/i18n_railtie.rb +25 -4
  124. data/lib/active_support/inflector/inflections.rb +36 -5
  125. data/lib/active_support/inflector/methods.rb +97 -90
  126. data/lib/active_support/inflector/transliterate.rb +36 -21
  127. data/lib/active_support/json/decoding.rb +11 -10
  128. data/lib/active_support/json/encoding.rb +4 -49
  129. data/lib/active_support/key_generator.rb +7 -9
  130. data/lib/active_support/locale/en.yml +2 -0
  131. data/lib/active_support/log_subscriber/test_helper.rb +3 -3
  132. data/lib/active_support/log_subscriber.rb +1 -1
  133. data/lib/active_support/logger.rb +50 -1
  134. data/lib/active_support/logger_silence.rb +8 -4
  135. data/lib/active_support/logger_thread_safe_level.rb +31 -0
  136. data/lib/active_support/message_encryptor.rb +4 -4
  137. data/lib/active_support/message_verifier.rb +70 -8
  138. data/lib/active_support/multibyte/chars.rb +13 -4
  139. data/lib/active_support/multibyte/unicode.rb +44 -21
  140. data/lib/active_support/notifications/fanout.rb +6 -6
  141. data/lib/active_support/notifications/instrumenter.rb +20 -2
  142. data/lib/active_support/notifications.rb +2 -2
  143. data/lib/active_support/number_helper/number_to_currency_converter.rb +7 -9
  144. data/lib/active_support/number_helper/number_to_delimited_converter.rb +8 -3
  145. data/lib/active_support/number_helper/number_to_human_converter.rb +6 -4
  146. data/lib/active_support/number_helper/number_to_human_size_converter.rb +6 -2
  147. data/lib/active_support/number_helper/number_to_percentage_converter.rb +1 -1
  148. data/lib/active_support/number_helper/number_to_phone_converter.rb +11 -2
  149. data/lib/active_support/number_helper/number_to_rounded_converter.rb +30 -25
  150. data/lib/active_support/number_helper.rb +90 -67
  151. data/lib/active_support/ordered_hash.rb +1 -1
  152. data/lib/active_support/ordered_options.rb +15 -1
  153. data/lib/active_support/per_thread_registry.rb +8 -3
  154. data/lib/active_support/rails.rb +2 -2
  155. data/lib/active_support/railtie.rb +6 -1
  156. data/lib/active_support/reloader.rb +129 -0
  157. data/lib/active_support/rescuable.rb +93 -47
  158. data/lib/active_support/security_utils.rb +7 -0
  159. data/lib/active_support/string_inquirer.rb +1 -1
  160. data/lib/active_support/subscriber.rb +5 -10
  161. data/lib/active_support/tagged_logging.rb +3 -1
  162. data/lib/active_support/test_case.rb +15 -29
  163. data/lib/active_support/testing/assertions.rb +15 -13
  164. data/lib/active_support/testing/autorun.rb +8 -1
  165. data/lib/active_support/testing/deprecation.rb +9 -8
  166. data/lib/active_support/testing/file_fixtures.rb +34 -0
  167. data/lib/active_support/testing/isolation.rb +22 -8
  168. data/lib/active_support/testing/method_call_assertions.rb +41 -0
  169. data/lib/active_support/testing/stream.rb +42 -0
  170. data/lib/active_support/testing/time_helpers.rb +13 -10
  171. data/lib/active_support/time_with_zone.rb +135 -46
  172. data/lib/active_support/values/time_zone.rb +95 -47
  173. data/lib/active_support/values/unicode_tables.dat +0 -0
  174. data/lib/active_support/xml_mini/jdom.rb +7 -6
  175. data/lib/active_support/xml_mini/libxml.rb +2 -2
  176. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  177. data/lib/active_support/xml_mini/rexml.rb +7 -8
  178. data/lib/active_support/xml_mini.rb +22 -14
  179. data/lib/active_support.rb +20 -6
  180. metadata +32 -35
  181. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -14
  182. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  183. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  184. data/lib/active_support/core_ext/object/itself.rb +0 -15
  185. data/lib/active_support/core_ext/thread.rb +0 -86
@@ -39,15 +39,16 @@ module ActiveSupport
39
39
  end
40
40
  end
41
41
 
42
- # Containing helpers that helps you test passage of time.
42
+ # Contains helpers that help you test passage of time.
43
43
  module TimeHelpers
44
44
  # Changes current time to the time in the future or in the past by a given time difference by
45
- # stubbing +Time.now+ and +Date.today+.
45
+ # stubbing +Time.now+, +Date.today+, and +DateTime.now+.
46
46
  #
47
- # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
47
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
48
48
  # travel 1.day
49
- # Time.current # => Sun, 10 Nov 2013 15:34:49 EST -05:00
50
- # Date.current # => Sun, 10 Nov 2013
49
+ # Time.current # => Sun, 10 Nov 2013 15:34:49 EST -05:00
50
+ # Date.current # => Sun, 10 Nov 2013
51
+ # DateTime.current # => Sun, 10 Nov 2013 15:34:49 -0500
51
52
  #
52
53
  # This method also accepts a block, which will return the current time back to its original
53
54
  # state at the end of the block:
@@ -61,13 +62,14 @@ module ActiveSupport
61
62
  travel_to Time.now + duration, &block
62
63
  end
63
64
 
64
- # Changes current time to the given time by stubbing +Time.now+ and
65
- # +Date.today+ to return the time or date passed into this method.
65
+ # Changes current time to the given time by stubbing +Time.now+,
66
+ # +Date.today+, and +DateTime.now+ to return the time or date passed into this method.
66
67
  #
67
- # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
68
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
68
69
  # travel_to Time.new(2004, 11, 24, 01, 04, 44)
69
- # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
70
- # Date.current # => Wed, 24 Nov 2004
70
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
71
+ # Date.current # => Wed, 24 Nov 2004
72
+ # DateTime.current # => Wed, 24 Nov 2004 01:04:44 -0500
71
73
  #
72
74
  # Dates are taken as their timestamp at the beginning of the day in the
73
75
  # application time zone. <tt>Time.current</tt> returns said timestamp,
@@ -99,6 +101,7 @@ module ActiveSupport
99
101
 
100
102
  simple_stubs.stub_object(Time, :now, now)
101
103
  simple_stubs.stub_object(Date, :today, now.to_date)
104
+ simple_stubs.stub_object(DateTime, :now, now.to_datetime)
102
105
 
103
106
  if block_given?
104
107
  begin
@@ -1,5 +1,7 @@
1
+ require 'active_support/duration'
1
2
  require 'active_support/values/time_zone'
2
3
  require 'active_support/core_ext/object/acts_like'
4
+ require 'active_support/core_ext/date_and_time/compatibility'
3
5
 
4
6
  module ActiveSupport
5
7
  # A Time-like class that can represent a time in any time zone. Necessary
@@ -13,7 +15,7 @@ module ActiveSupport
13
15
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
14
16
  # Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
15
17
  # Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45 EST -05:00
16
- # Time.zone.at(1170361845) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
18
+ # Time.zone.at(1171139445) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
17
19
  # Time.zone.now # => Sun, 18 May 2008 13:07:55 EDT -04:00
18
20
  # Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45 EST -05:00
19
21
  #
@@ -40,20 +42,24 @@ module ActiveSupport
40
42
  'Time'
41
43
  end
42
44
 
43
- include Comparable
45
+ PRECISIONS = Hash.new { |h, n| h[n] = "%FT%T.%#{n}N".freeze }
46
+ PRECISIONS[0] = '%FT%T'.freeze
47
+
48
+ include Comparable, DateAndTime::Compatibility
44
49
  attr_reader :time_zone
45
50
 
46
51
  def initialize(utc_time, time_zone, local_time = nil, period = nil)
47
- @utc, @time_zone, @time = utc_time, time_zone, local_time
52
+ @utc = utc_time ? transfer_time_values_to_utc_constructor(utc_time) : nil
53
+ @time_zone, @time = time_zone, local_time
48
54
  @period = @utc ? period : get_period_and_ensure_valid_local_time(period)
49
55
  end
50
56
 
51
- # Returns a Time or DateTime instance that represents the time in +time_zone+.
57
+ # Returns a <tt>Time</tt> instance that represents the time in +time_zone+.
52
58
  def time
53
59
  @time ||= period.to_local(@utc)
54
60
  end
55
61
 
56
- # Returns a Time or DateTime instance that represents the time in UTC.
62
+ # Returns a <tt>Time</tt> instance of the simultaneous time in the UTC timezone.
57
63
  def utc
58
64
  @utc ||= period.to_utc(@time)
59
65
  end
@@ -73,10 +79,9 @@ module ActiveSupport
73
79
  utc.in_time_zone(new_zone)
74
80
  end
75
81
 
76
- # Returns a <tt>Time.local()</tt> instance of the simultaneous time in your
77
- # system's <tt>ENV['TZ']</tt> zone.
82
+ # Returns a <tt>Time</tt> instance of the simultaneous time in the system timezone.
78
83
  def localtime(utc_offset = nil)
79
- utc.respond_to?(:getlocal) ? utc.getlocal(utc_offset) : utc.to_time.getlocal(utc_offset)
84
+ utc.getlocal(utc_offset)
80
85
  end
81
86
  alias_method :getlocal, :localtime
82
87
 
@@ -98,7 +103,7 @@ module ActiveSupport
98
103
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
99
104
  # Time.zone.now.utc? # => false
100
105
  def utc?
101
- time_zone.name == 'UTC'
106
+ period.offset.abbreviation == :UTC || period.offset.abbreviation == :UCT
102
107
  end
103
108
  alias_method :gmt?, :utc?
104
109
 
@@ -121,22 +126,27 @@ module ActiveSupport
121
126
  utc? && alternate_utc_string || TimeZone.seconds_to_utc_offset(utc_offset, colon)
122
127
  end
123
128
 
124
- # Time uses +zone+ to display the time zone abbreviation, so we're
125
- # duck-typing it.
129
+ # Returns the time zone abbreviation.
130
+ #
131
+ # Time.zone = 'Eastern Time (US & Canada)' # => "Eastern Time (US & Canada)"
132
+ # Time.zone.now.zone # => "EST"
126
133
  def zone
127
134
  period.zone_identifier.to_s
128
135
  end
129
136
 
137
+ # Returns a string of the object's date, time, zone and offset from UTC.
138
+ #
139
+ # Time.zone.now.inspect # => "Thu, 04 Dec 2014 11:00:25 EST -05:00"
130
140
  def inspect
131
141
  "#{time.strftime('%a, %d %b %Y %H:%M:%S')} #{zone} #{formatted_offset}"
132
142
  end
133
143
 
144
+ # Returns a string of the object's date and time in the ISO 8601 standard
145
+ # format.
146
+ #
147
+ # Time.zone.now.xmlschema # => "2014-12-04T11:02:37-05:00"
134
148
  def xmlschema(fraction_digits = 0)
135
- fraction = if fraction_digits.to_i > 0
136
- (".%06i" % time.usec)[0, fraction_digits.to_i + 1]
137
- end
138
-
139
- "#{time.strftime("%Y-%m-%dT%H:%M:%S")}#{fraction}#{formatted_offset(true, 'Z')}"
149
+ "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z'.freeze)}"
140
150
  end
141
151
  alias_method :iso8601, :xmlschema
142
152
 
@@ -160,12 +170,13 @@ module ActiveSupport
160
170
  end
161
171
  end
162
172
 
163
- def encode_with(coder)
164
- if coder.respond_to?(:represent_object)
165
- coder.represent_object(nil, utc)
166
- else
167
- coder.represent_scalar(nil, utc.strftime("%Y-%m-%d %H:%M:%S.%9NZ"))
168
- end
173
+ def init_with(coder) #:nodoc:
174
+ initialize(coder['utc'], coder['zone'], coder['time'])
175
+ end
176
+
177
+ def encode_with(coder) #:nodoc:
178
+ coder.tag = '!ruby/object:ActiveSupport::TimeWithZone'
179
+ coder.map = { 'utc' => utc, 'zone' => time_zone, 'time' => time }
169
180
  end
170
181
 
171
182
  # Returns a string of the object's date and time in the format used by
@@ -187,7 +198,7 @@ module ActiveSupport
187
198
 
188
199
  # Returns a string of the object's date and time.
189
200
  # Accepts an optional <tt>format</tt>:
190
- # * <tt>:default</tt> - default value, mimics Ruby 1.9 Time#to_s format.
201
+ # * <tt>:default</tt> - default value, mimics Ruby Time#to_s format.
191
202
  # * <tt>:db</tt> - format outputs time in UTC :db time. See Time#to_formatted_s(:db).
192
203
  # * Any key in <tt>Time::DATE_FORMATS</tt> can be used. See active_support/core_ext/time/conversions.rb.
193
204
  def to_s(format = :default)
@@ -196,7 +207,7 @@ module ActiveSupport
196
207
  elsif formatter = ::Time::DATE_FORMATS[format]
197
208
  formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
198
209
  else
199
- "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby 1.9 Time#to_s format
210
+ "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby Time#to_s format
200
211
  end
201
212
  end
202
213
  alias_method :to_formatted_s, :to_s
@@ -235,17 +246,32 @@ module ActiveSupport
235
246
  utc.future?
236
247
  end
237
248
 
249
+ # Returns +true+ if +other+ is equal to current object.
238
250
  def eql?(other)
239
- utc.eql?(other)
251
+ other.eql?(utc)
240
252
  end
241
253
 
242
254
  def hash
243
255
  utc.hash
244
256
  end
245
257
 
258
+ # Adds an interval of time to the current object's time and returns that
259
+ # value as a new TimeWithZone object.
260
+ #
261
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
262
+ # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
263
+ # now + 1000 # => Sun, 02 Nov 2014 01:43:08 EDT -04:00
264
+ #
265
+ # If we're adding a Duration of variable length (i.e., years, months, days),
266
+ # move forward from #time, otherwise move forward from #utc, for accuracy
267
+ # when moving across DST boundaries.
268
+ #
269
+ # For instance, a time + 24.hours will advance exactly 24 hours, while a
270
+ # time + 1.day will advance 23-25 hours, depending on the day.
271
+ #
272
+ # now + 24.hours # => Mon, 03 Nov 2014 00:26:28 EST -05:00
273
+ # now + 1.day # => Mon, 03 Nov 2014 01:26:28 EST -05:00
246
274
  def +(other)
247
- # If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
248
- # otherwise move forward from #utc, for accuracy when moving across DST boundaries
249
275
  if duration_of_variable_length?(other)
250
276
  method_missing(:+, other)
251
277
  else
@@ -253,10 +279,25 @@ module ActiveSupport
253
279
  result.in_time_zone(time_zone)
254
280
  end
255
281
  end
282
+ alias_method :since, :+
256
283
 
284
+ # Returns a new TimeWithZone object that represents the difference between
285
+ # the current object's time and the +other+ time.
286
+ #
287
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
288
+ # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28 EST -05:00
289
+ # now - 1000 # => Mon, 03 Nov 2014 00:09:48 EST -05:00
290
+ #
291
+ # If subtracting a Duration of variable length (i.e., years, months, days),
292
+ # move backward from #time, otherwise move backward from #utc, for accuracy
293
+ # when moving across DST boundaries.
294
+ #
295
+ # For instance, a time - 24.hours will go subtract exactly 24 hours, while a
296
+ # time - 1.day will subtract 23-25 hours, depending on the day.
297
+ #
298
+ # now - 24.hours # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
299
+ # now - 1.day # => Sun, 02 Nov 2014 00:26:28 EDT -04:00
257
300
  def -(other)
258
- # If we're subtracting a Duration of variable length (i.e., years, months, days), move backwards from #time,
259
- # otherwise move backwards #utc, for accuracy when moving across DST boundaries
260
301
  if other.acts_like?(:time)
261
302
  to_time - other.to_time
262
303
  elsif duration_of_variable_length?(other)
@@ -267,20 +308,48 @@ module ActiveSupport
267
308
  end
268
309
  end
269
310
 
270
- def since(other)
271
- # If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
272
- # otherwise move forward from #utc, for accuracy when moving across DST boundaries
273
- if duration_of_variable_length?(other)
274
- method_missing(:since, other)
275
- else
276
- utc.since(other).in_time_zone(time_zone)
277
- end
278
- end
279
-
311
+ # Subtracts an interval of time from the current object's time and returns
312
+ # the result as a new TimeWithZone object.
313
+ #
314
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
315
+ # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28 EST -05:00
316
+ # now.ago(1000) # => Mon, 03 Nov 2014 00:09:48 EST -05:00
317
+ #
318
+ # If we're subtracting a Duration of variable length (i.e., years, months,
319
+ # days), move backward from #time, otherwise move backward from #utc, for
320
+ # accuracy when moving across DST boundaries.
321
+ #
322
+ # For instance, <tt>time.ago(24.hours)</tt> will move back exactly 24 hours,
323
+ # while <tt>time.ago(1.day)</tt> will move back 23-25 hours, depending on
324
+ # the day.
325
+ #
326
+ # now.ago(24.hours) # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
327
+ # now.ago(1.day) # => Sun, 02 Nov 2014 00:26:28 EDT -04:00
280
328
  def ago(other)
281
329
  since(-other)
282
330
  end
283
331
 
332
+ # Uses Date to provide precise Time calculations for years, months, and days
333
+ # according to the proleptic Gregorian calendar. The result is returned as a
334
+ # new TimeWithZone object.
335
+ #
336
+ # The +options+ parameter takes a hash with any of these keys:
337
+ # <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>,
338
+ # <tt>:hours</tt>, <tt>:minutes</tt>, <tt>:seconds</tt>.
339
+ #
340
+ # If advancing by a value of variable length (i.e., years, weeks, months,
341
+ # days), move forward from #time, otherwise move forward from #utc, for
342
+ # accuracy when moving across DST boundaries.
343
+ #
344
+ # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
345
+ # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
346
+ # now.advance(seconds: 1) # => Sun, 02 Nov 2014 01:26:29 EDT -04:00
347
+ # now.advance(minutes: 1) # => Sun, 02 Nov 2014 01:27:28 EDT -04:00
348
+ # now.advance(hours: 1) # => Sun, 02 Nov 2014 01:26:28 EST -05:00
349
+ # now.advance(days: 1) # => Mon, 03 Nov 2014 01:26:28 EST -05:00
350
+ # now.advance(weeks: 1) # => Sun, 09 Nov 2014 01:26:28 EST -05:00
351
+ # now.advance(months: 1) # => Tue, 02 Dec 2014 01:26:28 EST -05:00
352
+ # now.advance(years: 1) # => Mon, 02 Nov 2015 01:26:28 EST -05:00
284
353
  def advance(options)
285
354
  # If we're advancing a value of variable length (i.e., years, weeks, months, days), advance from #time,
286
355
  # otherwise advance from #utc, for accuracy when moving across DST boundaries
@@ -299,28 +368,44 @@ module ActiveSupport
299
368
  EOV
300
369
  end
301
370
 
371
+ # Returns Array of parts of Time in sequence of
372
+ # [seconds, minutes, hours, day, month, year, weekday, yearday, dst?, zone].
373
+ #
374
+ # now = Time.zone.now # => Tue, 18 Aug 2015 02:29:27 UTC +00:00
375
+ # now.to_a # => [27, 29, 2, 18, 8, 2015, 2, 230, false, "UTC"]
302
376
  def to_a
303
377
  [time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone]
304
378
  end
305
379
 
380
+ # Returns the object's date and time as a floating point number of seconds
381
+ # since the Epoch (January 1, 1970 00:00 UTC).
382
+ #
383
+ # Time.zone.now.to_f # => 1417709320.285418
306
384
  def to_f
307
385
  utc.to_f
308
386
  end
309
387
 
388
+ # Returns the object's date and time as an integer number of seconds
389
+ # since the Epoch (January 1, 1970 00:00 UTC).
390
+ #
391
+ # Time.zone.now.to_i # => 1417709320
310
392
  def to_i
311
393
  utc.to_i
312
394
  end
313
395
  alias_method :tv_sec, :to_i
314
396
 
397
+ # Returns the object's date and time as a rational number of seconds
398
+ # since the Epoch (January 1, 1970 00:00 UTC).
399
+ #
400
+ # Time.zone.now.to_r # => (708854548642709/500000)
315
401
  def to_r
316
402
  utc.to_r
317
403
  end
318
404
 
319
- # Return an instance of Time in the system timezone.
320
- def to_time
321
- utc.to_time
322
- end
323
-
405
+ # Returns an instance of DateTime with the timezone's UTC offset
406
+ #
407
+ # Time.zone.now.to_datetime # => Tue, 18 Aug 2015 02:32:20 +0000
408
+ # Time.current.in_time_zone('Hawaii').to_datetime # => Mon, 17 Aug 2015 16:32:20 -1000
324
409
  def to_datetime
325
410
  utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
326
411
  end
@@ -336,6 +421,11 @@ module ActiveSupport
336
421
  end
337
422
  alias_method :kind_of?, :is_a?
338
423
 
424
+ # An instance of ActiveSupport::TimeWithZone is never blank
425
+ def blank?
426
+ false
427
+ end
428
+
339
429
  def freeze
340
430
  period; utc; time # preload instance variables before freezing
341
431
  super
@@ -360,7 +450,6 @@ module ActiveSupport
360
450
  # Ensure proxy class responds to all methods that underlying time instance
361
451
  # responds to.
362
452
  def respond_to_missing?(sym, include_priv)
363
- # consistently respond false to acts_like?(:date), regardless of whether #time is a Time or DateTime
364
453
  return false if sym.to_sym == :acts_like_date?
365
454
  time.respond_to?(sym, include_priv)
366
455
  end
@@ -388,7 +477,7 @@ module ActiveSupport
388
477
  end
389
478
 
390
479
  def transfer_time_values_to_utc_constructor(time)
391
- ::Time.utc(time.year, time.month, time.day, time.hour, time.min, time.sec, Rational(time.nsec, 1000))
480
+ ::Time.utc(time.year, time.month, time.day, time.hour, time.min, time.sec + time.subsec)
392
481
  end
393
482
 
394
483
  def duration_of_variable_length?(obj)
@@ -1,7 +1,6 @@
1
1
  require 'tzinfo'
2
- require 'thread_safe'
2
+ require 'concurrent/map'
3
3
  require 'active_support/core_ext/object/blank'
4
- require 'active_support/core_ext/object/try'
5
4
 
6
5
  module ActiveSupport
7
6
  # The TimeZone class serves as a wrapper around TZInfo::Timezone instances.
@@ -23,15 +22,9 @@ module ActiveSupport
23
22
  # config.time_zone = 'Eastern Time (US & Canada)'
24
23
  # end
25
24
  #
26
- # Time.zone # => #<TimeZone:0x514834...>
25
+ # Time.zone # => #<ActiveSupport::TimeZone:0x514834...>
27
26
  # Time.zone.name # => "Eastern Time (US & Canada)"
28
27
  # Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00
29
- #
30
- # The version of TZInfo bundled with Active Support only includes the
31
- # definitions necessary to support the zones defined by the TimeZone class.
32
- # If you need to use zones that aren't defined by TimeZone, you'll need to
33
- # install the TZInfo gem (if a recent version of the gem is installed locally,
34
- # this will be used instead of the bundled version.)
35
28
  class TimeZone
36
29
  # Keys are Rails TimeZone names, values are TZInfo identifiers.
37
30
  MAPPING = {
@@ -92,7 +85,8 @@ module ActiveSupport
92
85
  "Paris" => "Europe/Paris",
93
86
  "Amsterdam" => "Europe/Amsterdam",
94
87
  "Berlin" => "Europe/Berlin",
95
- "Bern" => "Europe/Berlin",
88
+ "Bern" => "Europe/Zurich",
89
+ "Zurich" => "Europe/Zurich",
96
90
  "Rome" => "Europe/Rome",
97
91
  "Stockholm" => "Europe/Stockholm",
98
92
  "Vienna" => "Europe/Vienna",
@@ -111,9 +105,11 @@ module ActiveSupport
111
105
  "Jerusalem" => "Asia/Jerusalem",
112
106
  "Harare" => "Africa/Harare",
113
107
  "Pretoria" => "Africa/Johannesburg",
108
+ "Kaliningrad" => "Europe/Kaliningrad",
114
109
  "Moscow" => "Europe/Moscow",
115
110
  "St. Petersburg" => "Europe/Moscow",
116
- "Volgograd" => "Europe/Moscow",
111
+ "Volgograd" => "Europe/Volgograd",
112
+ "Samara" => "Europe/Samara",
117
113
  "Kuwait" => "Asia/Kuwait",
118
114
  "Riyadh" => "Asia/Riyadh",
119
115
  "Nairobi" => "Africa/Nairobi",
@@ -170,6 +166,7 @@ module ActiveSupport
170
166
  "Guam" => "Pacific/Guam",
171
167
  "Port Moresby" => "Pacific/Port_Moresby",
172
168
  "Magadan" => "Asia/Magadan",
169
+ "Srednekolymsk" => "Asia/Srednekolymsk",
173
170
  "Solomon Is." => "Pacific/Guadalcanal",
174
171
  "New Caledonia" => "Pacific/Noumea",
175
172
  "Fiji" => "Pacific/Fiji",
@@ -186,13 +183,14 @@ module ActiveSupport
186
183
  UTC_OFFSET_WITH_COLON = '%s%02d:%02d'
187
184
  UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.tr(':', '')
188
185
 
189
- @lazy_zones_map = ThreadSafe::Cache.new
186
+ @lazy_zones_map = Concurrent::Map.new
187
+ @country_zones = Concurrent::Map.new
190
188
 
191
189
  class << self
192
190
  # Assumes self represents an offset from UTC in seconds (as returned from
193
191
  # Time#utc_offset) and turns this into an +HH:MM formatted string.
194
192
  #
195
- # TimeZone.seconds_to_utc_offset(-21_600) # => "-06:00"
193
+ # ActiveSupport::TimeZone.seconds_to_utc_offset(-21_600) # => "-06:00"
196
194
  def seconds_to_utc_offset(seconds, colon = true)
197
195
  format = colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON
198
196
  sign = (seconds < 0 ? '-' : '+')
@@ -202,7 +200,7 @@ module ActiveSupport
202
200
  end
203
201
 
204
202
  def find_tzinfo(name)
205
- TZInfo::TimezoneProxy.new(MAPPING[name] || name)
203
+ TZInfo::Timezone.new(MAPPING[name] || name)
206
204
  end
207
205
 
208
206
  alias_method :create, :new
@@ -221,13 +219,6 @@ module ActiveSupport
221
219
  @zones ||= zones_map.values.sort
222
220
  end
223
221
 
224
- def zones_map
225
- @zones_map ||= begin
226
- MAPPING.each_key {|place| self[place]} # load all the zones
227
- @lazy_zones_map
228
- end
229
- end
230
-
231
222
  # Locate a specific time zone object. If the argument is a string, it
232
223
  # is interpreted to mean the name of the timezone to locate. If it is a
233
224
  # numeric value it is either the hour offset, or the second offset, of the
@@ -237,7 +228,7 @@ module ActiveSupport
237
228
  case arg
238
229
  when String
239
230
  begin
240
- @lazy_zones_map[arg] ||= create(arg).tap { |tz| tz.utc_offset }
231
+ @lazy_zones_map[arg] ||= create(arg)
241
232
  rescue TZInfo::InvalidTimezoneIdentifier
242
233
  nil
243
234
  end
@@ -252,8 +243,27 @@ module ActiveSupport
252
243
  # A convenience method for returning a collection of TimeZone objects
253
244
  # for time zones in the USA.
254
245
  def us_zones
255
- @us_zones ||= all.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ }
246
+ country_zones(:us)
256
247
  end
248
+
249
+ # A convenience method for returning a collection of TimeZone objects
250
+ # for time zones in the country specified by its ISO 3166-1 Alpha2 code.
251
+ def country_zones(country_code)
252
+ code = country_code.to_s.upcase
253
+ @country_zones[code] ||=
254
+ TZInfo::Country.get(code).zone_identifiers.map do |tz_id|
255
+ name = MAPPING.key(tz_id)
256
+ name && self[name]
257
+ end.compact.sort!
258
+ end
259
+
260
+ private
261
+ def zones_map
262
+ @zones_map ||= begin
263
+ MAPPING.each_key {|place| self[place]} # load all the zones
264
+ @lazy_zones_map
265
+ end
266
+ end
257
267
  end
258
268
 
259
269
  include Comparable
@@ -268,7 +278,6 @@ module ActiveSupport
268
278
  @name = name
269
279
  @utc_offset = utc_offset
270
280
  @tzinfo = tzinfo || TimeZone.find_tzinfo(name)
271
- @current_period = nil
272
281
  end
273
282
 
274
283
  # Returns the offset of this time zone from UTC in seconds.
@@ -276,13 +285,16 @@ module ActiveSupport
276
285
  if @utc_offset
277
286
  @utc_offset
278
287
  else
279
- @current_period ||= tzinfo.current_period if tzinfo
280
- @current_period.utc_offset if @current_period
288
+ tzinfo.current_period.utc_offset if tzinfo && tzinfo.current_period
281
289
  end
282
290
  end
283
291
 
284
- # Returns the offset of this time zone as a formatted string, of the
285
- # format "+HH:MM".
292
+ # Returns a formatted string of the offset from UTC, or an alternative
293
+ # string if the time zone is already UTC.
294
+ #
295
+ # zone = ActiveSupport::TimeZone['Central Time (US & Canada)']
296
+ # zone.formatted_offset # => "-06:00"
297
+ # zone.formatted_offset(false) # => "-0600"
286
298
  def formatted_offset(colon=true, alternate_utc_string = nil)
287
299
  utc_offset == 0 && alternate_utc_string || self.class.seconds_to_utc_offset(utc_offset, colon)
288
300
  end
@@ -344,24 +356,31 @@ module ActiveSupport
344
356
  #
345
357
  # Time.zone.parse('Mar 2000') # => Wed, 01 Mar 2000 00:00:00 HST -10:00
346
358
  def parse(str, now=now())
347
- parts = Date._parse(str, false)
348
- return if parts.empty?
349
-
350
- time = Time.new(
351
- parts.fetch(:year, now.year),
352
- parts.fetch(:mon, now.month),
353
- parts.fetch(:mday, parts[:year] || parts[:mon] ? 1 : now.day),
354
- parts.fetch(:hour, 0),
355
- parts.fetch(:min, 0),
356
- parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
357
- parts.fetch(:offset, 0)
358
- )
359
-
360
- if parts[:offset]
361
- TimeWithZone.new(time.utc, self)
362
- else
363
- TimeWithZone.new(nil, self, time)
364
- end
359
+ parts_to_time(Date._parse(str, false), now)
360
+ end
361
+
362
+ # Parses +str+ according to +format+ and returns an ActiveSupport::TimeWithZone.
363
+ #
364
+ # Assumes that +str+ is a time in the time zone +self+,
365
+ # unless +format+ includes an explicit time zone.
366
+ # (This is the same behavior as +parse+.)
367
+ # In either case, the returned TimeWithZone has the timezone of +self+.
368
+ #
369
+ # Time.zone = 'Hawaii' # => "Hawaii"
370
+ # 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
371
+ #
372
+ # If upper components are missing from the string, they are supplied from
373
+ # TimeZone#now:
374
+ #
375
+ # Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00
376
+ # Time.zone.strptime('22:30:00', '%H:%M:%S') # => Fri, 31 Dec 1999 22:30:00 HST -10:00
377
+ #
378
+ # However, if the date component is not provided, but any other upper
379
+ # components are supplied, then the day of the month defaults to 1:
380
+ #
381
+ # Time.zone.strptime('Mar 2000', '%b %Y') # => Wed, 01 Mar 2000 00:00:00 HST -10:00
382
+ def strptime(str, format, now=now())
383
+ parts_to_time(DateTime._strptime(str, format), now)
365
384
  end
366
385
 
367
386
  # Returns an ActiveSupport::TimeWithZone instance representing the current
@@ -373,7 +392,7 @@ module ActiveSupport
373
392
  time_now.utc.in_time_zone(self)
374
393
  end
375
394
 
376
- # Return the current date in this time zone.
395
+ # Returns the current date in this time zone.
377
396
  def today
378
397
  tzinfo.now.to_date
379
398
  end
@@ -417,7 +436,36 @@ module ActiveSupport
417
436
  tzinfo.periods_for_local(time)
418
437
  end
419
438
 
439
+ def init_with(coder) #:nodoc:
440
+ initialize(coder['name'])
441
+ end
442
+
443
+ def encode_with(coder) #:nodoc:
444
+ coder.tag ="!ruby/object:#{self.class}"
445
+ coder.map = { 'name' => tzinfo.name }
446
+ end
447
+
420
448
  private
449
+ def parts_to_time(parts, now)
450
+ return if parts.empty?
451
+
452
+ time = Time.new(
453
+ parts.fetch(:year, now.year),
454
+ parts.fetch(:mon, now.month),
455
+ parts.fetch(:mday, parts[:year] || parts[:mon] ? 1 : now.day),
456
+ parts.fetch(:hour, 0),
457
+ parts.fetch(:min, 0),
458
+ parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
459
+ parts.fetch(:offset, 0)
460
+ )
461
+
462
+ if parts[:offset]
463
+ TimeWithZone.new(time.utc, self)
464
+ else
465
+ TimeWithZone.new(nil, self, time)
466
+ end
467
+ end
468
+
421
469
  def time_now
422
470
  Time.now
423
471
  end