activesupport 3.1.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 (276) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +798 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +13 -7
  5. data/lib/active_support/array_inquirer.rb +44 -0
  6. data/lib/active_support/backtrace_cleaner.rb +38 -34
  7. data/lib/active_support/benchmarkable.rb +17 -28
  8. data/lib/active_support/cache/file_store.rb +85 -70
  9. data/lib/active_support/cache/mem_cache_store.rb +75 -66
  10. data/lib/active_support/cache/memory_store.rb +31 -23
  11. data/lib/active_support/cache/null_store.rb +41 -0
  12. data/lib/active_support/cache/strategy/local_cache.rb +73 -70
  13. data/lib/active_support/cache/strategy/local_cache_middleware.rb +44 -0
  14. data/lib/active_support/cache.rb +360 -294
  15. data/lib/active_support/callbacks.rb +563 -393
  16. data/lib/active_support/concern.rb +42 -34
  17. data/lib/active_support/concurrency/latch.rb +19 -0
  18. data/lib/active_support/concurrency/share_lock.rb +186 -0
  19. data/lib/active_support/configurable.rb +70 -12
  20. data/lib/active_support/core_ext/array/access.rb +53 -9
  21. data/lib/active_support/core_ext/array/conversions.rb +109 -62
  22. data/lib/active_support/core_ext/array/extract_options.rb +2 -2
  23. data/lib/active_support/core_ext/array/grouping.rb +39 -32
  24. data/lib/active_support/core_ext/array/inquiry.rb +17 -0
  25. data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -0
  26. data/lib/active_support/core_ext/array/wrap.rb +16 -18
  27. data/lib/active_support/core_ext/array.rb +2 -2
  28. data/lib/active_support/core_ext/benchmark.rb +7 -0
  29. data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -36
  30. data/lib/active_support/core_ext/class/attribute.rb +47 -34
  31. data/lib/active_support/core_ext/class/attribute_accessors.rb +4 -79
  32. data/lib/active_support/core_ext/class/subclasses.rb +12 -7
  33. data/lib/active_support/core_ext/class.rb +0 -3
  34. data/lib/active_support/core_ext/date/blank.rb +12 -0
  35. data/lib/active_support/core_ext/date/calculations.rb +57 -167
  36. data/lib/active_support/core_ext/date/conversions.rb +31 -42
  37. data/lib/active_support/core_ext/date/zones.rb +2 -10
  38. data/lib/active_support/core_ext/date.rb +5 -0
  39. data/lib/active_support/core_ext/date_and_time/calculations.rb +335 -0
  40. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -0
  41. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  42. data/lib/active_support/core_ext/date_time/acts_like.rb +1 -0
  43. data/lib/active_support/core_ext/date_time/blank.rb +12 -0
  44. data/lib/active_support/core_ext/date_time/calculations.rb +132 -65
  45. data/lib/active_support/core_ext/date_time/compatibility.rb +5 -0
  46. data/lib/active_support/core_ext/date_time/conversions.rb +36 -34
  47. data/lib/active_support/core_ext/date_time.rb +5 -0
  48. data/lib/active_support/core_ext/digest/uuid.rb +51 -0
  49. data/lib/active_support/core_ext/enumerable.rb +81 -74
  50. data/lib/active_support/core_ext/file/atomic.rb +53 -26
  51. data/lib/active_support/core_ext/file.rb +0 -1
  52. data/lib/active_support/core_ext/hash/compact.rb +20 -0
  53. data/lib/active_support/core_ext/hash/conversions.rb +175 -70
  54. data/lib/active_support/core_ext/hash/deep_merge.rb +30 -8
  55. data/lib/active_support/core_ext/hash/except.rb +11 -12
  56. data/lib/active_support/core_ext/hash/indifferent_access.rb +7 -8
  57. data/lib/active_support/core_ext/hash/keys.rb +147 -24
  58. data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -3
  59. data/lib/active_support/core_ext/hash/slice.rb +22 -14
  60. data/lib/active_support/core_ext/hash/transform_values.rb +29 -0
  61. data/lib/active_support/core_ext/hash.rb +2 -2
  62. data/lib/active_support/core_ext/integer/inflections.rb +13 -1
  63. data/lib/active_support/core_ext/integer/multiple.rb +4 -0
  64. data/lib/active_support/core_ext/integer/time.rb +12 -22
  65. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -2
  66. data/lib/active_support/core_ext/kernel/concern.rb +12 -0
  67. data/lib/active_support/core_ext/kernel/debugger.rb +2 -15
  68. data/lib/active_support/core_ext/kernel/reporting.rb +12 -62
  69. data/lib/active_support/core_ext/kernel/singleton_class.rb +0 -7
  70. data/lib/active_support/core_ext/kernel.rb +2 -3
  71. data/lib/active_support/core_ext/load_error.rb +14 -7
  72. data/lib/active_support/core_ext/marshal.rb +22 -0
  73. data/lib/active_support/core_ext/module/aliasing.rb +16 -12
  74. data/lib/active_support/core_ext/module/anonymous.rb +12 -8
  75. data/lib/active_support/core_ext/module/attr_internal.rb +2 -5
  76. data/lib/active_support/core_ext/module/attribute_accessors.rb +165 -13
  77. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
  78. data/lib/active_support/core_ext/module/concerning.rb +135 -0
  79. data/lib/active_support/core_ext/module/delegation.rb +141 -68
  80. data/lib/active_support/core_ext/module/deprecation.rb +17 -3
  81. data/lib/active_support/core_ext/module/introspection.rb +9 -31
  82. data/lib/active_support/core_ext/module/method_transplanting.rb +3 -0
  83. data/lib/active_support/core_ext/module/qualified_const.rb +70 -0
  84. data/lib/active_support/core_ext/module/reachable.rb +1 -3
  85. data/lib/active_support/core_ext/module/remove_method.rb +24 -5
  86. data/lib/active_support/core_ext/module.rb +3 -3
  87. data/lib/active_support/core_ext/name_error.rb +15 -2
  88. data/lib/active_support/core_ext/numeric/bytes.rb +20 -0
  89. data/lib/active_support/core_ext/numeric/conversions.rb +145 -0
  90. data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
  91. data/lib/active_support/core_ext/numeric/time.rb +31 -36
  92. data/lib/active_support/core_ext/numeric.rb +2 -0
  93. data/lib/active_support/core_ext/object/acts_like.rb +4 -4
  94. data/lib/active_support/core_ext/object/blank.rb +52 -18
  95. data/lib/active_support/core_ext/object/deep_dup.rb +53 -0
  96. data/lib/active_support/core_ext/object/duplicable.rb +12 -20
  97. data/lib/active_support/core_ext/object/inclusion.rb +13 -1
  98. data/lib/active_support/core_ext/object/instance_variables.rb +7 -12
  99. data/lib/active_support/core_ext/object/json.rb +205 -0
  100. data/lib/active_support/core_ext/object/to_param.rb +1 -55
  101. data/lib/active_support/core_ext/object/to_query.rb +66 -9
  102. data/lib/active_support/core_ext/object/try.rb +124 -33
  103. data/lib/active_support/core_ext/object/with_options.rb +37 -11
  104. data/lib/active_support/core_ext/object.rb +2 -1
  105. data/lib/active_support/core_ext/range/conversions.rb +17 -7
  106. data/lib/active_support/core_ext/range/each.rb +21 -0
  107. data/lib/active_support/core_ext/range/include_range.rb +20 -18
  108. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  109. data/lib/active_support/core_ext/range.rb +1 -2
  110. data/lib/active_support/core_ext/securerandom.rb +23 -0
  111. data/lib/active_support/core_ext/string/access.rb +95 -90
  112. data/lib/active_support/core_ext/string/behavior.rb +1 -1
  113. data/lib/active_support/core_ext/string/conversions.rb +41 -38
  114. data/lib/active_support/core_ext/string/exclude.rb +6 -1
  115. data/lib/active_support/core_ext/string/filters.rb +70 -17
  116. data/lib/active_support/core_ext/string/indent.rb +43 -0
  117. data/lib/active_support/core_ext/string/inflections.rb +139 -59
  118. data/lib/active_support/core_ext/string/inquiry.rb +2 -2
  119. data/lib/active_support/core_ext/string/multibyte.rb +46 -65
  120. data/lib/active_support/core_ext/string/output_safety.rb +153 -56
  121. data/lib/active_support/core_ext/string/strip.rb +3 -6
  122. data/lib/active_support/core_ext/string/zones.rb +14 -0
  123. data/lib/active_support/core_ext/string.rb +2 -3
  124. data/lib/active_support/core_ext/struct.rb +3 -0
  125. data/lib/active_support/core_ext/time/calculations.rb +173 -173
  126. data/lib/active_support/core_ext/time/compatibility.rb +5 -0
  127. data/lib/active_support/core_ext/time/conversions.rb +33 -29
  128. data/lib/active_support/core_ext/time/marshal.rb +2 -56
  129. data/lib/active_support/core_ext/time/zones.rb +57 -32
  130. data/lib/active_support/core_ext/time.rb +5 -0
  131. data/lib/active_support/core_ext/uri.rb +13 -19
  132. data/lib/active_support/core_ext.rb +3 -2
  133. data/lib/active_support/dependencies/autoload.rb +47 -20
  134. data/lib/active_support/dependencies/interlock.rb +51 -0
  135. data/lib/active_support/dependencies.rb +315 -265
  136. data/lib/active_support/deprecation/behaviors.rb +71 -30
  137. data/lib/active_support/deprecation/instance_delegator.rb +24 -0
  138. data/lib/active_support/deprecation/method_wrappers.rb +59 -18
  139. data/lib/active_support/deprecation/proxy_wrappers.rb +82 -14
  140. data/lib/active_support/deprecation/reporting.rb +61 -14
  141. data/lib/active_support/deprecation.rb +38 -13
  142. data/lib/active_support/descendants_tracker.rb +34 -19
  143. data/lib/active_support/duration/iso8601_parser.rb +122 -0
  144. data/lib/active_support/duration/iso8601_serializer.rb +51 -0
  145. data/lib/active_support/duration.rb +85 -14
  146. data/lib/active_support/evented_file_update_checker.rb +194 -0
  147. data/lib/active_support/execution_wrapper.rb +117 -0
  148. data/lib/active_support/executor.rb +6 -0
  149. data/lib/active_support/file_update_checker.rb +138 -17
  150. data/lib/active_support/gem_version.rb +15 -0
  151. data/lib/active_support/gzip.rb +11 -5
  152. data/lib/active_support/hash_with_indifferent_access.rb +199 -49
  153. data/lib/active_support/i18n.rb +6 -2
  154. data/lib/active_support/i18n_railtie.rb +40 -21
  155. data/lib/active_support/inflections.rb +22 -13
  156. data/lib/active_support/inflector/inflections.rb +175 -144
  157. data/lib/active_support/inflector/methods.rb +328 -91
  158. data/lib/active_support/inflector/transliterate.rb +51 -37
  159. data/lib/active_support/json/decoding.rb +31 -22
  160. data/lib/active_support/json/encoding.rb +88 -248
  161. data/lib/active_support/key_generator.rb +71 -0
  162. data/lib/active_support/lazy_load_hooks.rb +27 -25
  163. data/lib/active_support/locale/en.yml +102 -3
  164. data/lib/active_support/log_subscriber/test_helper.rb +24 -21
  165. data/lib/active_support/log_subscriber.rb +36 -49
  166. data/lib/active_support/logger.rb +106 -0
  167. data/lib/active_support/logger_silence.rb +28 -0
  168. data/lib/active_support/logger_thread_safe_level.rb +31 -0
  169. data/lib/active_support/message_encryptor.rb +72 -36
  170. data/lib/active_support/message_verifier.rb +96 -24
  171. data/lib/active_support/multibyte/chars.rb +88 -333
  172. data/lib/active_support/multibyte/unicode.rb +156 -136
  173. data/lib/active_support/multibyte.rb +5 -28
  174. data/lib/active_support/notifications/fanout.rb +115 -19
  175. data/lib/active_support/notifications/instrumenter.rb +52 -15
  176. data/lib/active_support/notifications.rb +168 -33
  177. data/lib/active_support/number_helper/number_converter.rb +182 -0
  178. data/lib/active_support/number_helper/number_to_currency_converter.rb +44 -0
  179. data/lib/active_support/number_helper/number_to_delimited_converter.rb +28 -0
  180. data/lib/active_support/number_helper/number_to_human_converter.rb +68 -0
  181. data/lib/active_support/number_helper/number_to_human_size_converter.rb +62 -0
  182. data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
  183. data/lib/active_support/number_helper/number_to_phone_converter.rb +58 -0
  184. data/lib/active_support/number_helper/number_to_rounded_converter.rb +92 -0
  185. data/lib/active_support/number_helper.rb +368 -0
  186. data/lib/active_support/option_merger.rb +1 -1
  187. data/lib/active_support/ordered_hash.rb +18 -183
  188. data/lib/active_support/ordered_options.rb +44 -24
  189. data/lib/active_support/per_thread_registry.rb +58 -0
  190. data/lib/active_support/proxy_object.rb +13 -0
  191. data/lib/active_support/rails.rb +27 -0
  192. data/lib/active_support/railtie.rb +25 -34
  193. data/lib/active_support/reloader.rb +129 -0
  194. data/lib/active_support/rescuable.rb +98 -48
  195. data/lib/active_support/security_utils.rb +27 -0
  196. data/lib/active_support/string_inquirer.rb +14 -9
  197. data/lib/active_support/subscriber.rb +120 -0
  198. data/lib/active_support/tagged_logging.rb +78 -0
  199. data/lib/active_support/test_case.rb +69 -17
  200. data/lib/active_support/testing/assertions.rb +43 -41
  201. data/lib/active_support/testing/autorun.rb +12 -0
  202. data/lib/active_support/testing/constant_lookup.rb +50 -0
  203. data/lib/active_support/testing/declarative.rb +7 -21
  204. data/lib/active_support/testing/deprecation.rb +14 -33
  205. data/lib/active_support/testing/file_fixtures.rb +34 -0
  206. data/lib/active_support/testing/isolation.rb +53 -95
  207. data/lib/active_support/testing/method_call_assertions.rb +41 -0
  208. data/lib/active_support/testing/setup_and_teardown.rb +21 -82
  209. data/lib/active_support/testing/stream.rb +42 -0
  210. data/lib/active_support/testing/tagged_logging.rb +25 -0
  211. data/lib/active_support/testing/time_helpers.rb +134 -0
  212. data/lib/active_support/time.rb +6 -23
  213. data/lib/active_support/time_with_zone.rb +239 -92
  214. data/lib/active_support/values/time_zone.rb +236 -160
  215. data/lib/active_support/values/unicode_tables.dat +0 -0
  216. data/lib/active_support/version.rb +5 -7
  217. data/lib/active_support/xml_mini/jdom.rb +19 -13
  218. data/lib/active_support/xml_mini/libxml.rb +3 -4
  219. data/lib/active_support/xml_mini/libxmlsax.rb +2 -3
  220. data/lib/active_support/xml_mini/nokogiri.rb +3 -4
  221. data/lib/active_support/xml_mini/nokogirisax.rb +2 -3
  222. data/lib/active_support/xml_mini/rexml.rb +8 -10
  223. data/lib/active_support/xml_mini.rb +66 -34
  224. data/lib/active_support.rb +40 -23
  225. metadata +185 -134
  226. data/CHANGELOG +0 -1534
  227. data/lib/active_support/base64.rb +0 -42
  228. data/lib/active_support/basic_object.rb +0 -21
  229. data/lib/active_support/buffered_logger.rb +0 -137
  230. data/lib/active_support/cache/compressed_mem_cache_store.rb +0 -13
  231. data/lib/active_support/cache/synchronized_memory_store.rb +0 -11
  232. data/lib/active_support/core_ext/array/random_access.rb +0 -30
  233. data/lib/active_support/core_ext/array/uniq_by.rb +0 -16
  234. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -44
  235. data/lib/active_support/core_ext/class/inheritable_attributes.rb +0 -178
  236. data/lib/active_support/core_ext/date/freeze.rb +0 -31
  237. data/lib/active_support/core_ext/date_time/zones.rb +0 -21
  238. data/lib/active_support/core_ext/exception.rb +0 -3
  239. data/lib/active_support/core_ext/file/path.rb +0 -5
  240. data/lib/active_support/core_ext/float/rounding.rb +0 -19
  241. data/lib/active_support/core_ext/float.rb +0 -1
  242. data/lib/active_support/core_ext/hash/deep_dup.rb +0 -11
  243. data/lib/active_support/core_ext/hash/diff.rb +0 -13
  244. data/lib/active_support/core_ext/kernel/requires.rb +0 -28
  245. data/lib/active_support/core_ext/logger.rb +0 -81
  246. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +0 -31
  247. data/lib/active_support/core_ext/module/method_names.rb +0 -14
  248. data/lib/active_support/core_ext/module/synchronization.rb +0 -43
  249. data/lib/active_support/core_ext/object/to_json.rb +0 -19
  250. data/lib/active_support/core_ext/proc.rb +0 -14
  251. data/lib/active_support/core_ext/process/daemon.rb +0 -23
  252. data/lib/active_support/core_ext/process.rb +0 -1
  253. data/lib/active_support/core_ext/range/blockless_step.rb +0 -29
  254. data/lib/active_support/core_ext/range/cover.rb +0 -3
  255. data/lib/active_support/core_ext/rexml.rb +0 -46
  256. data/lib/active_support/core_ext/string/encoding.rb +0 -11
  257. data/lib/active_support/core_ext/string/interpolation.rb +0 -2
  258. data/lib/active_support/core_ext/string/xchar.rb +0 -18
  259. data/lib/active_support/core_ext/time/publicize_conversion_methods.rb +0 -10
  260. data/lib/active_support/file_watcher.rb +0 -36
  261. data/lib/active_support/json/variable.rb +0 -9
  262. data/lib/active_support/memoizable.rb +0 -105
  263. data/lib/active_support/multibyte/exceptions.rb +0 -8
  264. data/lib/active_support/multibyte/utils.rb +0 -60
  265. data/lib/active_support/ruby/shim.rb +0 -22
  266. data/lib/active_support/secure_random.rb +0 -6
  267. data/lib/active_support/testing/mochaing.rb +0 -7
  268. data/lib/active_support/testing/pending.rb +0 -52
  269. data/lib/active_support/testing/performance/jruby.rb +0 -115
  270. data/lib/active_support/testing/performance/rubinius.rb +0 -113
  271. data/lib/active_support/testing/performance/ruby/mri.rb +0 -57
  272. data/lib/active_support/testing/performance/ruby/yarv.rb +0 -57
  273. data/lib/active_support/testing/performance/ruby.rb +0 -152
  274. data/lib/active_support/testing/performance.rb +0 -317
  275. data/lib/active_support/time/autoload.rb +0 -5
  276. data/lib/active_support/whiny_nil.rb +0 -60
