cowtech-extensions 2.1.3 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/.gitignore +1 -0
  2. data/.yardopts +1 -0
  3. data/README.md +10 -0
  4. data/Rakefile +6 -3
  5. data/cowtech-extensions.gemspec +9 -5
  6. data/doc/Cowtech.html +128 -0
  7. data/doc/Cowtech/Extensions.html +546 -0
  8. data/doc/Cowtech/Extensions/Boolean.html +297 -0
  9. data/doc/Cowtech/Extensions/DateTime.html +787 -0
  10. data/doc/Cowtech/Extensions/DateTime/ClassMethods.html +1592 -0
  11. data/doc/Cowtech/Extensions/Exceptions.html +125 -0
  12. data/doc/Cowtech/Extensions/Exceptions/Dump.html +133 -0
  13. data/doc/Cowtech/Extensions/Hash.html +393 -0
  14. data/doc/Cowtech/Extensions/Math.html +130 -0
  15. data/doc/Cowtech/Extensions/Math/ClassMethods.html +362 -0
  16. data/doc/Cowtech/Extensions/Object.html +1565 -0
  17. data/doc/Cowtech/Extensions/Pathname.html +225 -0
  18. data/doc/Cowtech/Extensions/Settings.html +1249 -0
  19. data/doc/Cowtech/Extensions/String.html +471 -0
  20. data/doc/Cowtech/Extensions/TimeZone.html +1210 -0
  21. data/doc/Cowtech/Extensions/TimeZone/ClassMethods.html +925 -0
  22. data/doc/Cowtech/Extensions/Version.html +189 -0
  23. data/doc/_index.html +305 -0
  24. data/doc/class_list.html +53 -0
  25. data/doc/css/common.css +1 -0
  26. data/doc/css/full_list.css +57 -0
  27. data/doc/css/style.css +328 -0
  28. data/doc/file.README.html +103 -0
  29. data/doc/file_list.html +55 -0
  30. data/doc/frames.html +28 -0
  31. data/doc/index.html +103 -0
  32. data/doc/js/app.js +214 -0
  33. data/doc/js/full_list.js +173 -0
  34. data/doc/js/jquery.js +4 -0
  35. data/doc/method_list.html +620 -0
  36. data/doc/top-level-namespace.html +112 -0
  37. data/lib/cowtech-extensions.rb +47 -16
  38. data/lib/cowtech-extensions/boolean.rb +8 -1
  39. data/lib/cowtech-extensions/datetime.rb +377 -71
  40. data/lib/cowtech-extensions/exceptions.rb +16 -0
  41. data/lib/cowtech-extensions/hash.rb +20 -9
  42. data/lib/cowtech-extensions/math.rb +15 -8
  43. data/lib/cowtech-extensions/object.rb +84 -27
  44. data/lib/cowtech-extensions/pathname.rb +10 -1
  45. data/lib/cowtech-extensions/settings.rb +120 -0
  46. data/lib/cowtech-extensions/string.rb +30 -3
  47. data/lib/cowtech-extensions/version.rb +11 -2
  48. data/spec/coverage_helper.rb +19 -0
  49. data/spec/cowtech-extensions/boolean_spec.rb +4 -0
  50. data/spec/cowtech-extensions/datetime_spec.rb +238 -79
  51. data/spec/cowtech-extensions/hash_spec.rb +5 -2
  52. data/spec/cowtech-extensions/math_spec.rb +14 -4
  53. data/spec/cowtech-extensions/object_spec.rb +19 -1
  54. data/spec/cowtech-extensions/pathname_spec.rb +5 -1
  55. data/spec/cowtech-extensions/settings_spec.rb +101 -0
  56. data/spec/cowtech-extensions/string_spec.rb +13 -0
  57. data/spec/cowtech-extensions_spec.rb +33 -13
  58. data/spec/spec_helper.rb +2 -5
  59. metadata +182 -97
  60. data/lib/cowtech-extensions/utils.rb +0 -74
@@ -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="Cowtech.html" title="Cowtech (module)">Cowtech</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 Thu Jul 19 17:17:50 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>
@@ -6,10 +6,12 @@
6
6
 
7
7
  $KCODE='UTF8' if RUBY_VERSION < '1.9'
8
8
 
