cowtech-extensions 2.1.3 → 2.5.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 (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