third_base 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +20 -11
- data/lib/third_base/compat.rb +1 -1
- data/lib/third_base/date.rb +45 -23
- data/lib/third_base/datetime.rb +43 -21
- data/spec/compat/compat_class_methods_spec.rb +29 -0
- data/spec/date/parse_spec.rb +18 -1
- data/spec/datetime/parse_spec.rb +67 -3
- data/spec/datetime/strftime_spec.rb +12 -11
- metadata +2 -2
data/README
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
ThirdBase differs from Ruby's standard Date/DateTime class in the following
|
4
4
|
ways:
|
5
5
|
|
6
|
-
- ThirdBase is roughly 2-
|
6
|
+
- ThirdBase is roughly 2-10 times faster depending on usage
|
7
7
|
- ThirdBase has a lower memory footprint
|
8
8
|
- ThirdBase supports pluggable parsers
|
9
9
|
- ThirdBase doesn't depend on Ruby's Rational class
|
@@ -150,17 +150,17 @@ To add a parser type:
|
|
150
150
|
Date.add_parser_type(:mine)
|
151
151
|
DateTime.add_parser_type(:mine)
|
152
152
|
|
153
|
-
=== Adding Parsers to Parser Types
|
153
|
+
=== Adding Regexp Parsers to Parser Types
|
154
154
|
|
155
|
-
A ThirdBase Date/Datetime parser consists of two parts, a regular
|
156
|
-
expression, and a
|
157
|
-
passed to Date/DateTime.new!. The
|
158
|
-
|
159
|
-
is not able to successfully parse the
|
160
|
-
the regular expression). To add a
|
161
|
-
|
162
|
-
use, the regular expression, and a block
|
163
|
-
parser:
|
155
|
+
A ThirdBase Date/Datetime regexp parser consists of two parts, a regular
|
156
|
+
expression, and a block that takes a MatchData object and returns a
|
157
|
+
Date/DateTime instance or a hash to be passed to Date/DateTime.new!. The
|
158
|
+
block is only called if the regular expression matches the string to be
|
159
|
+
parsed, and it can return nil if it is not able to successfully parse the
|
160
|
+
string (even if the string matches the regular expression). To add a
|
161
|
+
parser, you use the add_parser class method, which takes an argument
|
162
|
+
specifying which parser family to use, the regular expression, and a block
|
163
|
+
that is used as a proc for the parser:
|
164
164
|
|
165
165
|
To add a parser to a parser type:
|
166
166
|
|
@@ -195,6 +195,15 @@ Adding a parser to a parser type adds it to the front of the array of parsers
|
|
195
195
|
for that type, so it will be tried before other parsers for that type. It is
|
196
196
|
an error to add a parser to a parser type that doesn't exist.
|
197
197
|
|
198
|
+
=== Adding strptime Parsers to Parser Types (New in 1.2.0)
|
199
|
+
|
200
|
+
ThirdBase 1.2.0 added the ability to more easily create common parsers
|
201
|
+
using the strptime format string syntax. These are created similar to regexp
|
202
|
+
parsers, but use a format string, and the block is then optional (and should
|
203
|
+
be omitted unless you know what you are doing):
|
204
|
+
|
205
|
+
DateTime.add_parser(:mine, '%Z %m~%Y~%d %S`%M`%H')
|
206
|
+
|
198
207
|
=== Modifying the Order of Parsers Types
|
199
208
|
|
200
209
|
You can change the order in which parsers types are tried by using the
|
data/lib/third_base/compat.rb
CHANGED
@@ -42,7 +42,7 @@ module ThirdBase
|
|
42
42
|
# * :zone : time zone offset as string
|
43
43
|
def _parse(str, comp=false)
|
44
44
|
d = DateTime.parse(str)
|
45
|
-
{:mon=>d.mon, :zone=>d.zone, :sec=>d.sec, :year=>d.year, :hour=>d.hour, :offset=>d.
|
45
|
+
{:mon=>d.mon, :zone=>d.zone, :sec=>d.sec, :year=>d.year, :hour=>d.hour, :offset=>d.utc_offset, :mday=>d.day, :min=>d.min, :sec_fraction=>d.usec/1000000.0}.reject{|k, v| d.not_parsed.include?(k)}
|
46
46
|
end
|
47
47
|
|
48
48
|
# Converts an Astronomical Julian Date to an Astronomical Modified Julian Date (substracts an integer from ajd)
|
data/lib/third_base/date.rb
CHANGED
@@ -43,7 +43,8 @@ module ThirdBase
|
|
43
43
|
[%r{\A#{MONTHNAME_RE_PATTERN}[-./ ](\d\d?)(?:st|nd|rd|th)?,?(?:[-./ ](-?(?:\d\d(?:\d\d)?)))?\z}io, proc{|m| {:civil=>[m[3] ? two_digit_year(m[3]) : Time.now.year, MONTH_NUM_MAP[m[1].downcase], m[2].to_i]}}],
|
44
44
|
[%r{\A(\d\d?)(?:st|nd|rd|th)?[-./ ]#{MONTHNAME_RE_PATTERN}[-./ ](-?\d{4})\z}io, proc{|m| {:civil=>[m[3].to_i, MONTH_NUM_MAP[m[2].downcase], m[1].to_i]}}],
|
45
45
|
[%r{\A(-?\d{4})[-./ ]#{MONTHNAME_RE_PATTERN}[-./ ](\d\d?)(?:st|nd|rd|th)?\z}io, proc{|m| {:civil=>[m[1].to_i, MONTH_NUM_MAP[m[2].downcase], m[3].to_i]}}],
|
46
|
-
[%r{\A#{MONTHNAME_RE_PATTERN}[-./ ](-?\d{4})\z}io, proc{|m| {:civil=>[m[2].to_i, MONTH_NUM_MAP[m[1].downcase], 1]}}]
|
46
|
+
[%r{\A#{MONTHNAME_RE_PATTERN}[-./ ](-?\d{4})\z}io, proc{|m| {:civil=>[m[2].to_i, MONTH_NUM_MAP[m[1].downcase], 1]}}],
|
47
|
+
[%r{\A#{ABBR_DAYNAME_RE_PATTERN} #{ABBR_MONTHNAME_RE_PATTERN} (\d\d?) (-?\d{4})\z}io, proc{|m| {:civil=>[m[4].to_i, MONTH_NUM_MAP[m[2].downcase], m[3].to_i]}}]]
|
47
48
|
DEFAULT_PARSERS[:eu] = [[%r{\A(\d\d?)[-./ ](\d\d?)[-./ ](\d\d\d\d)\z}o, proc{|m| {:civil=>[m[3].to_i, m[2].to_i, m[1].to_i]}}],
|
48
49
|
[%r{\A(\d\d?)[-./ ](\d?\d)[-./ ](\d?\d)\z}o, proc{|m| {:civil=>[two_digit_year(m[1]), m[2].to_i, m[3].to_i]}}]]
|
49
50
|
DEFAULT_PARSERS[:num] = [[%r{\A\d{2,8}\z}o, proc do |m|
|
@@ -91,13 +92,26 @@ module ThirdBase
|
|
91
92
|
alias new! new
|
92
93
|
end
|
93
94
|
|
94
|
-
# Add a parser to the parser type.
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
|
100
|
-
|
95
|
+
# Add a parser to the parser type. Arguments:
|
96
|
+
# * type - The parser type to which to add the parser, should be a Symbol.
|
97
|
+
# * pattern - Can be either a Regexp or String:
|
98
|
+
# * String - A strptime parser regular expression is created using pattern as the format string.
|
99
|
+
# If a block is given, it is used. If no block is given, the parser will
|
100
|
+
# operate identically to strptime.
|
101
|
+
# * Regexp - The regular expression is used directly. In this case, a block must be provided,
|
102
|
+
# or an error is raised.
|
103
|
+
#
|
104
|
+
# The block, if provided, should take a single MatchData argument. It should return
|
105
|
+
# nil if it cannot successfully parse the string, an instance of this class, or a hash of
|
106
|
+
# values to be passed to new!.
|
107
|
+
def self.add_parser(type, pattern, &block)
|
108
|
+
if pattern.is_a?(String)
|
109
|
+
pattern, blk = strptime_pattern_and_block(pattern)
|
110
|
+
block ||= blk
|
111
|
+
else
|
112
|
+
raise(ArgumentError, 'must provide block for Regexp parser') unless block_given?
|
113
|
+
end
|
114
|
+
parser_hash[type].unshift([pattern, block])
|
101
115
|
end
|
102
116
|
|
103
117
|
# Add a parser type to the list of parser types.
|
@@ -144,7 +158,7 @@ module ThirdBase
|
|
144
158
|
parsers(opts[:parser_types]) do |pattern, block|
|
145
159
|
if m = pattern.match(s)
|
146
160
|
if res = block.call(m)
|
147
|
-
return new!(res)
|
161
|
+
return res.is_a?(Hash) ? new!(res) : res
|
148
162
|
end
|
149
163
|
end
|
150
164
|
end
|
@@ -166,24 +180,15 @@ module ThirdBase
|
|
166
180
|
# Parse the string using the provided format (or the default format).
|
167
181
|
# Raises an ArgumentError if the format does not match the string.
|
168
182
|
def self.strptime(str, fmt=strptime_default)
|
169
|
-
|
183
|
+
pattern, block = strptime_pattern_and_block(fmt)
|
170
184
|
s = str.strip
|
171
|
-
|
172
|
-
|
173
|
-
pat, *blks = _strptime_part(x[1..1])
|
174
|
-
blocks += blks
|
175
|
-
pat
|
176
|
-
end
|
177
|
-
if m = /#{pattern}/i.match(s)
|
178
|
-
m.to_a[1..-1].zip(blocks) do |x, blk|
|
179
|
-
blk.call(date_hash, x)
|
180
|
-
end
|
181
|
-
new_from_parts(date_hash)
|
185
|
+
if m = pattern.match(s)
|
186
|
+
block.call(m)
|
182
187
|
else
|
183
188
|
raise ArgumentError, 'invalid date'
|
184
189
|
end
|
185
190
|
end
|
186
|
-
|
191
|
+
|
187
192
|
# Returns a date with the current year, month, and date.
|
188
193
|
def self.today
|
189
194
|
t = Time.now
|
@@ -283,6 +288,23 @@ module ThirdBase
|
|
283
288
|
'%Y-%m-%d'
|
284
289
|
end
|
285
290
|
|
291
|
+
def self.strptime_pattern_and_block(fmt)
|
292
|
+
blocks = []
|
293
|
+
pattern = Regexp.escape(expand_strptime_format(fmt)).gsub(STRFTIME_RE) do |x|
|
294
|
+
pat, *blks = _strptime_part(x[1..1])
|
295
|
+
blocks += blks
|
296
|
+
pat
|
297
|
+
end
|
298
|
+
block = proc do |m|
|
299
|
+
h = {}
|
300
|
+
m.to_a[1..-1].zip(blocks) do |x, blk|
|
301
|
+
blk.call(h, x)
|
302
|
+
end
|
303
|
+
new_from_parts(h)
|
304
|
+
end
|
305
|
+
[/\A#{pattern}\z/i, block]
|
306
|
+
end
|
307
|
+
|
286
308
|
def self.two_digit_year(y)
|
287
309
|
y = if y.length == 2
|
288
310
|
y = y.to_i
|
@@ -292,7 +314,7 @@ module ThirdBase
|
|
292
314
|
end
|
293
315
|
end
|
294
316
|
|
295
|
-
private_class_method :_expand_strptime_format, :_strptime_part, :default_parser_hash, :default_parser_list, :expand_strptime_format, :new_from_parts, :parser_hash, :parser_list, :parsers, :parsers_for_family, :strptime_default, :two_digit_year
|
317
|
+
private_class_method :_expand_strptime_format, :_strptime_part, :default_parser_hash, :default_parser_list, :expand_strptime_format, :new_from_parts, :parser_hash, :parser_list, :parsers, :parsers_for_family, :strptime_default, :strptime_pattern_and_block, :two_digit_year
|
296
318
|
|
297
319
|
reset_parsers!
|
298
320
|
|
data/lib/third_base/datetime.rb
CHANGED
@@ -4,11 +4,16 @@ module ThirdBase
|
|
4
4
|
# ThirdBase's DateTime class, which builds on the Date class and adds a time component of
|
5
5
|
# hours, minutes, seconds, microseconds, and an offset from UTC.
|
6
6
|
class DateTime < Date
|
7
|
-
|
7
|
+
TIME_ZONE_SECOND_OFFSETS = {
|
8
|
+
'UTC'=>0, 'Z'=>0, 'UT'=>0, 'GMT'=>0,
|
9
|
+
'EST'=>-18000, 'EDT'=>-14400, 'CST'=>-21600, 'CDT'=>-18000, 'MST'=>-25200, 'MDT'=>-21600, 'PST'=>-28800, 'PDT'=>-25200,
|
10
|
+
'A'=>3600, 'B'=>7200, 'C'=>10800, 'D'=>14400, 'E'=>18000, 'F'=>21600, 'G'=>25200, 'H'=>28800, 'I'=>32400, 'K'=>36000, 'L'=>39600, 'M'=>43200,
|
11
|
+
'N'=>-3600, 'O'=>-7200, 'P'=>-10800, 'Q'=>-14400, 'R'=>-18000, 'S'=>-21600, 'T'=>-25200, 'U'=>-28800, 'V'=>-32400, 'W'=>-36000, 'X'=>-39600, 'Y'=>-43200}
|
8
12
|
PARSER_LIST = []
|
9
13
|
DEFAULT_PARSER_LIST = [:time, :iso, :us, :num]
|
10
14
|
DEFAULT_PARSERS = {}
|
11
|
-
|
15
|
+
TIME_ZONE_RE_STRING = "(#{TIME_ZONE_SECOND_OFFSETS.keys.sort.join('|')}|[+-](?:\\d\\d:?(?:\\d\\d)?))"
|
16
|
+
TIME_RE_STRING = "(?:[T ]?([\\d ]?\\d):(\\d\\d)(?::(\\d\\d(\\.\\d+)?))?([ap]m?)? ?#{TIME_ZONE_RE_STRING}?)?"
|
12
17
|
DEFAULT_PARSERS[:time] = [[%r{\A#{TIME_RE_STRING}\z}io, proc do |m|
|
13
18
|
unless m[0] == ''
|
14
19
|
t = Time.now
|
@@ -21,7 +26,8 @@ module ThirdBase
|
|
21
26
|
[%r{\A#{MONTHNAME_RE_PATTERN}[-./ ](\d\d?)(?:st|nd|rd|th)?,?(?:[-./ ](-?(?:\d\d(?:\d\d)?)))?#{TIME_RE_STRING}\z}io, proc{|m| add_parsed_time_parts(m, :civil=>[m[3] ? two_digit_year(m[3]) : Time.now.year, MONTH_NUM_MAP[m[1].downcase], m[2].to_i], :not_parsed=>m[3] ? [] : [:year])}],
|
22
27
|
[%r{\A(\d\d?)(?:st|nd|rd|th)?[-./ ]#{MONTHNAME_RE_PATTERN}[-./ ](-?\d{4})#{TIME_RE_STRING}\z}io, proc{|m| add_parsed_time_parts(m, :civil=>[m[3].to_i, MONTH_NUM_MAP[m[2].downcase], m[1].to_i])}],
|
23
28
|
[%r{\A(-?\d{4})[-./ ]#{MONTHNAME_RE_PATTERN}[-./ ](\d\d?)(?:st|nd|rd|th)?#{TIME_RE_STRING}\z}io, proc{|m| add_parsed_time_parts(m, :civil=>[m[1].to_i, MONTH_NUM_MAP[m[2].downcase], m[3].to_i])}],
|
24
|
-
[%r{\A#{MONTHNAME_RE_PATTERN}[-./ ](-?\d{4})#{TIME_RE_STRING}\z}io, proc{|m| add_parsed_time_parts(m, {:civil=>[m[2].to_i, MONTH_NUM_MAP[m[1].downcase], 1]}, 3)}]
|
29
|
+
[%r{\A#{MONTHNAME_RE_PATTERN}[-./ ](-?\d{4})#{TIME_RE_STRING}\z}io, proc{|m| add_parsed_time_parts(m, {:civil=>[m[2].to_i, MONTH_NUM_MAP[m[1].downcase], 1]}, 3)}],
|
30
|
+
[%r{\A#{ABBR_DAYNAME_RE_PATTERN} #{ABBR_MONTHNAME_RE_PATTERN} (\d\d?) #{TIME_RE_STRING} (-?\d{4})\z}io, proc{|m| add_parsed_time_parts(m, {:civil=>[m[10].to_i, MONTH_NUM_MAP[m[2].downcase], m[3].to_i]})}]]
|
25
31
|
DEFAULT_PARSERS[:eu] = [[%r{\A(\d\d?)[-./ ](\d\d?)[-./ ](\d{4})#{TIME_RE_STRING}\z}io, proc{|m| add_parsed_time_parts(m, :civil=>[m[3].to_i, m[2].to_i, m[1].to_i])}],
|
26
32
|
[%r{\A(\d\d?)[-./ ](\d?\d)[-./ ](\d?\d)#{TIME_RE_STRING}\z}io, proc{|m| add_parsed_time_parts(m, :civil=>[two_digit_year(m[1]), m[2].to_i, m[3].to_i])}]]
|
27
33
|
DEFAULT_PARSERS[:num] = [[%r{\A(\d{2,8})#{TIME_RE_STRING}\z}io, proc do |n|
|
@@ -58,7 +64,7 @@ module ThirdBase
|
|
58
64
|
minutes, seconds = i.divmod(60)
|
59
65
|
h.merge!(:jd=>j+UNIXEPOCH, :hour=>hours, :min=>minutes, :sec=>seconds)
|
60
66
|
end
|
61
|
-
STRPTIME_PROC_z = proc{|h,x|
|
67
|
+
STRPTIME_PROC_z = proc{|h,x| h[:offset] = convert_parsed_offset(x)}
|
62
68
|
|
63
69
|
# Public Class Methods
|
64
70
|
|
@@ -102,7 +108,7 @@ module ThirdBase
|
|
102
108
|
when '%T', '%X' then '%H:%M:%S'
|
103
109
|
when '%R' then '%H:%M'
|
104
110
|
when '%r' then '%I:%M:%S %p'
|
105
|
-
when '%+' then '%a %b %e %H:%M:%S %
|
111
|
+
when '%+' then '%a %b %e %H:%M:%S %Z %Y'
|
106
112
|
else super(v)
|
107
113
|
end
|
108
114
|
end
|
@@ -115,7 +121,7 @@ module ThirdBase
|
|
115
121
|
when 'P', 'p' then ['([ap]m)', STRPTIME_PROC_P]
|
116
122
|
when 'S' then ['(\d\d)', STRPTIME_PROC_S]
|
117
123
|
when 's' then ['(\d+)', STRPTIME_PROC_s]
|
118
|
-
when 'z', 'Z' then [
|
124
|
+
when 'z', 'Z' then [TIME_ZONE_RE_STRING, STRPTIME_PROC_z]
|
119
125
|
else super(v)
|
120
126
|
end
|
121
127
|
end
|
@@ -133,8 +139,7 @@ module ThirdBase
|
|
133
139
|
meridian = m[i+4]
|
134
140
|
hour = hour_with_meridian(hour, /a/io.match(meridian) ? :am : :pm) if meridian
|
135
141
|
offset = if of = m[i+5]
|
136
|
-
|
137
|
-
offset = x == 'Z' ? 0 : x[0..2].to_i*3600 + x[3..4].to_i*60
|
142
|
+
convert_parsed_offset(of)
|
138
143
|
else
|
139
144
|
not_parsed.concat([:zone, :offset])
|
140
145
|
Time.now.utc_offset
|
@@ -168,11 +173,18 @@ module ThirdBase
|
|
168
173
|
end
|
169
174
|
|
170
175
|
def self.new_from_parts(date_hash)
|
176
|
+
not_parsed = [:hour, :min, :sec].reject{|x| date_hash.has_key?(x)}
|
177
|
+
not_parsed.concat([:zone, :offset]) unless date_hash.has_key?(:offset)
|
178
|
+
not_parsed << :year unless date_hash.has_key?(:year) || date_hash.has_key?(:cwyear)
|
179
|
+
not_parsed << :mon unless date_hash.has_key?(:month) || date_hash.has_key?(:cweek)
|
180
|
+
not_parsed << :mday unless date_hash.has_key?(:day) || date_hash.has_key?(:cwday)
|
181
|
+
not_parsed << :sec_fraction
|
182
|
+
|
171
183
|
date_hash[:hour] = hour_with_meridian(date_hash[:hour], date_hash[:meridian]) if date_hash[:meridian]
|
172
184
|
d = now
|
173
|
-
weights = {:cwyear=>1, :year=>1, :cweek=>2, :cwday=>3, :yday=>3, :month=>2, :day=>3, :hour=>4, :min=>5, :sec=>6}
|
185
|
+
weights = {:cwyear=>1, :year=>1, :cweek=>2, :cwday=>3, :yday=>3, :month=>2, :day=>3, :hour=>4, :min=>5, :sec=>6, :offset=>7}
|
174
186
|
columns = {}
|
175
|
-
min =
|
187
|
+
min = 8
|
176
188
|
max = 0
|
177
189
|
date_hash.each do |k,v|
|
178
190
|
if w = weights[k]
|
@@ -183,21 +195,31 @@ module ThirdBase
|
|
183
195
|
offset = date_hash[:offset] || d.offset
|
184
196
|
hour = date_hash[:hour] || (min > 4 ? d.hour : 0)
|
185
197
|
minute = date_hash[:min] || (min > 5 ? d.min : 0)
|
186
|
-
sec = date_hash[:sec] || 0
|
198
|
+
sec = date_hash[:sec] || (min > 6 ? d.sec : 0)
|
199
|
+
hash = {:parts=>[hour, minute, sec, 0], :offset=>offset.to_i, :not_parsed=>not_parsed}
|
187
200
|
if date_hash[:jd]
|
188
|
-
jd
|
201
|
+
new!(hash.merge!(:jd=>date_hash[:jd]))
|
189
202
|
elsif date_hash[:year] || date_hash[:yday] || date_hash[:month] || date_hash[:day] || !(date_hash[:cwyear] || date_hash[:cweek])
|
190
203
|
if date_hash[:yday]
|
191
|
-
ordinal
|
204
|
+
new!(hash.merge!(:ordinal=>[date_hash[:year]||d.year, date_hash[:yday]]))
|
192
205
|
else
|
193
|
-
civil
|
206
|
+
new!(hash.merge!(:civil=>[date_hash[:year]||d.year, date_hash[:month]||(min > 2 ? d.mon : 1), date_hash[:day]||(min > 3 ? d.day : 1)]))
|
194
207
|
end
|
195
208
|
elsif date_hash[:cwyear] || date_hash[:cweek] || date_hash[:cwday]
|
196
|
-
commercial
|
209
|
+
new!(hash.merge!(:commercial=>[date_hash[:cwyear]||d.cwyear, date_hash[:cweek]||(min > 2 ? d.cweek : 1), date_hash[:cwday]||(min > 3 ? d.cwday : 1)]))
|
197
210
|
else
|
198
211
|
raise ArgumentError, 'invalid date'
|
199
212
|
end
|
200
213
|
end
|
214
|
+
|
215
|
+
def self.convert_parsed_offset(of)
|
216
|
+
if offset = TIME_ZONE_SECOND_OFFSETS[of.upcase]
|
217
|
+
offset
|
218
|
+
else
|
219
|
+
x = of.gsub(':','')
|
220
|
+
x[0..2].to_i*3600 + x[3..4].to_i*60
|
221
|
+
end
|
222
|
+
end
|
201
223
|
|
202
224
|
def self.parser_hash
|
203
225
|
PARSERS
|
@@ -211,7 +233,7 @@ module ThirdBase
|
|
211
233
|
'%Y-%m-%dT%H:%M:%S'
|
212
234
|
end
|
213
235
|
|
214
|
-
private_class_method :_expand_strptime_format, :_strptime_part, :add_parsed_time_parts, :default_parser_hash, :default_parser_list, :new_from_parts, :parser_hash, :parser_list, :strptime_default
|
236
|
+
private_class_method :_expand_strptime_format, :_strptime_part, :add_parsed_time_parts, :convert_parsed_offset, :default_parser_hash, :default_parser_list, :new_from_parts, :parser_hash, :parser_list, :strptime_default
|
215
237
|
|
216
238
|
reset_parsers!
|
217
239
|
|
@@ -342,7 +364,7 @@ module ThirdBase
|
|
342
364
|
|
343
365
|
# Return the offset as a time zone string (+/-HHMM).
|
344
366
|
def zone
|
345
|
-
strftime('%
|
367
|
+
strftime('%Z')
|
346
368
|
end
|
347
369
|
|
348
370
|
private
|
@@ -362,9 +384,9 @@ module ThirdBase
|
|
362
384
|
when 'S' then '%02d' % sec
|
363
385
|
when 's' then '%d' % ((jd - UNIXEPOCH)*86400 + hour*3600 + min*60 + sec - @offset)
|
364
386
|
when 'T', 'X' then strftime('%H:%M:%S')
|
365
|
-
when '+' then strftime('%a %b %e %H:%M:%S %
|
366
|
-
when 'Z' then
|
367
|
-
when 'z' then "%+03d
|
387
|
+
when '+' then strftime('%a %b %e %H:%M:%S %Z %Y')
|
388
|
+
when 'Z' then "%+03d:%02d" % (@offset/60).divmod(60)
|
389
|
+
when 'z' then "%+03d%02d" % (@offset/60).divmod(60)
|
368
390
|
else super(v)
|
369
391
|
end
|
370
392
|
end
|
@@ -385,7 +407,7 @@ module ThirdBase
|
|
385
407
|
end
|
386
408
|
|
387
409
|
def strftime_default
|
388
|
-
'%Y-%m-%dT%H:%M:%S%
|
410
|
+
'%Y-%m-%dT%H:%M:%S%Z'
|
389
411
|
end
|
390
412
|
|
391
413
|
def time_parts
|
@@ -30,6 +30,35 @@ describe ThirdBase::CompatClassMethods do
|
|
30
30
|
Date._parse('2008-10-20').should == {:year=>2008, :mday=>20, :mon=>10}
|
31
31
|
Date._parse('11:12:13').should == {:sec=>13, :hour=>11, :min=>12}
|
32
32
|
end
|
33
|
+
|
34
|
+
it "#_parse should not contain fields in the hash that were guessed when a strptime-parser is used" do
|
35
|
+
DateTime.add_parser(:us, "%m-%d<>%H/%M")
|
36
|
+
Date._parse('10-11<>12/13').should == {:mon=>10, :mday=>11, :hour=>12, :min=>13}
|
37
|
+
|
38
|
+
DateTime.add_parser(:us, "%d")
|
39
|
+
Date._parse('11').should == {:mday=>11}
|
40
|
+
|
41
|
+
DateTime.add_parser(:us, "%m")
|
42
|
+
Date._parse('11').should == {:mon=>11}
|
43
|
+
|
44
|
+
DateTime.add_parser(:us, "%Y")
|
45
|
+
Date._parse('2009').should == {:year=>2009}
|
46
|
+
|
47
|
+
DateTime.add_parser(:us, "%H")
|
48
|
+
Date._parse('11').should == {:hour=>11}
|
49
|
+
|
50
|
+
DateTime.add_parser(:us, "%M")
|
51
|
+
Date._parse('11').should == {:min=>11}
|
52
|
+
|
53
|
+
DateTime.add_parser(:us, "%S")
|
54
|
+
Date._parse('20').should == {:sec=>20}
|
55
|
+
|
56
|
+
DateTime.add_parser(:us, "%Z")
|
57
|
+
Date._parse('UTC').should == {:zone=>'+00:00', :offset=>0}
|
58
|
+
Date._parse('CDT').should == {:zone=>'-05:00', :offset=>-18000}
|
59
|
+
|
60
|
+
DateTime.reset_parsers!
|
61
|
+
end
|
33
62
|
|
34
63
|
it "#ajd_to_amjd should convert a astronomical julian date to a astronomical modified julian date" do
|
35
64
|
Date.ajd_to_amjd(2400002).should == 1
|
data/spec/date/parse_spec.rb
CHANGED
@@ -82,6 +82,10 @@ describe :date_parse, :shared => true do
|
|
82
82
|
Date.parse("november#{@sep}5th").should == Date.civil(Date.today.year, 11, 5)
|
83
83
|
end
|
84
84
|
|
85
|
+
it "can parse a weekday, month, day, and year into a Date object" do
|
86
|
+
Date.parse("Mon Aug 10 2009").should == Date.civil(2009, 8, 10)
|
87
|
+
end
|
88
|
+
|
85
89
|
it "can parse a month name, day and year into a Date object" do
|
86
90
|
Date.parse("november#{@sep}5th#{@sep}2005").should == Date.civil(2005, 11, 5)
|
87
91
|
end
|
@@ -209,12 +213,25 @@ describe "Date parser modifications" do
|
|
209
213
|
Date.reset_parsers!
|
210
214
|
end
|
211
215
|
|
212
|
-
it "should
|
216
|
+
it "should raise an ArgumentError if it can't parse a date" do
|
213
217
|
proc{Date.parse("today")}.should raise_error(ArgumentError)
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should be able to add a parser to an existing parser type that takes precedence" do
|
214
221
|
Date.add_parser(:iso, /today/){t = Time.now; {:civil=>[t.year, t.mon, t.day]}}
|
215
222
|
Date.parse("today").should == Date.today
|
216
223
|
end
|
217
224
|
|
225
|
+
it "should be able to handle parsers that return Date instances" do
|
226
|
+
Date.add_parser(:iso, /today/){t = Time.now; Date.new(t.year, t.mon, t.day)}
|
227
|
+
Date.parse("today").should == Date.today
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should be able to specify a strptime format string for a parser" do
|
231
|
+
Date.add_parser(:iso, "%d<<%m<<%Y")
|
232
|
+
Date.parse("03<<02<<2001").should == Date.new(2001, 2, 3)
|
233
|
+
end
|
234
|
+
|
218
235
|
it "should be able to add new parser types" do
|
219
236
|
proc{Date.parse("today")}.should raise_error(ArgumentError)
|
220
237
|
Date.add_parser_type(:mine)
|
data/spec/datetime/parse_spec.rb
CHANGED
@@ -22,7 +22,7 @@ describe "DateTime#parse" do
|
|
22
22
|
DateTime.parse("12:02:03p").should == DateTime.civil(DateTime.today.year, DateTime.today.month, DateTime.today.day, 12, 2, 3)
|
23
23
|
proc{DateTime.parse("13:02:03p")}.should raise_error(ArgumentError)
|
24
24
|
proc{DateTime.parse("00:02:03p")}.should raise_error(ArgumentError)
|
25
|
-
proc{DateTime.parse("00:02:
|
25
|
+
proc{DateTime.parse("00:02:03rsdf")}.should raise_error(ArgumentError)
|
26
26
|
end
|
27
27
|
|
28
28
|
it "should use the current time offset if no time offset is specified" do
|
@@ -34,6 +34,50 @@ describe "DateTime#parse" do
|
|
34
34
|
DateTime.parse("01:02:03-01").should == DateTime.civil(DateTime.today.year, DateTime.today.month, DateTime.today.day, 1, 2, 3, 0, -3600)
|
35
35
|
end
|
36
36
|
|
37
|
+
it "should parse the time zone abbreviations supported by ruby's Time class" do
|
38
|
+
DateTime.parse("01:02:03 UTC").offset.should == 0
|
39
|
+
DateTime.parse("01:02:03 UT").offset.should == 0
|
40
|
+
DateTime.parse("01:02:03 GMT").offset.should == 0
|
41
|
+
DateTime.parse("01:02:03 EST").offset.should == -5*3600
|
42
|
+
DateTime.parse("01:02:03 EDT").offset.should == -4*3600
|
43
|
+
DateTime.parse("01:02:03 CST").offset.should == -6*3600
|
44
|
+
DateTime.parse("01:02:03 CDT").offset.should == -5*3600
|
45
|
+
DateTime.parse("01:02:03 MST").offset.should == -7*3600
|
46
|
+
DateTime.parse("01:02:03 MDT").offset.should == -6*3600
|
47
|
+
DateTime.parse("01:02:03 PST").offset.should == -8*3600
|
48
|
+
DateTime.parse("01:02:03 PDT").offset.should == -7*3600
|
49
|
+
DateTime.parse("01:02:03 A").offset.should == 1*3600
|
50
|
+
DateTime.parse("01:02:03 B").offset.should == 2*3600
|
51
|
+
DateTime.parse("01:02:03 C").offset.should == 3*3600
|
52
|
+
DateTime.parse("01:02:03 D").offset.should == 4*3600
|
53
|
+
DateTime.parse("01:02:03 E").offset.should == 5*3600
|
54
|
+
DateTime.parse("01:02:03 F").offset.should == 6*3600
|
55
|
+
DateTime.parse("01:02:03 G").offset.should == 7*3600
|
56
|
+
DateTime.parse("01:02:03 H").offset.should == 8*3600
|
57
|
+
DateTime.parse("01:02:03 I").offset.should == 9*3600
|
58
|
+
DateTime.parse("01:02:03 K").offset.should == 10*3600
|
59
|
+
DateTime.parse("01:02:03 L").offset.should == 11*3600
|
60
|
+
DateTime.parse("01:02:03 M").offset.should == 12*3600
|
61
|
+
DateTime.parse("01:02:03 N").offset.should == -1*3600
|
62
|
+
DateTime.parse("01:02:03 O").offset.should == -2*3600
|
63
|
+
DateTime.parse("01:02:03 P").offset.should == -3*3600
|
64
|
+
DateTime.parse("01:02:03 Q").offset.should == -4*3600
|
65
|
+
DateTime.parse("01:02:03 R").offset.should == -5*3600
|
66
|
+
DateTime.parse("01:02:03 S").offset.should == -6*3600
|
67
|
+
DateTime.parse("01:02:03 T").offset.should == -7*3600
|
68
|
+
DateTime.parse("01:02:03 U").offset.should == -8*3600
|
69
|
+
DateTime.parse("01:02:03 V").offset.should == -9*3600
|
70
|
+
DateTime.parse("01:02:03 W").offset.should == -10*3600
|
71
|
+
DateTime.parse("01:02:03 X").offset.should == -11*3600
|
72
|
+
DateTime.parse("01:02:03 Y").offset.should == -12*3600
|
73
|
+
DateTime.parse("01:02:03 Z").offset.should == 0
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should parse the time strings output by ruby's Time class" do
|
77
|
+
proc{DateTime.parse(Time.now.to_s)}.should_not raise_error
|
78
|
+
proc{DateTime.parse(Time.now.strftime('%+'))}.should_not raise_error
|
79
|
+
end
|
80
|
+
|
37
81
|
it "can handle DD as month day number" do
|
38
82
|
DateTime.parse("10").should == DateTime.civil(DateTime.today.year, DateTime.today.month, 10)
|
39
83
|
DateTime.parse("10 01:02:03").should == DateTime.civil(DateTime.today.year, DateTime.today.month, 10, 1, 2, 3)
|
@@ -318,15 +362,35 @@ describe "DateTime parser modifications" do
|
|
318
362
|
DateTime.reset_parsers!
|
319
363
|
end
|
320
364
|
|
365
|
+
it "should raise an ArgumentError if it can't parse a date" do
|
366
|
+
proc{DateTime.parse("today")}.should raise_error(ArgumentError)
|
367
|
+
end
|
368
|
+
|
321
369
|
it "should be able to add a parser to an existing parser type that takes precedence" do
|
322
370
|
d = DateTime.now
|
323
|
-
proc{DateTime.parse("today")}.should raise_error(ArgumentError)
|
324
371
|
DateTime.add_parser(:iso, /\Anow\z/){{:civil=>[d.year, d.mon, d.day], :parts=>[d.hour, d.min, d.sec, d.usec], :offset=>d.offset}}
|
325
372
|
DateTime.parse("now").should == d
|
326
373
|
end
|
327
374
|
|
375
|
+
it "should be able to handle parsers that return Date instances" do
|
376
|
+
d = DateTime.now
|
377
|
+
DateTime.add_parser(:iso, /\Anow\z/){d}
|
378
|
+
DateTime.parse("now").should == d
|
379
|
+
end
|
380
|
+
|
381
|
+
it "should be able to specify a strptime format string for a parser" do
|
382
|
+
DateTime.add_parser(:iso, "%Z||%S>>%M>>%H||%d<<%m<<%Y")
|
383
|
+
DateTime.parse("UTC||06>>05>>04||03<<02<<2001").should == DateTime.new(2001,2,3,4,5,6)
|
384
|
+
end
|
385
|
+
|
386
|
+
it "should assume current seconds if just offset is given" do
|
387
|
+
DateTime.add_parser_type(:mine)
|
388
|
+
DateTime.use_parsers(:mine)
|
389
|
+
DateTime.add_parser(:mine, "%Z")
|
390
|
+
DateTime.parse("UTC").sec.should == DateTime.now.sec
|
391
|
+
end
|
392
|
+
|
328
393
|
it "should be able to add new parser types" do
|
329
|
-
proc{DateTime.parse("today")}.should raise_error(ArgumentError)
|
330
394
|
DateTime.add_parser_type(:mine)
|
331
395
|
d = DateTime.now
|
332
396
|
DateTime.add_parser(:mine, /\Anow\z/){{:civil=>[d.year, d.mon, d.day], :parts=>[d.hour, d.min, d.sec, d.usec], :offset=>d.offset}}
|
@@ -52,17 +52,18 @@ describe "DateTime#strftime" do
|
|
52
52
|
DateTime.civil(2008, 11, 12, 14, 3, 31, 0, -28800).strftime('%s').should == "1226527411"
|
53
53
|
end
|
54
54
|
|
55
|
-
it "should be able to print the time zone offset as a Z if the offset is zero" do
|
56
|
-
DateTime.civil(2000, 4, 6, 10, 11, 12).strftime('%Z').should == "Z"
|
57
|
-
DateTime.civil(2000, 4, 6, 10, 11, 12, 0, -43200).strftime('%Z').should == "-12:00"
|
58
|
-
end
|
59
|
-
|
60
55
|
it "should be able to print the time zone offset as a string of hours and minutes" do
|
61
|
-
DateTime.civil(2000, 4, 6, 10, 11, 12).strftime('%z').should == "+
|
62
|
-
DateTime.civil(2000, 4, 6, 10, 11, 12, 0, -43200).strftime('%z').should == "-
|
63
|
-
DateTime.civil(2000, 4, 6, 10, 11, 12, 0, 43200).strftime('%z').should == "+
|
64
|
-
DateTime.civil(2000, 4, 6, 10, 11, 12, 0, -3600).strftime('%z').should == "-
|
65
|
-
DateTime.civil(2000, 4, 6, 10, 11, 12, 0, 3600).strftime('%z').should == "+
|
56
|
+
DateTime.civil(2000, 4, 6, 10, 11, 12).strftime('%z').should == "+0000"
|
57
|
+
DateTime.civil(2000, 4, 6, 10, 11, 12, 0, -43200).strftime('%z').should == "-1200"
|
58
|
+
DateTime.civil(2000, 4, 6, 10, 11, 12, 0, 43200).strftime('%z').should == "+1200"
|
59
|
+
DateTime.civil(2000, 4, 6, 10, 11, 12, 0, -3600).strftime('%z').should == "-0100"
|
60
|
+
DateTime.civil(2000, 4, 6, 10, 11, 12, 0, 3600).strftime('%z').should == "+0100"
|
61
|
+
|
62
|
+
DateTime.civil(2000, 4, 6, 10, 11, 12).strftime('%Z').should == "+00:00"
|
63
|
+
DateTime.civil(2000, 4, 6, 10, 11, 12, 0, -43200).strftime('%Z').should == "-12:00"
|
64
|
+
DateTime.civil(2000, 4, 6, 10, 11, 12, 0, 43200).strftime('%Z').should == "+12:00"
|
65
|
+
DateTime.civil(2000, 4, 6, 10, 11, 12, 0, -3600).strftime('%Z').should == "-01:00"
|
66
|
+
DateTime.civil(2000, 4, 6, 10, 11, 12, 0, 3600).strftime('%Z').should == "+01:00"
|
66
67
|
end
|
67
68
|
|
68
69
|
############################
|
@@ -97,6 +98,6 @@ describe "DateTime#strftime" do
|
|
97
98
|
it "should be able to print the common date and timezone" do
|
98
99
|
DateTime.civil(2000, 4, 6, 10, 11, 12).strftime("%+").should == "Thu Apr 6 10:11:12 +00:00 2000"
|
99
100
|
DateTime.civil(2000, 4, 6, 10, 11, 12, 0, 43200).strftime("%+").should == "Thu Apr 6 10:11:12 +12:00 2000"
|
100
|
-
DateTime.civil(2000, 4, 6, 10, 11, 12).strftime("%+").should == DateTime.civil(2000, 4, 6, 10, 11, 12).strftime('%a %b %e %H:%M:%S %
|
101
|
+
DateTime.civil(2000, 4, 6, 10, 11, 12).strftime("%+").should == DateTime.civil(2000, 4, 6, 10, 11, 12).strftime('%a %b %e %H:%M:%S %Z %Y')
|
101
102
|
end
|
102
103
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: third_base
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-08-17 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|