9
+ require "json"
9
10
  require "active_support/all"
10
11
  require "action_view"
11
12
 
12
- require "cowtech-extensions/utils"
13
+ require "cowtech-extensions/exceptions"
14
+ require "cowtech-extensions/settings"
13
15
  require "cowtech-extensions/object"
14
16
  require "cowtech-extensions/boolean"
15
17
  require "cowtech-extensions/string"
@@ -18,12 +20,35 @@ require "cowtech-extensions/datetime"
18
20
  require "cowtech-extensions/math"
19
21
  require "cowtech-extensions/pathname"
20
22
 
23
+ # This is the top level module for Cowtech libraries.
21
24
  module Cowtech
25
+ # Several Ruby object enhancements.
22
26
  module Extensions
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.
23
37
  def self.settings
24
- Cowtech::Extensions::Settings.instance
38
+ ::Cowtech::Extensions::Settings.instance
25
39
  end
26
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.
27
52
  def self.load!(*what)
28
53
  what = ["object", "boolean", "string", "hash", "datetime", "math", "pathname"] if what.count == 0
29
54
  what.collect! { |w| w.to_s }
@@ -32,51 +57,55 @@ module Cowtech
32
57
 
33
58
  if what.include?("object") then
34
59
  ::Object.class_eval do
35
- include Cowtech::Extensions::Object
60
+ include ::Cowtech::Extensions::Object
36
61
  end
37
62
  end
38
63
 
39
64
  if what.include?("boolean") then
40
65
  ::TrueClass.class_eval do
41
- include Cowtech::Extensions::Object
42
- include Cowtech::Extensions::Boolean
66
+ include ::Cowtech::Extensions::Object
67
+ include ::Cowtech::Extensions::Boolean
43
68
  end
44
69
 
45
70
  ::FalseClass.class_eval do
46
- include Cowtech::Extensions::Object
47
- include Cowtech::Extensions::Boolean
71
+ include ::Cowtech::Extensions::Object
72
+ include ::Cowtech::Extensions::Boolean
48
73
  end
49
74
  end
50
75
 
51
76
  if what.include?("string") then
52
77
  ::String.class_eval do
53
- include Cowtech::Extensions::String
78
+ include ::Cowtech::Extensions::String
54
79
  end
55
80
  end
56
81
 
57
82
  if what.include?("hash") then
58
83
  ::Hash.class_eval do
59
- include Cowtech::Extensions::Hash
84
+ include ::Cowtech::Extensions::Hash
60
85
  end
61
86
  end
62
87
 
63
88
  if what.include?("datetime") then
64
89
  ::Time.class_eval do
65
- include Cowtech::Extensions::DateTime
90
+ include ::Cowtech::Extensions::DateTime
66
91
  end
67
92
 
68
93
  ::Date.class_eval do
69
- include Cowtech::Extensions::DateTime
94
+ include ::Cowtech::Extensions::DateTime
70
95
  end
71
96
 
72
97
  ::DateTime.class_eval do
73
- include Cowtech::Extensions::DateTime
74
- end
98
+ include ::Cowtech::Extensions::DateTime
99
+ end
100
+
101
+ ::ActiveSupport::TimeZone.class_eval do
102
+ include ::Cowtech::Extensions::TimeZone
103
+ end
75
104
  end
76
105
 
77
106
  if what.include?("math") then
78
107
  ::Math.class_eval do
79
- include Cowtech::Extensions::Math
108
+ include ::Cowtech::Extensions::Math
80
109
  end
81
110
  end
82
111
 
@@ -84,9 +113,11 @@ module Cowtech
84
113
  require "pathname"
85
114
 
86
115
  ::Pathname.class_eval do
87
- include Cowtech::Extensions::Pathname
116
+ include ::Cowtech::Extensions::Pathname
88
117
  end
89
- end
118
+ end
119
+
120
+ ::Cowtech::Extensions::Settings.instance
90
121
  end
91
122
  end
92
123
  end
@@ -6,13 +6,20 @@
6
6
 
7
7
  module Cowtech
8
8
  module Extensions
9
+ # Extension for the boolean values.
9
10
  module Boolean
10
- extend ActiveSupport::Concern
11
+ extend ::ActiveSupport::Concern
11
12
 