@@ -0,0 +1,335 @@
1
+ require 'active_support/core_ext/object/try'
2
+
3
+ module DateAndTime
4
+ module Calculations
5
+ DAYS_INTO_WEEK = {
6
+ :monday => 0,
7
+ :tuesday => 1,
8
+ :wednesday => 2,
9
+ :thursday => 3,
10
+ :friday => 4,
11
+ :saturday => 5,
12
+ :sunday => 6
13
+ }
14
+ WEEKEND_DAYS = [ 6, 0 ]
15
+
16
+ # Returns a new date/time representing yesterday.
17
+ def yesterday
18
+ advance(days: -1)
19
+ end
20
+
21
+ # Returns a new date/time representing the previous day.
22
+ def prev_day
23
+ advance(days: -1)
24
+ end
25
+
26
+ # Returns a new date/time representing tomorrow.
27
+ def tomorrow
28
+ advance(days: 1)
29
+ end
30
+
31
+ # Returns a new date/time representing the next day.
32
+ def next_day
33
+ advance(days: 1)
34
+ end
35
+
36
+ # Returns true if the date/time is today.
37
+ def today?
38
+ to_date == ::Date.current
39
+ end
40
+
41
+ # Returns true if the date/time is in the past.
42
+ def past?
43
+ self < self.class.current
44
+ end
45
+
46
+ # Returns true if the date/time is in the future.
47
+ def future?
48
+ self > self.class.current
49
+ end
50
+
51
+ # Returns true if the date/time falls on a Saturday or Sunday.
52
+ def on_weekend?
53
+ WEEKEND_DAYS.include?(wday)
54
+ end
55
+
56
+ # Returns true if the date/time does not fall on a Saturday or Sunday.
57
+ def on_weekday?
58
+ !WEEKEND_DAYS.include?(wday)
59
+ end
60
+
61
+ # Returns a new date/time the specified number of days ago.
62
+ def days_ago(days)
63
+ advance(:days => -days)
64
+ end
65
+
66
+ # Returns a new date/time the specified number of days in the future.
67
+ def days_since(days)
68
+ advance(:days => days)
69
+ end
70
+
71
+ # Returns a new date/time the specified number of weeks ago.
72
+ def weeks_ago(weeks)
73
+ advance(:weeks => -weeks)
74
+ end
75
+
76
+ # Returns a new date/time the specified number of weeks in the future.
77
+ def weeks_since(weeks)
78
+ advance(:weeks => weeks)
79
+ end
80
+
81
+ # Returns a new date/time the specified number of months ago.
82
+ def months_ago(months)
83
+ advance(:months => -months)
84
+ end
85
+
86
+ # Returns a new date/time the specified number of months in the future.
87
+ def months_since(months)
88
+ advance(:months => months)
89
+ end
90
+
91
+ # Returns a new date/time the specified number of years ago.
92
+ def years_ago(years)
93
+ advance(:years => -years)
94
+ end
95
+
96
+ # Returns a new date/time the specified number of years in the future.
97
+ def years_since(years)
98
+ advance(:years => years)
99
+ end
100
+
101
+ # Returns a new date/time at the start of the month.
102
+ #
103
+ # today = Date.today # => Thu, 18 Jun 2015
104
+ # today.beginning_of_month # => Mon, 01 Jun 2015
105
+ #
106
+ # +DateTime+ objects will have a time set to 0:00.
107
+ #
108
+ # now = DateTime.current # => Thu, 18 Jun 2015 15:23:13 +0000
109
+ # now.beginning_of_month # => Mon, 01 Jun 2015 00:00:00 +0000
110
+ def beginning_of_month
111
+ first_hour(change(:day => 1))
112
+ end
113
+ alias :at_beginning_of_month :beginning_of_month
114
+
115
+ # Returns a new date/time at the start of the quarter.
116
+ #
117
+ # today = Date.today # => Fri, 10 Jul 2015
118
+ # today.beginning_of_quarter # => Wed, 01 Jul 2015
119
+ #
120
+ # +DateTime+ objects will have a time set to 0:00.
121
+ #
122
+ # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
123
+ # now.beginning_of_quarter # => Wed, 01 Jul 2015 00:00:00 +0000
124
+ def beginning_of_quarter
125
+ first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month }
126
+ beginning_of_month.change(:month => first_quarter_month)
127
+ end
128
+ alias :at_beginning_of_quarter :beginning_of_quarter
129
+
130
+ # Returns a new date/time at the end of the quarter.
131
+ #
132
+ # today = Date.today # => Fri, 10 Jul 2015
133
+ # today.end_of_quarter # => Wed, 30 Sep 2015
134
+ #
135
+ # +DateTime+ objects will have a time set to 23:59:59.
136
+ #
137
+ # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
138
+ # now.end_of_quarter # => Wed, 30 Sep 2015 23:59:59 +0000
139
+ def end_of_quarter
140
+ last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month }
141
+ beginning_of_month.change(:month => last_quarter_month).end_of_month
142
+ end
143
+ alias :at_end_of_quarter :end_of_quarter
144
+
145
+ # Returns a new date/time at the beginning of the year.
146
+ #
147
+ # today = Date.today # => Fri, 10 Jul 2015
148
+ # today.beginning_of_year # => Thu, 01 Jan 2015
149
+ #
150
+ # +DateTime+ objects will have a time set to 0:00.
151
+ #
152
+ # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
153
+ # now.beginning_of_year # => Thu, 01 Jan 2015 00:00:00 +0000
154
+ def beginning_of_year
155
+ change(:month => 1).beginning_of_month
156
+ end
157
+ alias :at_beginning_of_year :beginning_of_year
158
+
159
+ # Returns a new date/time representing the given day in the next week.
160
+ #
161
+ # today = Date.today # => Thu, 07 May 2015
162
+ # today.next_week # => Mon, 11 May 2015
163
+ #
164
+ # The +given_day_in_next_week+ defaults to the beginning of the week
165
+ # which is determined by +Date.beginning_of_week+ or +config.beginning_of_week+
166
+ # when set.
167
+ #
168
+ # today = Date.today # => Thu, 07 May 2015
169
+ # today.next_week(:friday) # => Fri, 15 May 2015
170
+ #
171
+ # +DateTime+ objects have their time set to 0:00 unless +same_time+ is true.
172
+ #
173
+ # now = DateTime.current # => Thu, 07 May 2015 13:31:16 +0000
174
+ # now.next_week # => Mon, 11 May 2015 00:00:00 +0000
175
+ def next_week(given_day_in_next_week = Date.beginning_of_week, same_time: false)
176
+ result = first_hour(weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week)))
177
+ same_time ? copy_time_to(result) : result
178
+ end
179
+
180
+ # Returns a new date/time representing the next weekday.
181
+ def next_weekday
182
+ if next_day.on_weekend?
183
+ next_week(:monday, same_time: true)
184
+ else
185
+ next_day
186
+ end
187
+ end
188
+
189
+ # Short-hand for months_since(1).
190
+ def next_month
191
+ months_since(1)
192
+ end
193
+
194
+ # Short-hand for months_since(3)
195
+ def next_quarter
196
+ months_since(3)
197
+ end
198
+
199
+ # Short-hand for years_since(1).
200
+ def next_year
201
+ years_since(1)
202
+ end
203
+
204
+ # Returns a new date/time representing the given day in the previous week.
205
+ # Week is assumed to start on +start_day+, default is
206
+ # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
207
+ # DateTime objects have their time set to 0:00 unless +same_time+ is true.
208
+ def prev_week(start_day = Date.beginning_of_week, same_time: false)
209
+ result = first_hour(weeks_ago(1).beginning_of_week.days_since(days_span(start_day)))
210
+ same_time ? copy_time_to(result) : result
211
+ end
212
+ alias_method :last_week, :prev_week
213
+
214
+ # Returns a new date/time representing the previous weekday.
215
+ def prev_weekday
216
+ if prev_day.on_weekend?
217
+ copy_time_to(beginning_of_week(:friday))
218
+ else
219
+ prev_day
220
+ end
221
+ end
222
+ alias_method :last_weekday, :prev_weekday
223
+
224
+ # Short-hand for months_ago(1).
225
+ def prev_month
226
+ months_ago(1)
227
+ end
228
+ alias_method :last_month, :prev_month
229
+
230
+ # Short-hand for months_ago(3).
231
+ def prev_quarter
232
+ months_ago(3)
233
+ end
234
+ alias_method :last_quarter, :prev_quarter
235
+
236
+ # Short-hand for years_ago(1).
237
+ def prev_year
238
+ years_ago(1)
239
+ end
240
+ alias_method :last_year, :prev_year
241
+
242
+ # Returns the number of days to the start of the week on the given day.
243
+ # Week is assumed to start on +start_day+, default is
244
+ # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
245
+ def days_to_week_start(start_day = Date.beginning_of_week)
246
+ start_day_number = DAYS_INTO_WEEK[start_day]
247
+ current_day_number = wday != 0 ? wday - 1 : 6
248
+ (current_day_number - start_day_number) % 7
249
+ end
250
+
251
+ # Returns a new date/time representing the start of this week on the given day.
252
+ # Week is assumed to start on +start_day+, default is
253
+ # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
254
+ # +DateTime+ objects have their time set to 0:00.
255
+ def beginning_of_week(start_day = Date.beginning_of_week)
256
+ result = days_ago(days_to_week_start(start_day))
257
+ acts_like?(:time) ? result.midnight : result
258
+ end
259
+ alias :at_beginning_of_week :beginning_of_week
260
+
261
+ # Returns Monday of this week assuming that week starts on Monday.
262
+ # +DateTime+ objects have their time set to 0:00.
263
+ def monday
264
+ beginning_of_week(:monday)
265
+ end
266
+
267
+ # Returns a new date/time representing the end of this week on the given day.
268
+ # Week is assumed to start on +start_day+, default is
269
+ # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
270
+ # DateTime objects have their time set to 23:59:59.
271
+ def end_of_week(start_day = Date.beginning_of_week)
272
+ last_hour(days_since(6 - days_to_week_start(start_day)))
273
+ end
274
+ alias :at_end_of_week :end_of_week
275
+
276
+ # Returns Sunday of this week assuming that week starts on Monday.
277
+ # +DateTime+ objects have their time set to 23:59:59.
278
+ def sunday
279
+ end_of_week(:monday)
280
+ end
281
+
282
+ # Returns a new date/time representing the end of the month.
283
+ # DateTime objects will have a time set to 23:59:59.
284
+ def end_of_month
285
+ last_day = ::Time.days_in_month(month, year)
286
+ last_hour(days_since(last_day - day))
287
+ end
288
+ alias :at_end_of_month :end_of_month
289
+
290
+ # Returns a new date/time representing the end of the year.
291
+ # DateTime objects will have a time set to 23:59:59.
292
+ def end_of_year
293
+ change(:month => 12).end_of_month
294
+ end
295
+ alias :at_end_of_year :end_of_year
296
+
297
+ # Returns a Range representing the whole week of the current date/time.
298
+ # Week starts on start_day, default is <tt>Date.week_start</tt> or <tt>config.week_start</tt> when set.
299
+ def all_week(start_day = Date.beginning_of_week)
300
+ beginning_of_week(start_day)..end_of_week(start_day)
301
+ end
302
+
303
+ # Returns a Range representing the whole month of the current date/time.
304
+ def all_month
305
+ beginning_of_month..end_of_month
306
+ end
307
+
308
+ # Returns a Range representing the whole quarter of the current date/time.
309
+ def all_quarter
310
+ beginning_of_quarter..end_of_quarter
311
+ end
312
+
313
+ # Returns a Range representing the whole year of the current date/time.
314
+ def all_year
315
+ beginning_of_year..end_of_year
316
+ end
317
+
318
+ private
319
+ def first_hour(date_or_time)
320
+ date_or_time.acts_like?(:time) ? date_or_time.beginning_of_day : date_or_time
321
+ end
322
+
323
+ def last_hour(date_or_time)
324
+ date_or_time.acts_like?(:time) ? date_or_time.end_of_day : date_or_time
325
+ end
326
+
327
+ def days_span(day)
328
+ (DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
329
+ end
330
+
331
+ def copy_time_to(other)
332
+ other.change(hour: hour, min: min, sec: sec, usec: try(:usec))
333
+ end
334
+ end
335
+ end
@@ -0,0 +1,18 @@
1
+ require 'active_support/core_ext/module/attribute_accessors'
2
+
3
+ module DateAndTime
4
+ module Compatibility
5
+ # If true, +to_time+ preserves the timezone offset of receiver.
6
+ #
7
+ # NOTE: With Ruby 2.4+ the default for +to_time+ changed from
8
+ # converting to the local system time, to preserving the offset
9
+ # of the receiver. For backwards compatibility we're overriding
10
+ # this behavior, but new apps will have an initializer that sets
11
+ # this to true, because the new behavior is preferred.
12
+ mattr_accessor(:preserve_timezone, instance_writer: false) { false }
13
+
14
+ def to_time
15
+ preserve_timezone ? getlocal(utc_offset) : getlocal
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,40 @@
1
+ module DateAndTime
2
+ module Zones
3
+ # Returns the simultaneous time in <tt>Time.zone</tt> if a zone is given or
4
+ # if Time.zone_default is set. Otherwise, it returns the current time.
5
+ #
6
+ # Time.zone = 'Hawaii' # => 'Hawaii'
7
+ # Time.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
8
+ # Date.new(2000).in_time_zone # => Sat, 01 Jan 2000 00:00:00 HST -10:00
9
+ #
10
+ # This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone
11
+ # instead of the operating system's time zone.
12
+ #
13
+ # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
14
+ # and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
15
+ #
16
+ # Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
17
+ # Date.new(2000).in_time_zone('Alaska') # => Sat, 01 Jan 2000 00:00:00 AKST -09:00
18
+ def in_time_zone(zone = ::Time.zone)
19
+ time_zone = ::Time.find_zone! zone
20
+ time = acts_like?(:time) ? self : nil
21
+
22
+ if time_zone
23
+ time_with_zone(time, time_zone)
24
+ else
25
+ time || self.to_time
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def time_with_zone(time, zone)
32
+ if time
33
+ ActiveSupport::TimeWithZone.new(time.utc? ? time : time.getutc, zone)
34
+ else
35
+ ActiveSupport::TimeWithZone.new(nil, zone, to_time(:utc))
36
+ end
37
+ end
38
+ end
39
+ end
40
+
@@ -1,3 +1,4 @@
1
+ require 'date'
1
2
  require 'active_support/core_ext/object/acts_like'
2
3
 
3
4
  class DateTime
@@ -0,0 +1,12 @@
1
+ require 'date'
2
+
3
+ class DateTime #:nodoc:
4
+ # No DateTime is ever blank:
5
+ #
6
+ # DateTime.now.blank? # => false
7
+ #
8
+ # @return [false]
9
+ def blank?
10
+ false
11
+ end
12
+ end
@@ -1,46 +1,61 @@
1
- require 'rational' unless RUBY_VERSION >= '1.9.2'
1
+ require 'date'
2
2
 
3
3
  class DateTime
4
4
  class << self
5
- # DateTimes aren't aware of DST rules, so use a consistent non-DST offset when creating a DateTime with an offset in the local zone
6
- def local_offset
7
- ::Time.local(2007).utc_offset.to_r / 86400
8
- end
9
-
10
- # Returns <tt>Time.zone.now.to_datetime</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise returns <tt>Time.now.to_datetime</tt>.
5
+ # Returns <tt>Time.zone.now.to_datetime</tt> when <tt>Time.zone</tt> or
6
+ # <tt>config.time_zone</tt> are set, otherwise returns
7
+ # <tt>Time.now.to_datetime</tt>.
11
8
  def current
12
9
  ::Time.zone ? ::Time.zone.now.to_datetime : ::Time.now.to_datetime
13
10
  end
14
11
  end
15
12
 
16
- # Tells whether the DateTime object's datetime lies in the past
17
- def past?
18
- self < ::DateTime.current
13
+ # Returns the number of seconds since 00:00:00.
14
+ #
15
+ # DateTime.new(2012, 8, 29, 0, 0, 0).seconds_since_midnight # => 0
16
+ # DateTime.new(2012, 8, 29, 12, 34, 56).seconds_since_midnight # => 45296
17
+ # DateTime.new(2012, 8, 29, 23, 59, 59).seconds_since_midnight # => 86399
18
+ def seconds_since_midnight
19
+ sec + (min * 60) + (hour * 3600)
19
20
  end
20
21
 
21
- # Tells whether the DateTime object's datetime lies in the future
22
- def future?
23
- self > ::DateTime.current
22
+ # Returns the number of seconds until 23:59:59.
23
+ #
24
+ # DateTime.new(2012, 8, 29, 0, 0, 0).seconds_until_end_of_day # => 86399
25
+ # DateTime.new(2012, 8, 29, 12, 34, 56).seconds_until_end_of_day # => 41103
26
+ # DateTime.new(2012, 8, 29, 23, 59, 59).seconds_until_end_of_day # => 0
27
+ def seconds_until_end_of_day
28
+ end_of_day.to_i - to_i
24
29
  end
25
30
 
26
- # Seconds since midnight: DateTime.now.seconds_since_midnight
27
- def seconds_since_midnight
28
- sec + (min * 60) + (hour * 3600)
31
+ # Returns the fraction of a second as a +Rational+
32
+ #
33
+ # DateTime.new(2012, 8, 29, 0, 0, 0.5).subsec # => (1/2)
34
+ def subsec
35
+ sec_fraction
29
36
  end
30
37
 
31
- # Returns a new DateTime where one or more of the elements have been changed according to the +options+ parameter. The time options
32
- # (hour, minute, sec) reset cascadingly, so if only the hour is passed, then minute and sec is set to 0. If the hour and
33
- # minute is passed, then sec is set to 0.
38
+ # Returns a new DateTime where one or more of the elements have been changed
39
+ # according to the +options+ parameter. The time options (<tt>:hour</tt>,
40
+ # <tt>:min</tt>, <tt>:sec</tt>) reset cascadingly, so if only the hour is
41
+ # passed, then minute and sec is set to 0. If the hour and minute is passed,
42
+ # then sec is set to 0. The +options+ parameter takes a hash with any of these
43
+ # keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>,
44
+ # <tt>:min</tt>, <tt>:sec</tt>, <tt>:offset</tt>, <tt>:start</tt>.
45
+ #
46
+ # DateTime.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => DateTime.new(2012, 8, 1, 22, 35, 0)
47
+ # DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => DateTime.new(1981, 8, 1, 22, 35, 0)
48
+ # DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => DateTime.new(1981, 8, 29, 0, 0, 0)
34
49
  def change(options)
35
50
  ::DateTime.civil(
36
- options[:year] || year,
37
- options[:month] || month,
38
- options[:day] || day,
39
- options[:hour] || hour,
40
- options[:min] || (options[:hour] ? 0 : min),
41
- options[:sec] || ((options[:hour] || options[:min]) ? 0 : sec),
42
- options[:offset] || offset,
43
- options[:start] || start
51
+ options.fetch(:year, year),
52
+ options.fetch(:month, month),
53
+ options.fetch(:day, day),
54
+ options.fetch(:hour, hour),
55
+ options.fetch(:min, options[:hour] ? 0 : min),
56
+ options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec + sec_fraction),
57
+ options.fetch(:offset, offset),
58
+ options.fetch(:start, start)
44
59
  )
