lazier 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. data/.gitignore +6 -0
  2. data/.travis.yml +8 -0
  3. data/.yardopts +1 -0
  4. data/Gemfile +9 -0
  5. data/Gemfile.lock +82 -0
  6. data/README.md +33 -0
  7. data/Rakefile +18 -0
  8. data/doc/Lazier.html +557 -0
  9. data/doc/Lazier/Boolean.html +297 -0
  10. data/doc/Lazier/DateTime.html +787 -0
  11. data/doc/Lazier/DateTime/ClassMethods.html +1592 -0
  12. data/doc/Lazier/Exceptions.html +125 -0
  13. data/doc/Lazier/Exceptions/Dump.html +133 -0
  14. data/doc/Lazier/Hash.html +393 -0
  15. data/doc/Lazier/Math.html +130 -0
  16. data/doc/Lazier/Math/ClassMethods.html +362 -0
  17. data/doc/Lazier/Object.html +1565 -0
  18. data/doc/Lazier/Pathname.html +225 -0
  19. data/doc/Lazier/Settings.html +1249 -0
  20. data/doc/Lazier/String.html +471 -0
  21. data/doc/Lazier/TimeZone.html +1675 -0
  22. data/doc/Lazier/TimeZone/ClassMethods.html +1055 -0
  23. data/doc/Lazier/Version.html +189 -0
  24. data/doc/_index.html +306 -0
  25. data/doc/class_list.html +53 -0
  26. data/doc/css/common.css +1 -0
  27. data/doc/css/full_list.css +57 -0
  28. data/doc/css/style.css +328 -0
  29. data/doc/file.README.html +107 -0
  30. data/doc/file_list.html +55 -0
  31. data/doc/frames.html +28 -0
  32. data/doc/index.html +107 -0
  33. data/doc/js/app.js +214 -0
  34. data/doc/js/full_list.js +173 -0
  35. data/doc/js/jquery.js +4 -0
  36. data/doc/method_list.html +652 -0
  37. data/doc/top-level-namespace.html +112 -0
  38. data/lazier.gemspec +36 -0
  39. data/lib/lazier.rb +127 -0
  40. data/lib/lazier/boolean.rb +26 -0
  41. data/lib/lazier/datetime.rb +548 -0
  42. data/lib/lazier/exceptions.rb +14 -0
  43. data/lib/lazier/hash.rb +40 -0
  44. data/lib/lazier/math.rb +47 -0
  45. data/lib/lazier/object.rb +163 -0
  46. data/lib/lazier/pathname.rb +26 -0
  47. data/lib/lazier/settings.rb +118 -0
  48. data/lib/lazier/string.rb +54 -0
  49. data/lib/lazier/version.rb +24 -0
  50. data/spec/coverage_helper.rb +19 -0
  51. data/spec/cowtech-extensions/boolean_spec.rb +30 -0
  52. data/spec/cowtech-extensions/datetime_spec.rb +352 -0
  53. data/spec/cowtech-extensions/hash_spec.rb +30 -0
  54. data/spec/cowtech-extensions/math_spec.rb +41 -0
  55. data/spec/cowtech-extensions/object_spec.rb +231 -0
  56. data/spec/cowtech-extensions/pathname_spec.rb +21 -0
  57. data/spec/cowtech-extensions/settings_spec.rb +118 -0
  58. data/spec/cowtech-extensions/string_spec.rb +45 -0
  59. data/spec/lazier_spec.rb +57 -0
  60. data/spec/spec_helper.rb +16 -0
  61. metadata +292 -0