13
+ # Converts the boolean to an integer.
14
+ #
15
+ # @return [Fixnum] `1` for `true`, `0` for `false`.
12
16
  def to_i
13
17
  (self == true) ? 1 : 0
14
18
  end
15
19
 
20
+ # Returns the boolean itself for use in form helpers.
21
+ #
22
+ # @return [Boolean] The boolean value.
16
23
  def value
17
24
  self
18
25
  end
@@ -6,130 +6,216 @@
6
6
 
7
7
  module Cowtech
8
8
  module Extensions
9
+ # Extensions for date and time objects.
9
10
  module DateTime
10
- extend ActiveSupport::Concern
11
+ extend ::ActiveSupport::Concern
11
12
 
13
+ # General methods.
12
14
  module ClassMethods
15
+ # Returns strings representations of days.
16
+ # @see Settings#setup_date_names
17
+ #
18
+ # @param short [Boolean] If return the abbreviated representations.
19
+ # @return [Array] Return string representations of days.
13
20
  def days(short = true)
14
- days = Cowtech::Extensions.settings.date_names[short ? :short_days : :long_days]
21
+ days = ::Cowtech::Extensions.settings.date_names[short ? :short_days : :long_days]
15
22
  (1..7).to_a.collect { |i|
16
23
  {:value => i.to_s, :label=> days[i - 1]}
17
24
  }
18
25
 
19
26
  end
20
27
 
28
+ # Returns strings representations of months.
29
+ # @see Settings#setup_date_names
30
+ #
31
+ # @param short [Boolean] If return the abbreviated representations.
32
+ # @return [Array] Return string representations of months.
21
33
  def months(short = true)
22
- months = Cowtech::Extensions.settings.date_names[short ? :short_months : :long_months]
34
+ months = ::Cowtech::Extensions.settings.date_names[short ? :short_months : :long_months]
23
35
  (1..12).collect { |i|
24
36
  {:value => i.to_s.rjust(2, "0"), :label=> months.at(i - 1)}
25
37
  }
26
38
  end
27
39
 
28
- def years(offset = 10, also_future = true, reference = nil)
29
- y = (reference || Date.today).year
30
- (y - offset..(also_future ? y + offset : y)).collect { |year| {:value => year, :label => year} }
40
+ # Returns a range of years.
41
+ #
42
+ # ```ruby
43
+ # Date.years(3, false, 2010)
44
+ # # => [2007, 2008, 2009, 2010]
45
+ # ```
46
+ #
47
+ # ```ruby
48
+ # Date.years(1, true, 2010, true)
49
+ # # => [{:value=>2009, :label=>2009}, {:value=>2010, :label=>2010}, {:value=>2011, :label=>2011}]
50
+ # ```
51
+ #
52
+ #
53
+ # @param offset [Fixnum] The width of the range.
54
+ # @param also_future [Boolean] If return also future years.
55
+ # @param reference [Fixnum] The ending (or middle, if `also_future` is `true`) value of the range. Defaults to the current year.
56
+ # @param as_objects [Boolean] If to return years in hashes with `:value` and `label` keys.
57
+ # @return [Array] A range of years. Every entry is
58
+ def years(offset = 10, also_future = true, reference = nil, as_objects = false)
59
+ y = reference || ::Date.today.year
60
+ (y - offset..(also_future ? y + offset : y)).collect { |year| as_objects ? {:value => year, :label => year} : year }
31
61
  end
32
62
 
33
- def easter(year = nil)
34
- day = 1
35
- month = 3
36
- year = Date.today.year if !year.is_integer?
63
+ # Returns all the availabe timezones.
64
+ #
65
+ # @return [Array]All the zone available.
66
+ def timezones
67
+ ::ActiveSupport::TimeZone.all
68
+ end
37
69
 
38
- # Compute using Gauss Method
39
- a = year % 19
40
- d = ((19 * a) + 24) % 30
41
- e = ((2 * (year % 4)) + (4 * (year % 7)) + (6 * d) + 5) % 7
70
+ # Returns a list of names of all timezones.
71
+ #
72
+ # @param with_dst [Boolean] If include DST version of the zones.
73
+ # @param dst_label [String] Label for the DST indication. Defaults to `(Daylight Saving Time)`.
74
+ # @return [Array] A list of names of timezones.
75
+ def list_timezones(with_dst = true, dst_label = nil)
76
+ ::ActiveSupport::TimeZone.list_all(with_dst, dst_label)
77
+ end
42
78
 