45
60
  end
46
61
 
@@ -49,26 +64,45 @@ class DateTime
49
64
  # <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
50
65
  # <tt>:minutes</tt>, <tt>:seconds</tt>.
51
66
  def advance(options)
67
+ unless options[:weeks].nil?
68
+ options[:weeks], partial_weeks = options[:weeks].divmod(1)
69
+ options[:days] = options.fetch(:days, 0) + 7 * partial_weeks
70
+ end
71
+
72
+ unless options[:days].nil?
73
+ options[:days], partial_days = options[:days].divmod(1)
74
+ options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
75
+ end
76
+
52
77
  d = to_date.advance(options)
53
78
  datetime_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
54
- seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600
55
- seconds_to_advance == 0 ? datetime_advanced_by_date : datetime_advanced_by_date.since(seconds_to_advance)
79
+ seconds_to_advance = \
80
+ options.fetch(:seconds, 0) +
81
+ options.fetch(:minutes, 0) * 60 +
82
+ options.fetch(:hours, 0) * 3600
83
+
84
+ if seconds_to_advance.zero?
85
+ datetime_advanced_by_date
86
+ else
87
+ datetime_advanced_by_date.since(seconds_to_advance)
88
+ end
56
89
  end
57
90
 
58
- # Returns a new DateTime representing the time a number of seconds ago
91
+ # Returns a new DateTime representing the time a number of seconds ago.
59
92
  # Do not use this method in combination with x.months, use months_ago instead!