@@ -0,0 +1,112 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; Documentation by YARD 0.8.2.1
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" media="screen" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" media="screen" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ hasFrames = window.top.frames.main ? true : false;
19
+ relpath = '';
20
+ framesUrl = "frames.html#!" + escape(window.location.href);
21
+ </script>
22
+
23
+
24
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
25
+
26
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
27
+
28
+
29
+ </head>
30
+ <body>
31
+ <div id="header">
32
+ <div id="menu">
33
+
34
+ <a href="_index.html">Index</a> &raquo;
35
+
36
+
37
+ <span class="title">Top Level Namespace</span>
38
+
39
+
40
+ <div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
41
+ </div>
42
+
43
+ <div id="search">
44
+
45
+ <a class="full_list_link" id="class_list_link"
46
+ href="class_list.html">
47
+ Class List
48
+ </a>
49
+
50
+ <a class="full_list_link" id="method_list_link"
51
+ href="method_list.html">
52
+ Method List
53
+ </a>
54
+
55
+ <a class="full_list_link" id="file_list_link"
56
+ href="file_list.html">
57
+ File List
58
+ </a>
59
+
60
+ </div>
61
+ <div class="clear"></div>
62
+ </div>
63
+
64
+ <iframe id="search_frame"></iframe>
65
+
66
+ <div id="content"><h1>Top Level Namespace
67
+
68
+
69
+
70
+ </h1>
71
+
72
+ <dl class="box">
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+ </dl>
82
+ <div class="clear"></div>
83
+
84
+ <h2>Defined Under Namespace</h2>
85
+ <p class="children">
86
+
87
+
88
+ <strong class="modules">Modules:</strong> <span class='object_link'><a href="Lazier.html" title="Lazier (module)">Lazier</a></span>
89
+
90
+
91
+
92
+
93
+ </p>
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+ </div>
104
+
105
+ <div id="footer">
106
+ Generated on Wed Aug 1 08:57:55 2012 by
107
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
108
+ 0.8.2.1 (ruby-1.9.2).
109
+ </div>
110
+
111
+ </body>
112
+ </html>
data/lazier.gemspec ADDED
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the lazier gem. Copyright (C) 2011 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ require File.expand_path('../lib/lazier/version', __FILE__)
8
+
9
+ Gem::Specification.new do |gem|
10
+ gem.name = "lazier"
11
+ gem.version = Lazier::Version::STRING
12
+ gem.homepage = "http://github.com/ShogunPanda/lazier"
13
+ gem.summary = %q{Several Ruby object enhancements.}
14
+ gem.description = %q{Several Ruby object enhancements.}
15
+ gem.rubyforge_project = "lazier"
16
+
17
+ gem.authors = ["Shogun"]
18
+ gem.email = ["shogun_panda@me.com"]
19
+
20
+ gem.files = `git ls-files`.split($\)
21
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
22
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
23
+ gem.require_paths = ["lib"]
24
+
25
+ gem.add_dependency("json", "~> 1.7.0")
26
+ gem.add_dependency("actionpack", "~> 3.0")
27
+ gem.add_dependency("tzinfo", "~> 0.3.0")
28
+
29
+ gem.add_development_dependency("rspec", "~> 2.11.0")
30
+ gem.add_development_dependency("rake", "~> 0.9.0")
31
+ gem.add_development_dependency("simplecov", "~> 0.6.0")
32
+ gem.add_development_dependency("pry", ">= 0")
33
+ gem.add_development_dependency("yard", "~> 0.8.0")
34
+ gem.add_development_dependency("redcarpet", "~> 2.1.0")
35
+ gem.add_development_dependency("github-markup", "~> 0.7.0")
36
+ end
data/lib/lazier.rb ADDED
@@ -0,0 +1,127 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the lazier gem. Copyright (C) 2011 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ $KCODE='UTF8' if RUBY_VERSION < '1.9'
8
+
9
+ require "json"
10
+ require "tzinfo"
11
+ require "active_support/all"
12
+ require "action_view"
13
+
14
+ require "lazier/version" if !defined?(Lazier::Version)
15
+ require "lazier/exceptions"
16
+ require "lazier/settings"
17
+ require "lazier/object"
18
+ require "lazier/boolean"
19
+ require "lazier/string"
20
+ require "lazier/hash"
21
+ require "lazier/datetime"
22
+ require "lazier/math"
23
+ require "lazier/pathname"
24
+
25
+ # Several Ruby object enhancements.
26
+ module Lazier
27
+ # Checks if we are running under Ruby 1.8
28
+ #
29
+ # @return [Boolean] `true` for Ruby 1.8, `false` otherwise.
30
+ def self.is_ruby_18?
31
+ RUBY_VERSION =~ /^1\.8/
32
+ end
33
+
34
+ # Returns the settings for the extensions
35
+ #
36
+ # @return [Settings] The settings for the extensions.
37
+ def self.settings
38
+ ::Lazier::Settings.instance
39
+ end
40
+
41
+ # Loads the extensions.
42
+ #
43
+ # @param what [Array] The modules to load. Valid values are:
44
+ # @option object Extensions for all objects.
45
+ # @option boolean Extensions for boolean values.
46
+ # @option string Extensions for strings.
47
+ # @option hash Extensions for hashs.
48
+ # @option datetime Extensions date and time objects.
49
+ # @option math Extensions for Math module.
50
+ # @option pathname Extensions for path objects.
51
+ # @return [Settings] The settings for the extensions.
52
+ def self.load!(*what)
53
+ what = ["object", "boolean", "string", "hash", "datetime", "math", "pathname"] if what.count == 0
54
+ what.collect! { |w| w.to_s }
55
+
56
+ # Dependency resolving
57
+ what << "object" if what.include?("datetime")
58
+ what << "object" if what.include?("math")
59
+ what.compact.uniq!
60
+
61
+ if what.include?("object") then
62
+ ::Object.class_eval do
63
+ include ::Lazier::Object
64
+ end
65
+ end
66
+
67
+ if what.include?("boolean") then
68
+ ::TrueClass.class_eval do
69
+ include ::Lazier::Object
70
+ include ::Lazier::Boolean
71
+ end
72
+
73
+ ::FalseClass.class_eval do
74
+ include ::Lazier::Object
75
+ include ::Lazier::Boolean
76
+ end
77
+ end
78
+
79
+ if what.include?("string") then
80
+ ::String.class_eval do
81
+ include ::Lazier::String
82
+ end
83
+ end
84
+
85
+ if what.include?("hash") then
86
+ ::Hash.class_eval do
87
+ include ::Lazier::Hash
88
+ end
89
+ end
90
+
91
+ if what.include?("datetime") then
92
+ ::Time.class_eval do
93
+ include ::Lazier::DateTime
94
+ end
95
+
96
+ ::Date.class_eval do
97
+ include ::Lazier::DateTime
98
+ end
99
+
100
+ ::DateTime.class_eval do
101
+ include ::Lazier::DateTime
102
+ end
103
+
104
+ ::ActiveSupport::TimeZone.class_eval do
105
+ include ::Lazier::TimeZone
106
+ end
107
+ end
108
+
109
+ if what.include?("math") then
110
+ ::Math.class_eval do
111
+ include ::Lazier::Math
112
+ end
113
+ end
114
+
115
+ if what.include?("pathname") then
116
+ require "pathname"
117
+
118
+ ::Pathname.class_eval do
119
+ include ::Lazier::Pathname
120
+ end
121
+ end
122
+
123
+ yield if block_given?
124
+
125
+ ::Lazier::Settings.instance
126
+ end
127
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the lazier gem. Copyright (C) 2011 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ module Lazier
8
+ # Extension for the boolean values.
9
+ module Boolean
10
+ extend ::ActiveSupport::Concern
11
+
12
+ # Converts the boolean to an integer.
13
+ #
14
+ # @return [Fixnum] `1` for `true`, `0` for `false`.
15
+ def to_i
16
+ (self == true) ? 1 : 0
17
+ end
18
+
19
+ # Returns the boolean itself for use in form helpers.
20
+ #
21
+ # @return [Boolean] The boolean value.
22
+ def value
23
+ self
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,548 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the lazier gem. Copyright (C) 2011 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ module Lazier
8
+ # Extensions for date and time objects.
9
+ module DateTime
10
+ extend ::ActiveSupport::Concern
11
+
12
+ # General methods.
13
+ module ClassMethods
14
+ # Returns strings representations of days.
15
+ # @see Settings#setup_date_names
16
+ #
17
+ # @param short [Boolean] If return the abbreviated representations.
18
+ # @return [Array] Return string representations of days.
19
+ def days(short = true)
20
+ days = ::Lazier.settings.date_names[short ? :short_days : :long_days]
21
+ (1..7).to_a.collect { |i|
22
+ {:value => i.to_s, :label=> days[i - 1]}
23
+ }
24
+
25
+ end
26
+
27
+ # Returns strings representations of months.
28
+ # @see Settings#setup_date_names
29
+ #
30
+ # @param short [Boolean] If return the abbreviated representations.
31
+ # @return [Array] Return string representations of months.
32
+ def months(short = true)
33
+ months = ::Lazier.settings.date_names[short ? :short_months : :long_months]
34
+ (1..12).collect { |i|
35
+ {:value => i.to_s.rjust(2, "0"), :label=> months.at(i - 1)}
36
+ }
37
+ end
38
+
39
+ # Returns a range of years.
40
+ #
41
+ # ```ruby
42
+ # Date.years(3, false, 2010)
43
+ # # => [2007, 2008, 2009, 2010]
44
+ # ```
45
+ #
46
+ # ```ruby
47
+ # Date.years(1, true, 2010, true)
48
+ # # => [{:value=>2009, :label=>2009}, {:value=>2010, :label=>2010}, {:value=>2011, :label=>2011}]
49
+ # ```
50
+ #
51
+ #
52
+ # @param offset [Fixnum] The width of the range.
53
+ # @param also_future [Boolean] If return also future years.
54
+ # @param reference [Fixnum] The ending (or middle, if `also_future` is `true`) value of the range. Defaults to the current year.
55
+ # @param as_objects [Boolean] If to return years in hashes with `:value` and `label` keys.
56
+ # @return [Array] A range of years. Every entry is
57
+ def years(offset = 10, also_future = true, reference = nil, as_objects = false)
58
+ y = reference || ::Date.today.year
59
+ (y - offset..(also_future ? y + offset : y)).collect { |year| as_objects ? {:value => year, :label => year} : year }
60
+ end
61
+
62
+ # Returns all the availabe timezones.
63
+ #
64
+ # @return [Array]All the zone available.
65
+ def timezones
66
+ ::ActiveSupport::TimeZone.all
67
+ end
68
+
69
+ # Returns a list of names of all timezones.
70
+ #
71
+ # @param with_dst [Boolean] If include DST version of the zones.
72
+ # @param dst_label [String] Label for the DST indication. Defaults to `(DST)`.
73
+ # @return [Array] A list of names of timezones.
74
+ def list_timezones(with_dst = true, dst_label = nil)
75
+ ::ActiveSupport::TimeZone.list_all(with_dst, dst_label)
76
+ end
77
+
78
+ # Find a zone by its name.
79
+ #
80
+ # @param name [String] The zone name.
81
+ # @param dst_label [String] Label for the DST indication. Defaults to `(DST)`.
82
+ # @return [TimeZone] A timezone or `nil` if no zone was found.
83
+ def find_timezone(name = true, dst_label = nil)
84
+ ::ActiveSupport::TimeZone.find(name, dst_label)
85
+ end
86
+
87
+ # Returns a string representation of a timezone.
88
+ #
89
+ # ```ruby
90
+ # DateTime.parameterize_zone(ActiveSupport::TimeZone["Pacific Time (US & Canada)"])
91
+ # # => "-0800@pacific-time-us-canada"
92
+ # ```
93
+ # @param tz [TimeZone] The zone to represent.
94
+ # @param with_offset [Boolean] If to include offset into the representation.
95
+ # @return [String] A string representation which can be used for searches.
96
+ def parameterize_zone(tz, with_offset = true)
97
+ ::ActiveSupport::TimeZone::parameterize_zone(tz, with_offset)
98
+ end
99
+
100
+ # Finds a parameterized timezone.
101
+ # @see DateTime#parameterize_zone
102
+ #
103
+ # @param tz [String] The zone to unparameterize.
104
+ # @param as_string [Boolean] If return just the zone name.
105
+ # @param dst_label [String] Label for the DST indication. Defaults to `(DST)`.
106
+ # @return [String|TimeZone] The found timezone or `nil` if the zone is not valid.
107
+ def unparameterize_zone(tz, as_string = false, dst_label = nil)
108
+ ::ActiveSupport::TimeZone::unparameterize_zone(tz, as_string, dst_label)
109
+ end
110
+
111
+ # Returns an offset in rational value.
112
+ #
113
+ # @param offset [Fixnum] The offset to convert.
114
+ # @return [Rational] The converted offset.
115
+ def rationalize_offset(offset)
116
+ ::ActiveSupport::TimeZone.rationalize_offset(offset)
117
+ end
118
+
119
+ # Returns the Easter (according to Gregorian calendar) date for the year.
120
+ # @see http://en.wikipedia.org/wiki/Computus#Anonymous_Gregorian_algorithm
121
+ #
122
+ # @param year [Fixnum] The year to compute the date for. Defaults to the current year.
123
+ # @return [Date] The Easter date for the year.
124
+ def easter(year = nil)
125
+ year = ::Date.today.year if !year.is_integer?
126
+
127
+ # Compute using Anonymouse Gregorian Algorithm: http://en.wikipedia.org/wiki/Computus#Anonymous_Gregorian_algorithm
128
+ a = year % 19
129
+ b = (year / 100.0).floor
130
+ c = year % 100
131
+ d = (b / 4.0).floor
132
+ e = b % 4
133
+ f = ((b + 8) / 25.0).floor
134
+ g = ((b - f + 1) / 3.0).floor
135
+ h = ((19 * a) + b - d - g + 15) % 30
136
+ i = (c / 4.0).floor
137
+ k = c % 4
138
+ l = (32 + (2 * e) + (2 * i) - h - k) % 7
139
+ m = ((a + (11 * h) + (22 * l)) / 451.0).floor
140
+
141
+ day = ((h + l - (7 * m) + 114) % 31) + 1
142
+ month = ((h + l - (7 * m) + 114) / 31.0).floor
143
+ ::Date.civil(year, month, day)
144
+ end
145
+
146
+ # Lookups a custom datetime format.
147
+ # @see Settings#setup_date_formats
148
+ #
149
+ # @param key [Symbol] The name of the format to search.
150
+ # @return [String] The format or the name itself (if the format has not been found).
151
+ def custom_format(key)
152
+ ::Lazier.settings.date_formats.fetch(key.to_sym, key).ensure_string
153
+ end
154
+
155
+ # Checks if the date is valid against to a specific format.
156
+ # @see DateTime#custom_format
157
+ #
158
+ # @param value [String] The value to check.
159
+ # @param format [String] The format to check the value against.
160
+ # @return [Boolean] `true` if the value is valid against the format, `false` otherwise.
161
+ def is_valid?(value, format = "%F %T")
162
+ rv = true
163
+
164
+ format = self.custom_format(format)
165
+
166
+ begin
167
+ ::DateTime.strptime(value.ensure_string, format)
168
+ rescue => e
169
+ rv = false
170
+ end
171
+
172
+ rv
173
+ end
174
+ end
175
+
176
+ # Returns the UTC::Time representation of the current datetime.
177
+ #
178
+ # @return [UTC::Time] The UTC::Time representation of the current datetime.
179
+ def utc_time
180
+ ua = (self.respond_to?(:utc) ? self : self.to_datetime).utc
181
+ ::Time.utc(ua.year, ua.month, ua.day, ua.hour, ua.min, ua.sec)
182
+ end
183
+
184
+ # Returns the number of months passed between the beginning of the base year and the current date.
185
+ #
186
+ # ```ruby
187
+ # DateTime.civil(2012, 6, 1).in_months(2011)
188
+ # # => 18
189
+ # ```
190
+ #
191
+ # @param base [DateTime] The base year to start computation from. Default to current year.
192
+ # @return [Fixnum] Returns the number of months passed between the beginning of the base year and the current date.
193
+ def in_months(base = nil)
194
+ base ||= ::Date.today.year
195
+ ((self.year) - base) * 12 + self.month
196
+ end
197
+
198
+ # Returns the current month number with leading 0.
199
+ #
200
+ # @return [String] The current month number with leading 0.
201
+ def padded_month
202
+ self.month.to_s.rjust(2, "0")
203
+ end
204
+
205
+ # Formats a datetime, looking up also custom formats.
206
+ # @see Settings#setup_date_formats
207
+ #
208
+ # @param format [String] A format or a custom format name to use for formatting.
209
+ # @return [String] The formatted date.
210
+ def lstrftime(format = nil)
211
+ rv = nil
212
+ names = ::Lazier.settings.date_names
213
+
214
+ final_format = ::DateTime.custom_format(format).ensure_string.gsub(/(%{1,2}:?[abz])/i) do |match|
215
+ mrv = match
216
+
217
+ # Handling of %z is to fix ruby 1.8 bug in OSX: http://bugs.ruby-lang.org/issues/2396
218
+ if match !~ /^%%/ then
219
+ case match
220
+ when "%a"
221
+ mrv = names[:short_days][self.wday]
222
+ when "%A"
223
+ mrv = names[:long_days][self.wday]
224
+ when "%b"
225
+ mrv = names[:short_months][self.month - 1]
226
+ when "%B"
227
+ mrv = names[:long_months][self.month - 1]
228
+ when "%z"
229
+ mrv = ::Lazier.is_ruby_18? ? self.formatted_offset(false) : nil
230
+ when "%:z"
231
+ mrv = ::Lazier.is_ruby_18? ? self.formatted_offset(true) : nil
232
+ end
233
+ end
234
+
235
+ mrv ? mrv.sub("%", "%%") : match
236
+ end
237
+
238
+ self.strftime(final_format)
239
+ end
240
+
241
+ # Formats a datetime in the current timezone.
242
+ #
243
+ # @param format [String] The format to use for formatting.
244
+ # @return [String] The formatted date.
245
+ def local_strftime(format = nil)
246
+ (self.respond_to?(:in_time_zone) ? self.in_time_zone : self).strftime(::DateTime.custom_format(format))
247
+ end
248
+
249
+ # Formats a datetime in the current timezone, looking up also custom formats.
250
+ # @see Settings#setup_date_formats
251
+ #
252
+ # @param format [String] A format or a custom format name.
253
+ # @return [String] The formatted date.
254
+ def local_lstrftime(format = nil)
255
+ (self.respond_to?(:in_time_zone) ? self.in_time_zone : self).lstrftime(format)
256
+ end
257
+ end
258
+
259
+ # Extensions for timezone objects.
260
+ module TimeZone
261
+ extend ::ActiveSupport::Concern
262
+
263
+ # General methods.
264
+ module ClassMethods
265
+ # Returns an offset in rational value.
266
+ #
267
+ # @param offset [Fixnum] The offset to convert.
268
+ # @return [Rational] The converted offset.
269
+ def rationalize_offset(offset)
270
+ offset = offset.try(:offset) if !offset.is_a?(::Fixnum)
271
+ ::TZInfo::OffsetRationals.rational_for_offset(offset.to_integer)
272
+ end
273
+
274
+ # Returns a +HH:MM formatted representation of the offset.
275
+ #
276
+ # @param offset [Rational|Fixnum] The offset to represent, in seconds or as a rational.
277
+ # @param colon [Boolean] If to put the colon in the output string.
278
+ # @return [String] The formatted offset.
279
+ def format_offset(offset, colon = true)
280
+ offset = (offset * 86400).to_i if offset.is_a?(::Rational)
281
+ offset.is_a?(::Fixnum) ? self.seconds_to_utc_offset(offset, colon) : nil
282
+ end
283
+
284
+ # Find a zone by its name.
285
+ #
286
+ # @param name [String] The zone name.
287
+ # @param dst_label [String] Label for the DST indication. Defaults to `(DST)`.
288
+ # @return [TimeZone] A timezone or `nil` if no zone was found.
289
+ def find(name, dst_label = nil)
290
+ catch(:zone) do
291
+ ::ActiveSupport::TimeZone.all.each do |zone|
292
+ zone.aliases.each do |zone_alias|
293
+ throw(:zone, zone) if [zone.to_str(zone_alias), zone.to_str_with_dst(dst_label, nil, zone_alias)].include?(name)
294
+ end
295
+ end
296
+
297
+ nil
298
+ end
299
+ end
300
+
301
+ # Returns a list of names of all timezones.
302
+ #
303
+ # @param with_dst [Boolean] If include DST version of the zones.
304
+ # @param dst_label [String] Label for the DST indication. Defaults to `(DST)`.
305
+ # @return [Array] A list of names of timezones.
306
+ def list_all(with_dst = true, dst_label = nil)
307
+ dst_label ||= "(DST)"
308
+ dst_key = "DST-#{dst_label}"
309
+ @zones_names ||= { "STANDARD" => ::ActiveSupport::TimeZone.all.collect(&:to_s) }
310
+
311
+ if with_dst && @zones_names[dst_key].blank? then
312
+ @zones_names[dst_key] = []
313
+
314
+ ::ActiveSupport::TimeZone.all.each do |zone|
315
+ zone.aliases.each do |zone_alias|
316
+ @zones_names[dst_key] << zone.to_str(zone_alias)
317
+ @zones_names[dst_key] << zone.to_str_with_dst(dst_label, nil, zone_alias) if zone.uses_dst? && zone_alias !~ /(#{Regexp.quote(dst_label)})$/
318
+ end
319
+ end
320
+
321
+ @zones_names[dst_key]= @zones_names[dst_key].uniq.compact.sort { |a,b| ::ActiveSupport::TimeZone.compare(a, b) } # Sort by name
322
+ end
323
+
324
+ @zones_names[with_dst ? dst_key : "STANDARD"]
325
+ end
326
+
327
+ # Returns a string representation of a timezone.
328
+ #
329
+ # ```ruby
330
+ # DateTime.parameterize_zone(ActiveSupport::TimeZone["Pacific Time (US & Canada)"])
331
+ # # => "-0800@pacific-time-us-canada"
332
+ # ```
333
+ # @param tz [TimeZone] The zone to represent.
334
+ # @param with_offset [Boolean] If to include offset into the representation.
335
+ # @return [String] A string representation which can be used for searches.
336
+ def parameterize_zone(tz, with_offset = true)
337
+ tz = tz.to_s if !tz.is_a?(::String)
338
+
339
+ if tz =~ /^(\([a-z]+([+-])(\d{2})(:?)(\d{2})\)\s(.+))$/i then
340
+ with_offset ? "#{$2}#{$3}#{$5}@#{$6.parameterize}" : $6.parameterize
341
+ elsif !with_offset then
342
+ tz.gsub(/^([+-]?(\d{2})(:?)(\d{2})@)/, "")
343
+ else
344
+ tz.parameterize
345
+ end
346
+ end
347
+
348
+ # Finds a parameterized timezone.
349
+ # @see DateTime#parameterize_zone
350
+ #
351
+ # @param tz [String] The zone to unparameterize.
352
+ # @param as_string [Boolean] If return just the zone name.
353
+ # @param dst_label [String] Label for the DST indication. Defaults to `(DST)`.
354
+ # @return [String|TimeZone] The found timezone or `nil` if the zone is not valid.
355
+ def unparameterize_zone(tz, as_string = false, dst_label = nil)
356
+ tz = self.parameterize_zone(tz, false)
357
+ rv = catch(:zone) do
358
+ self.list_all(true, dst_label).each do |zone|
359
+ throw(:zone, zone) if self.parameterize_zone(zone, false) =~ /(#{Regexp.quote(tz)})$/
360
+ end
361
+
362
+ nil
363
+ end
364
+
365
+ if rv then
366
+ (as_string ? rv : self.find(rv, dst_label))
367
+ else
368
+ nil
369
+ end
370
+ end
371
+
372
+ # Compares two timezones. They are sorted by the location name.
373
+ #
374
+ # @param left [String|TimeZone] The first zone name to compare.
375
+ # @param right [String|TimeZone] The second zone name to compare.
376
+ # @return [Fixnum] The result of comparison, like Ruby's operator `<=>`.
377
+ def compare(left, right)
378
+ left = left.to_str if left.is_a?(::ActiveSupport::TimeZone)
379
+ right = right.to_str if right.is_a?(::ActiveSupport::TimeZone)
380
+ left.ensure_string.split(" ", 2)[1] <=> right.ensure_string.split(" ", 2)[1]
381
+ end
382
+ end
383
+
384
+ # Returns a list of valid aliases (city names) for this timezone (basing on offset).
385
+ #
386
+ # @param dst_label [String] Label for the DST indication. Defaults to `(DST)`.
387
+ # @return [Array] A list of aliases for this timezone
388
+ def aliases(dst_label = nil)
389
+ reference = self.name
390
+ reference = self.class::MAPPING[self.name] if self.class::MAPPING.has_key?(self.name) # We are an alias
391
+ reference = reference.gsub("_", " ")
392
+
393
+ if @aliases.blank? then
394
+ # First we search for aliases by name
395
+ @aliases = [reference]
396
+
397
+ self.class::MAPPING.each do |name, zone|
398
+ if zone.gsub("_", " ") == reference then
399
+ if name == "International Date Line West" || name == "UTC" || name.include?("(US & Canada)")
400
+ @aliases << name
401
+ else
402
+ @aliases << reference.gsub(/\/.*/, "/" + name)
403
+ end
404
+ end
405
+ end
406
+
407
+ @aliases = @aliases.uniq.compact.sort
408
+ end
409
+
410
+ @aliases
411
+ end
412
+
413
+ # Returns the current offset for this timezone, taking care of Daylight Saving Time (DST).
414
+ #
415
+ # @param rational [Boolean] If to return the offset as a Rational.
416
+ # @param date [DateTime] The date to consider. Defaults to now.
417
+ # @return [Fixnum|Rational] The offset of this timezone.
418
+ def current_offset(rational = false, date = nil)
419
+ date ||= ::DateTime.now
420
+
421
+ dst_period = self.dst_period
422
+
423
+ rv = (self.period_for_utc(date.utc).dst? ? self.dst_offset : self.offset)
424
+ rational ? self.class.rationalize_offset(rv) : rv
425
+ end
426
+
427
+ # Returns the standard offset for this timezone.
428
+ #
429
+ # @param rational [Boolean] If to return the offset as a Rational.
430
+ # @return [Fixnum|Rational] The offset of this timezone.
431
+ def offset(rational = false)
432
+ rv = self.utc_offset
433
+ rational ? self.class.rationalize_offset(rv) : rv
434
+ end
435
+
436
+ # Gets a period for this timezone when the Daylight Saving Time (DST) is active (it takes care of different hemispheres).
437
+ #
438
+ # @param year [Fixnum] The year to which refer to. Defaults to the current year.
439
+ # @return [TimezonePeriod] A period when the Daylight Saving Time (DST) is active or `nil` if the timezone doesn't use DST for that year.
440
+ def dst_period(year = nil)
441
+ year ||= ::Date.today.year
442
+
443
+ nothern_summer = ::DateTime.civil(year, 7, 15).utc # This is a representation of a summer period in the Northern Hemisphere.
444
+ southern_summer = ::DateTime.civil(year, 1, 15).utc # This is a representation of a summer period in the Northern Hemisphere.
445
+
446
+ period = self.period_for_utc(nothern_summer)
447
+ period = self.period_for_utc(southern_summer) if !period.dst?
448
+ period.dst? ? period : nil
449
+ end
450
+
451
+ # Checks if the timezone uses Daylight Saving Time (DST) for that date or year.
452
+ #
453
+ # @param reference [Object] The date or year to check. Defaults to the current year.
454
+ # @return [Boolean] `true` if the zone uses DST for that date or year, `false` otherwise.
455
+ def uses_dst?(reference = nil)
456
+ if reference.respond_to?(:year) && reference.respond_to?(:utc) then # This is a date like object
457
+ self.dst_period(reference.year).present? && self.period_for_utc(reference.utc).dst?
458
+ else
459
+ self.dst_period(reference).present?
460
+ end
461
+ end
462
+
463
+ # Return the correction applied to the standard offset the timezone when the Daylight Saving Time (DST) is active.
464
+ #
465
+ # @param rational [Boolean] If to return the offset as a Rational.
466
+ # @param year [Fixnum] The year to which refer to. Defaults to the current year.
467
+ # @return [Fixnum|Rational] The correction for dst.
468
+ def dst_correction(rational = false, year = nil)
469
+ period = self.dst_period(year)
470
+ rv = period ? period.std_offset : 0
471
+ rational ? self.class.rationalize_offset(rv) : rv
472
+ end
473
+
474
+ # Returns the standard offset for this timezone timezone when the Daylight Saving Time (DST) is active.
475
+ #
476
+ # @param rational [Boolean] If to return the offset as a Rational.
477
+ # @param year [Fixnum] The year to which refer to. Defaults to the current year.
478
+ # @return [Fixnum|Rational] The DST offset for this timezone or `0`, if the timezone doesn't use DST for that year.
479
+ def dst_offset(rational = false, year = nil)
480
+ period = self.dst_period(year)
481
+ rv = period ? period.utc_total_offset : 0
482
+ rational ? self.class.rationalize_offset(rv) : rv
483
+ end
484
+
485
+ # Returns the name for this zone with Daylight Saving Time (DST) active.
486
+ #
487
+ # @param dst_label [String] Label for the DST indication. Defaults to `(DST)`.
488
+ # @param year [Fixnum] The year to which refer to. Defaults to the current year.
489
+ # @param name [String] The name to use for this zone. Defaults to the zone name.
490
+ # @return [String] The name for the zone with DST or `nil`, if the timezone doesn't use DST for that year.
491
+ def dst_name(dst_label = nil, year = nil, name = nil)
492
+ dst_label ||= "(DST)"
493
+ name ||= self.name
494
+
495
+ self.uses_dst?(year) ? "#{name} #{dst_label}" : nil
496
+ end
497
+
498
+ # Returns the name for this zone with Daylight Saving Time (DST) active.
499
+ #
500
+ # @param name [String] The name to use for this zone. Defaults to the zone name.
501
+ # @param colon [Boolean] If to put the colon in the output string.
502
+ # @return [String] The name for this zone.
503
+ def to_str(name = nil, colon = true)
504
+ name ||= self.aliases.first
505
+ "(GMT#{self.formatted_offset(colon)}) #{name}"
506
+ end
507
+
508
+ # Returns a string representation for this zone with Daylight Saving Time (DST) active.
509
+ #
510
+ # @param dst_label [String] Label for the DST indication. Defaults to `(DST)`.
511
+ # @param year [Fixnum] The year to which refer to. Defaults to the current year.
512
+ # @param name [String] The name to use for this zone. Defaults to the zone name.
513
+ # @return [String] The string representation for the zone with DST or `nil`, if the timezone doesn't use DST for that year.
514
+ def to_str_with_dst(dst_label = nil, year = nil, name = nil)
515
+ dst_label ||= "(DST)"
516
+ name ||= self.aliases.first
517
+
518
+ if self.uses_dst?(year) then
519
+ period = self.dst_period(year)
520
+ offset = self.class.seconds_to_utc_offset(period.utc_total_offset)
521
+ "(GMT#{offset}) #{name} #{dst_label}"
522
+ else
523
+ nil
524
+ end
525
+ end
526
+
527
+ # Returns a parametized string representation for this zone.
528
+ #
529
+ # @param with_offset [Boolean] If to include offset into the representation.
530
+ # @param name [String] The name to use for this zone. Defaults to the zone name.
531
+ # @return [String] The parametized string representation for this zone.
532
+ def to_str_parameterized(with_offset = true, name = nil)
533
+ ::ActiveSupport::TimeZone.parameterize_zone(name || self.to_str, with_offset)
534
+ end
535
+
536
+ # Returns a parametized string representation for this zone with Daylight Saving Time (DST) active.
537
+ #
538
+ # @param dst_label [String] Label for the DST indication. Defaults to `(DST)`.
539
+ # @param with_offset [Boolean] If to include offset into the representation.
540
+ # @param year [Fixnum] The year to which refer to. Defaults to the current year.
541
+ # @param name [String] The name to use for this zone. Defaults to the zone name.
542
+ # @return [String] The parametized string representation for this zone with DST or `nil`, if the timezone doesn't use DST for that year.
543
+ def to_str_with_dst_parameterized(dst_label = nil, with_offset = true, year = nil, name = nil)
544
+ rv = self.to_str_with_dst(dst_label, year, name)
545
+ rv ? ::ActiveSupport::TimeZone.parameterize_zone(rv) : nil
546
+ end
547
+ end
548
+ end