43
- if d + e < 10 then
44
- day = d + e + 22
45
- else
46
- day = d + e - 9
47
- month = 4
48
- end
79
+ # Find a zone by its name.
80
+ #
81
+ # @param name [String] The zone name.
82
+ # @param dst_label [String] Label for the DST indication. Defaults to `(Daylight Saving Time)`.
83
+ # @return [TimeZone] A timezone or `nil` if no zone was found.
84
+ def find_timezone(name = true, dst_label = nil)
85
+ ::ActiveSupport::TimeZone.find(name, dst_label)
86
+ end
49
87
 
50
- if day == 26 && month == 4 then
51
- day = 19
52
- elsif day == 25 && month == 4 && d == 28 && e == 6 && a > 10 then
53
- day = 18
54
- end
55
- # End
88
+ # Returns a string representation of a timezone.
89
+ #
90
+ # ```ruby
91
+ # DateTime.parameterize_zone(ActiveSupport::TimeZone["Pacific Time (US & Canada)"])
92
+ # # => "-0800@pacific-time-us-canada"
93
+ # ```
94
+ # @param tz [TimeZone] The zone to represent.
95
+ # @param with_offset [Boolean] If to include offset into the representation.
96
+ # @return [String] A string representation which can be used for searches.
97
+ def parameterize_zone(tz, with_offset = true)
98
+ ::ActiveSupport::TimeZone::parameterize_zone(tz, with_offset)
99
+ end
100
+
101
+ # Finds a parameterized timezone.
102
+ # @see DateTime#parameterize_zone
103
+ #
104
+ # @param tz [String] The zone to unparameterize.
105
+ # @param as_string [Boolean] If return just the zone name.
106
+ # @param dst_label [String] Label for the DST indication. Defaults to `(Daylight Saving Time)`.
107
+ # @return [String|TimeZone] The found timezone or `nil` if the zone is not valid.
108
+ def unparameterize_zone(tz, as_string = false, dst_label = nil)
109
+ ::ActiveSupport::TimeZone::unparameterize_zone(tz, as_string, dst_label)
110
+ end
56
111
 
57
- Date.civil(year, month, day)
112
+ # Returns an offset in rational value.
113
+ #
114
+ # @param offset [Fixnum] The offset to convert.
115
+ # @return [Rational] The converted offset.
116
+ def rationalize_offset(offset)
117
+ ::ActiveSupport::TimeZone.rationalize_offset(offset)
58
118
  end
59
119
 
120
+ # Returns the Easter (according to Gregorian calendar) date for the year.
121
+ # @see http://en.wikipedia.org/wiki/Computus#Anonymous_Gregorian_algorithm
122
+ #
123
+ # @param year [Fixnum] The year to compute the date for. Defaults to the current year.
124
+ # @return [Date] The Easter date for the year.
125
+ def easter(year = nil)
126
+ year = ::Date.today.year if !year.is_integer?
127
+
128
+ # Compute using Anonymouse Gregorian Algorithm: http://en.wikipedia.org/wiki/Computus#Anonymous_Gregorian_algorithm
129
+ a = year % 19
130
+ b = (year / 100.0).floor
131
+ c = year % 100
132
+ d = (b / 4.0).floor
133
+ e = b % 4
134
+ f = ((b + 8) / 25.0).floor
135
+ g = ((b - f + 1) / 3.0).floor
136
+ h = ((19 * a) + b - d - g + 15) % 30
137
+ i = (c / 4.0).floor
138
+ k = c % 4
139
+ l = (32 + (2 * e) + (2 * i) - h - k) % 7
140
+ m = ((a + (11 * h) + (22 * l)) / 451.0).floor
141
+
142
+ day = ((h + l - (7 * m) + 114) % 31) + 1
143
+ month = ((h + l - (7 * m) + 114) / 31.0).floor
144
+ ::Date.civil(year, month, day)
145
+ end
146
+
147
+ # Lookups a custom datetime format.
148
+ # @see Settings#setup_date_formats
149
+ #
150
+ # @param key [Symbol] The name of the format to search.
151
+ # @return [String] The format or the name itself (if the format has not been found).
60
152
  def custom_format(key)