60
93
  def ago(seconds)
61
94
  since(-seconds)
62
95
  end
63
96
 
64
- # Returns a new DateTime representing the time a number of seconds since the instance time
65
- # Do not use this method in combination with x.months, use months_since instead!
97
+ # Returns a new DateTime representing the time a number of seconds since the
98
+ # instance time. Do not use this method in combination with x.months, use
99
+ # months_since instead!
66
100
  def since(seconds)
67
101
  self + Rational(seconds.round, 86400)
68
102
  end
69
103
  alias :in :since
70
104
 
71
- # Returns a new DateTime representing the start of the day (0:00)
105
+ # Returns a new DateTime representing the start of the day (0:00).
72
106
  def beginning_of_day
73
107
  change(:hour => 0)
74
108
  end
@@ -76,57 +110,90 @@ class DateTime
76
110
  alias :at_midnight :beginning_of_day
77
111
  alias :at_beginning_of_day :beginning_of_day
78
112
 
79
- # Returns a new DateTime representing the end of the day (23:59:59)
113
+ # Returns a new DateTime representing the middle of the day (12:00)
114
+ def middle_of_day
115
+ change(:hour => 12)
116
+ end
117
+ alias :midday :middle_of_day
118
+ alias :noon :middle_of_day
119
+ alias :at_midday :middle_of_day
120
+ alias :at_noon :middle_of_day
121
+ alias :at_middle_of_day :middle_of_day
122
+
123
+ # Returns a new DateTime representing the end of the day (23:59:59).
80
124
  def end_of_day
