third_base 1.1.1 → 1.2.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.
- 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
|
|