61
- Cowtech::Extensions.settings.date_formats.fetch(key.to_sym, key).ensure_string
153
+ ::Cowtech::Extensions.settings.date_formats.fetch(key.to_sym, key).ensure_string
62
154
  end
63
155
 
156
+ # Checks if the date is valid against to a specific format.
157
+ # @see DateTime#custom_format
158
+ #
159
+ # @param value [String] The value to check.
160
+ # @param format [String] The format to check the value against.
161
+ # @return [Boolean] `true` if the value is valid against the format, `false` otherwise.
64
162
  def is_valid?(value, format = "%F %T")
65
163
  rv = true
66
164
 
67
165
  format = self.custom_format(format)
68
166
 
69
167
  begin
70
- ::DateTime.strptime(value, format)
168
+ ::DateTime.strptime(value.ensure_string, format)
71
169
  rescue => e
72
170
  rv = false
73
171
  end
74
172
 
75
173
  rv
76
174
  end
77
-
78
- def rational_offset(tz = ::Time.zone)
79
- Rational((tz.tzinfo.current_period.utc_offset / 3600), 24)
80
- end
81
-
82
- def parameterize_zone(tz)
83
- tz = tz.to_s if !tz.is_a?(String)
84
-
85
- if tz =~ /^(\([a-z]+([+-])(\d{2}):(\d{2})\)\s(.+))$/i then
86
- "#{$2}#{$3}#{$4}@#{$5.parameterize}"
87
- else
88
- tz.parameterize
89
- end
90
- end
91
-
92
- def find_parameterized_zone(tz, as_string = false)
93
- tz = Date.parameterize_zone(tz) if !tz.is_a?(String)
94
- tz = tz.gsub(/^(.+\d{4}@)?/, "")
95
-
96
- rv = catch(:zone) do
97
- ActiveSupport::TimeZone::MAPPING.each_key do |zone|
98
- throw(:zone, zone) if ::DateTime.parameterize_zone(zone) == tz
99
- end
100
-
101
- nil
102
- end
103
-
104
- if rv then
105
- (as_string ? rv : ActiveSupport::TimeZone[rv])
106
- else
107
- nil
108
- end
109
- end
110
175
  end
111
176
 
177
+ # Returns the UTC::Time representation of the current datetime.
178
+ #
179
+ # @return [UTC::Time] The UTC::Time representation of the current datetime.
112
180
  def utc_time
113
181
  ua = (self.respond_to?(:utc) ? self : self.to_datetime).utc
114
182
  ::Time.utc(ua.year, ua.month, ua.day, ua.hour, ua.min, ua.sec)
115
183
  end
116
184
 
185
+ # Returns the number of months passed between the beginning of the base year and the current date.
186
+ #
187
+ # ```ruby
188
+ # DateTime.civil(2012, 6, 1).in_months(2011)
189
+ # # => 18
190
+ # ```
191
+ #
192
+ # @param base [DateTime] The base year to start computation from. Default to current year.
193
+ # @return [Fixnum] Returns the number of months passed between the beginning of the base year and the current date.
117
194
  def in_months(base = nil)
118
- base ||= Date.today.year
195
+ base ||= ::Date.today.year
119
196
  ((self.year) - base) * 12 + self.month
120
197
  end
121
198
 
199
+ # Returns the current month number with leading 0.
200
+ #
201
+ # @return [String] The current month number with leading 0.
122
202
  def padded_month
123
203
  self.month.to_s.rjust(2, "0")
124
204
  end
125
205
 
206
+ # Formats a datetime, looking up also custom formats.
207
+ # @see Settings#setup_date_formats
208
+ #
209
+ # @param format [String] A format or a custom format name to use for formatting.
210
+ # @return [String] The formatted date.
126
211
  def lstrftime(format = nil)
127
212
  rv = nil
128
- names = Cowtech::Extensions.settings.date_names
213
+ names = ::Cowtech::Extensions.settings.date_names
129
214
 
130
- final_format = ::DateTime.custom_format(format).ensure_string.gsub(/(%{1,2}[abz])/i) do |match|
215
+ final_format = ::DateTime.custom_format(format).ensure_string.gsub(/(%{1,2}:?[abz])/i) do |match|
131
216
  mrv = match