81
125
  change(:hour => 23, :min => 59, :sec => 59)
82
126
  end
127
+ alias :at_end_of_day :end_of_day
83
128
 
84
- # 1.9.3 defines + and - on DateTime, < 1.9.3 do not.
85
- if DateTime.public_instance_methods(false).include?(:+)
86
- def plus_with_duration(other) #:nodoc:
87
- if ActiveSupport::Duration === other
88
- other.since(self)
89
- else
90
- plus_without_duration(other)
91
- end
92
- end
93
- alias_method :plus_without_duration, :+
94
- alias_method :+, :plus_with_duration
95
-
96
- def minus_with_duration(other) #:nodoc:
97
- if ActiveSupport::Duration === other
98
- plus_with_duration(-other)
99
- else
100
- minus_without_duration(other)
101
- end
102
- end
103
- alias_method :minus_without_duration, :-
104
- alias_method :-, :minus_with_duration
129
+ # Returns a new DateTime representing the start of the hour (hh:00:00).
130
+ def beginning_of_hour
131
+ change(:min => 0)
105
132
  end
133
+ alias :at_beginning_of_hour :beginning_of_hour
106
134
 
107
- # Adjusts DateTime to UTC by adding its offset value; offset is set to 0
108
- #
109
- # Example:
135
+ # Returns a new DateTime representing the end of the hour (hh:59:59).
136
+ def end_of_hour
137
+ change(:min => 59, :sec => 59)
138
+ end
139
+ alias :at_end_of_hour :end_of_hour
140
+
141
+ # Returns a new DateTime representing the start of the minute (hh:mm:00).
142
+ def beginning_of_minute
143
+ change(:sec => 0)
144
+ end
145
+ alias :at_beginning_of_minute :beginning_of_minute
146
+
147
+ # Returns a new DateTime representing the end of the minute (hh:mm:59).
148
+ def end_of_minute
149
+ change(:sec => 59)
150
+ end
151
+ alias :at_end_of_minute :end_of_minute
152
+
153
+ # Returns a <tt>Time</tt> instance of the simultaneous time in the system timezone.
154
+ def localtime(utc_offset = nil)
155
+ utc = new_offset(0)
156
+
157
+ Time.utc(
158
+ utc.year, utc.month, utc.day,
159
+ utc.hour, utc.min, utc.sec + utc.sec_fraction
160
+ ).getlocal(utc_offset)
161
+ end
162
+ alias_method :getlocal, :localtime
163
+
164
+ # Returns a <tt>Time</tt> instance of the simultaneous time in the UTC timezone.
110
165
  #