132
217
 
218
+ # Handling of %z is to fix ruby 1.8 bug in OSX: http://bugs.ruby-lang.org/issues/2396
133
219
  if match !~ /^%%/ then
134
220
  case match
135
221
  when "%a"
@@ -140,28 +226,248 @@ module Cowtech
140
226
  mrv = names[:short_months][self.month - 1]
141
227
  when "%B"
142
228
  mrv = names[:long_months][self.month - 1]
143
- when "%Z"
144
- mrv = self.formatted_offset(true) if RUBY_VERSION =~ /^1\.8/ # This is to fix ruby 1.8 bug in OSX
145
229
  when "%z"
146
- mrv = self.formatted_offset(false) if RUBY_VERSION =~ /^1\.8/ # This is to fix ruby 1.8 bug in OSX
230
+ mrv = ::Cowtech::Extensions.is_ruby_18? ? self.formatted_offset(false) : nil
231
+ when "%:z"
232
+ mrv = ::Cowtech::Extensions.is_ruby_18? ? self.formatted_offset(true) : nil
147
233
  end
148
-
149
- mrv.sub!("%", "%%")
150
234
  end
151
235
 
152
- mrv
236
+ mrv ? mrv.sub("%", "%%") : match
153
237
  end
154
238
 
155
239
  self.strftime(final_format)
156
240
  end
157
241
 
242
+ # Formats a datetime in the current timezone.
243
+ #
244
+ # @param format [String] The format to use for formatting.
245
+ # @return [String] The formatted date.
158
246
  def local_strftime(format = nil)
159
247
  (self.respond_to?(:in_time_zone) ? self.in_time_zone : self).strftime(::DateTime.custom_format(format))
160
248
  end
161
249
 
250
+ # Formats a datetime in the current timezone, looking up also custom formats.
251
+ # @see Settings#setup_date_formats
252
+ #
253
+ # @param format [String] A format or a custom format name.
254
+ # @return [String] The formatted date.
162
255
  def local_lstrftime(format = nil)
163
256
  (self.respond_to?(:in_time_zone) ? self.in_time_zone : self).lstrftime(format)
164
257
  end
165
258
  end
259
+
260
+ # Extensions for timezone objects.
261
+ module TimeZone
262
+ extend ::ActiveSupport::Concern
263
+
264
+ # General methods.
265
+ module ClassMethods
266
+ # Returns an offset in rational value.
267
+ #
268
+ # @param offset [Fixnum] The offset to convert.
269
+ # @return [Rational] The converted offset.
270
+ def rationalize_offset(offset)
271
+ offset = offset.try(:offset) if !offset.is_a?(::Fixnum)
272
+ ::TZInfo::OffsetRationals.rational_for_offset(offset.to_integer)
273
+ end
274
+
275
+ # Returns a +HH:MM formatted representation of the offset.
276
+ #
277
+ # @param offset [Rational|Fixnum] The offset to represent, in seconds or as a rational.
278
+ # @param colon [Boolean] If to put the colon in the output string.
279
+ # @return [String] The formatted offset.
280
+ def format_offset(offset, colon = true)
281
+ offset = (offset * 86400).to_i if offset.is_a?(::Rational)
282
+ offset.is_a?(::Fixnum) ? self.seconds_to_utc_offset(offset, colon) : nil
283
+ end
284
+
285
+ # Find a zone by its name.
286
+ #
287
+ # @param name [String] The zone name.
288
+ # @param dst_label [String] Label for the DST indication. Defaults to `(Daylight Saving Time)`.
289
+ # @return [TimeZone] A timezone or `nil` if no zone was found.
290
+ def find(name, dst_label = nil)
291
+ catch(:zone) do
292
+ ::ActiveSupport::TimeZone.all.each do |zone|
293
+ throw(:zone, zone) if [zone.to_s, zone.to_s_with_dst(dst_label)].include?(name)
294
+ end
295
+
296
+ nil
297
+ end
298
+ end
299
+
300
+ # Returns a list of names of all timezones.
301
+ #
302
+ # @param with_dst [Boolean] If include DST version of the zones.
303
+ # @param dst_label [String] Label for the DST indication. Defaults to `(Daylight Saving Time)`.
304
+ # @return [Array] A list of names of timezones.
305
+ def list_all(with_dst = true, dst_label = nil)
306
+ dst_key = "DST-#{dst_label}"
307
+ @zones_names ||= { "STANDARD" => ActiveSupport::TimeZone.all.collect(&:to_s) }
308
+
309
+ if with_dst && @zones_names[dst_key].blank? then
310
+ @zones_names[dst_key] = []
311
+
312
+ ::ActiveSupport::TimeZone.all.each do |zone|
313
+ @zones_names[dst_key] << zone.to_s
314
+ @zones_names[dst_key] << zone.to_s_with_dst(dst_label)
315
+ end
316
+ end
317
+
318
+ @zones_names[with_dst ? dst_key : "STANDARD"]
319
+ end
320
+
321
+ # Returns a string representation of a timezone.
322
+ #
323
+ # ```ruby
324
+ # DateTime.parameterize_zone(ActiveSupport::TimeZone["Pacific Time (US & Canada)"])
325
+ # # => "-0800@pacific-time-us-canada"
326
+ # ```
327
+ # @param tz [TimeZone] The zone to represent.
328
+ # @param with_offset [Boolean] If to include offset into the representation.
329
+ # @return [String] A string representation which can be used for searches.
330
+ def parameterize_zone(tz, with_offset = true)
331
+ tz = tz.to_s if !tz.is_a?(::String)
332
+
333
+ if tz =~ /^(\([a-z]+([+-])(\d{2})(:?)(\d{2})\)\s(.+))$/i then
334
+ with_offset ? "#{$2}#{$3}#{$5}@#{$6.parameterize}" : $6.parameterize
335
+ elsif !with_offset then
336
+ tz.gsub(/^([+-]?(\d{2})(:?)(\d{2})@)/, "")
337
+ else
338
+ tz.parameterize
339
+ end
340
+ end
341
+
342
+ # Finds a parameterized timezone.
343
+ # @see DateTime#parameterize_zone
344
+ #
345
+ # @param tz [String] The zone to unparameterize.
346
+ # @param as_string [Boolean] If return just the zone name.
347
+ # @param dst_label [String] Label for the DST indication. Defaults to `(Daylight Saving Time)`.
348
+ # @return [String|TimeZone] The found timezone or `nil` if the zone is not valid.
349
+ def unparameterize_zone(tz, as_string = false, dst_label = nil)
350
+ tz = self.parameterize_zone(tz, false)
351
+
352
+ rv = catch(:zone) do
353
+ self.list_all(true, dst_label).each do |zone|
354
+ throw(:zone, zone) if self.parameterize_zone(zone, false) == tz
355
+ end
356
+
357
+ nil
358
+ end
359
+
360
+ if rv then
361
+ (as_string ? rv : self.find(rv, dst_label))
362
+ else
363
+ nil
364
+ end
365
+ end
366
+ end
367
+
368
+ # Returns the current offset for this timezone, taking care of DST (Daylight Saving Time).
369
+ #
370
+ # @param rational [Boolean] If to return the offset as a Rational.
371
+ # @param date [DateTime] The date to consider. Defaults to now.
372
+ # @return [Fixnum|Rational] The offset of this timezone.
373
+ def current_offset(rational = false, date = nil)
374
+ date ||= ::DateTime.now
375
+
376
+ dst_period = self.dst_period
377
+
378
+ rv = (self.period_for_utc(date.utc).dst? ? self.dst_offset : self.offset)
379
+ rational ? self.class.rationalize_offset(rv) : rv
380
+ end
381
+
382
+ # Returns the standard offset for this timezone.
383
+ #
384
+ # @param rational [Boolean] If to return the offset as a Rational.
385
+ # @return [Fixnum|Rational] The offset of this timezone.
386
+ def offset(rational = false)
387
+ rv = self.utc_offset
388
+ rational ? self.class.rationalize_offset(rv) : rv
389
+ end
390
+
391
+ # Gets a period for this timezone when the DST (Daylight Saving Time) is active (it takes care of different hemispheres).
392
+ #
393
+ # @param year [Fixnum] The year to which refer to. Defaults to the current year.
394
+ # @return [TimezonePeriod] A period when the DST (Daylight Saving Time) is active or `nil` if the timezone doesn't use DST for that year.
395
+ def dst_period(year = nil)
396
+ year ||= ::Date.today.year
397
+
398
+ nothern_summer = ::DateTime.civil(year, 7, 15).utc # This is a representation of a summer period in the Northern Hemisphere.
399
+ southern_summer = ::DateTime.civil(year, 1, 15).utc # This is a representation of a summer period in the Northern Hemisphere.
400
+
401
+ period = self.period_for_utc(nothern_summer)
402
+ period = self.period_for_utc(southern_summer) if !period.dst?
403
+ period.dst? ? period : nil
404
+ end
405
+
406
+ # Checks if the timezone uses DST (Daylight Saving Time) for that year.
407
+ #
408
+ # @param year [Fixnum] The year to check. Defaults to the current year.
409
+ # @return [Boolean] `true` if the zone uses DST, `false` otherwise.
410
+ def uses_dst?(year = nil)
411
+ self.dst_period(year).present?
412
+ end
413
+
414
+ # Return the correction applied to the standard offset the timezone when the DST (Daylight Saving Time) is active.
415
+ #
416
+ # @param rational [Boolean] If to return the offset as a Rational.
417
+ # @param year [Fixnum] The year to which refer to. Defaults to the current year.
418
+ # @return [Fixnum|Rational] The correction for dst.
419
+ def dst_correction(rational = false, year = nil)
420
+ period = self.dst_period(year)
421
+ rv = period ? period.std_offset : 0
422
+ rational ? self.class.rationalize_offset(rv) : rv
423
+ end
424
+
425
+ # Returns the standard offset for this timezone timezone when the DST (Daylight Saving Time) is active.
426
+ #
427
+ # @param rational [Boolean] If to return the offset as a Rational.
428
+ # @param year [Fixnum] The year to which refer to. Defaults to the current year.
429
+ # @return [Fixnum|Rational] The DST offset for this timezone or `0` , if the timezone doesn't use DST for that year.
430
+ def dst_offset(rational = false, year = nil)
431
+ period = self.dst_period(year)
432
+ rv = period ? period.utc_total_offset : 0
433
+ rational ? self.class.rationalize_offset(rv) : rv
434
+ end
435
+
436
+ # Returns the name for this zone with DST (Daylight Saving Time) active.
437
+ #
438
+ # @param dst_label [String] Label for the DST indication. Defaults to `(Daylight Saving Time)`.
439
+ # @param year [Fixnum] The year to which refer to. Defaults to the current year.
440
+ # @return [String] The name for the zone with DST or `nil`, if the timezone doesn't use DST for that year.
441
+ def to_s_with_dst(dst_label = nil, year = nil)
442
+ dst_label ||= "(Daylight Saving Time)"
443
+
444
+ if self.uses_dst?(year) then
445
+ period = self.dst_period(year)
446
+ offset = self.class.seconds_to_utc_offset(period.utc_total_offset)
447
+ "(GMT#{offset}) #{name} #{dst_label}"
448
+ else
449
+ nil
450
+ end
451
+ end
452
+
453
+ # Returns the parametized name for this zone.
454
+ #
455
+ # @param with_offset [Boolean] If to include offset into the representation.
456
+ # @return [String] The parametized name for this zone.
457
+ def to_s_parameterized(with_offset = true)
458
+ ::ActiveSupport::TimeZone.parameterize_zone(self.to_s, with_offset)
459
+ end
460
+
461
+ # Returns the parametized name for this zone with DST (Daylight Saving Time) active.
462
+ #
463
+ # @param dst_label [String] Label for the DST indication. Defaults to `(Daylight Saving Time)`.
464
+ # @param with_offset [Boolean] If to include offset into the representation.
465
+ # @param year [Fixnum] The year to which refer to. Defaults to the current year.
466
+ # @return [String] The parametized name for this zone with DST or `nil`, if the timezone doesn't use DST for that year.
467
+ def to_s_with_dst_parameterized(dst_label = nil, with_offset = true, year = nil)
468
+ rv = self.to_s_with_dst(dst_label, year)
469
+ rv ? ::ActiveSupport::TimeZone.parameterize_zone(rv) : nil
470
+ end
471
+ end
166
472
  end
167
473
  end