111
- # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600
112
- # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc # => Mon, 21 Feb 2005 16:11:12 +0000
166
+ # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600
167
+ # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc # => Mon, 21 Feb 2005 16:11:12 UTC
113
168
  def utc
114
- new_offset(0)
169
+ utc = new_offset(0)
170
+
171
+ Time.utc(
172
+ utc.year, utc.month, utc.day,
173
+ utc.hour, utc.min, utc.sec + utc.sec_fraction
174
+ )
115
175
  end
176
+ alias_method :getgm, :utc
116
177
  alias_method :getutc, :utc
178
+ alias_method :gmtime, :utc
117
179
 
118
- # Returns true if offset == 0
180
+ # Returns +true+ if <tt>offset == 0</tt>.
119
181
  def utc?
120
182
  offset == 0
121
183
  end
122
184
 
123
- # Returns the offset value in seconds
185
+ # Returns the offset value in seconds.
124
186
  def utc_offset
125
187
  (offset * 86400).to_i
126
188
  end
127
189
 
128
- # Layers additional behavior on DateTime#<=> so that Time and ActiveSupport::TimeWithZone instances can be compared with a DateTime
190
+ # Layers additional behavior on DateTime#<=> so that Time and
191
+ # ActiveSupport::TimeWithZone instances can be compared with a DateTime.
129
192
  def <=>(other)
130
- super other.to_datetime
193
+ if other.respond_to? :to_datetime
194
+ super other.to_datetime rescue nil
195
+ else
196
+ super
197
+ end
131
198
  end
132
199
  end
@@ -0,0 +1,5 @@
1
+ require 'active_support/core_ext/date_and_time/compatibility'
2
+
3
+ class DateTime
4
+ prepend DateAndTime::Compatibility
5
+ end