third_base 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +19 -0
- data/README +261 -0
- data/benchmark/date.rb +18 -0
- data/benchmark/datetime.rb +18 -0
- data/bin/third_base +4 -0
- data/lib/third_base/compat/date/format.rb +3 -0
- data/lib/third_base/compat/date.rb +3 -0
- data/lib/third_base/compat.rb +405 -0
- data/lib/third_base/date.rb +674 -0
- data/lib/third_base/datetime.rb +385 -0
- data/lib/third_base.rb +2 -0
- data/spec/compat/compat_class_methods_spec.rb +208 -0
- data/spec/compat/compat_instance_methods_spec.rb +54 -0
- data/spec/compat/date_spec.rb +56 -0
- data/spec/compat/datetime_spec.rb +77 -0
- data/spec/compat_spec_helper.rb +2 -0
- data/spec/date/accessor_spec.rb +134 -0
- data/spec/date/add_month_spec.rb +28 -0
- data/spec/date/add_spec.rb +24 -0
- data/spec/date/boat_spec.rb +31 -0
- data/spec/date/civil_spec.rb +47 -0
- data/spec/date/commercial_spec.rb +34 -0
- data/spec/date/constants_spec.rb +18 -0
- data/spec/date/downto_spec.rb +17 -0
- data/spec/date/eql_spec.rb +9 -0
- data/spec/date/hash_spec.rb +13 -0
- data/spec/date/julian_spec.rb +13 -0
- data/spec/date/leap_spec.rb +19 -0
- data/spec/date/minus_month_spec.rb +26 -0
- data/spec/date/minus_spec.rb +47 -0
- data/spec/date/ordinal_spec.rb +13 -0
- data/spec/date/parse_spec.rb +227 -0
- data/spec/date/step_spec.rb +55 -0
- data/spec/date/strftime_spec.rb +132 -0
- data/spec/date/strptime_spec.rb +118 -0
- data/spec/date/succ_spec.rb +16 -0
- data/spec/date/today_spec.rb +11 -0
- data/spec/date/upto_spec.rb +17 -0
- data/spec/date_spec_helper.rb +3 -0
- data/spec/datetime/accessor_spec.rb +53 -0
- data/spec/datetime/add_spec.rb +36 -0
- data/spec/datetime/boat_spec.rb +43 -0
- data/spec/datetime/constructor_spec.rb +58 -0
- data/spec/datetime/eql_spec.rb +11 -0
- data/spec/datetime/minus_spec.rb +65 -0
- data/spec/datetime/now_spec.rb +14 -0
- data/spec/datetime/parse_spec.rb +338 -0
- data/spec/datetime/strftime_spec.rb +102 -0
- data/spec/datetime/strptime_spec.rb +84 -0
- data/spec/datetime_spec_helper.rb +3 -0
- data/spec/spec_helper.rb +54 -0
- metadata +107 -0
@@ -0,0 +1,385 @@
|
|
1
|
+
require 'third_base/date'
|
2
|
+
|
3
|
+
module ThirdBase
|
4
|
+
# ThirdBase's DateTime class, which builds on the Date class and adds a time component of
|
5
|
+
# hours, minutes, seconds, microseconds, and an offset from UTC.
|
6
|
+
class DateTime < Date
|
7
|
+
|
8
|
+
PARSER_LIST = []
|
9
|
+
DEFAULT_PARSER_LIST = [:time, :iso, :us, :num]
|
10
|
+
DEFAULT_PARSERS = {}
|
11
|
+
TIME_RE_STRING = '(?:[T ]?([\d ]?\d):(\d\d)(?::(\d\d(\.\d+)?))?([ap]m?)? ?(Z|[+-](?:\d\d:?(?:\d\d)?))?)?'
|
12
|
+
DEFAULT_PARSERS[:time] = [[%r{\A#{TIME_RE_STRING}\z}io, proc do |m|
|
13
|
+
unless m[0] == ''
|
14
|
+
t = Time.now
|
15
|
+
add_parsed_time_parts(m, {:civil=>[t.year, t.mon, t.day]}, 1)
|
16
|
+
end
|
17
|
+
end]]
|
18
|
+
DEFAULT_PARSERS[:iso] = [[%r{\A(-?\d{4})[-./ ](\d\d)[-./ ](\d\d)#{TIME_RE_STRING}\z}io, proc{|m| add_parsed_time_parts(m, :civil=>[m[1].to_i, m[2].to_i, m[3].to_i])}]]
|
19
|
+
DEFAULT_PARSERS[:us] = [[%r{\A(\d\d?)[-./ ](\d\d?)[-./ ](\d\d(?:\d\d)?)#{TIME_RE_STRING}\z}io, proc{|m| add_parsed_time_parts(m, :civil=>[two_digit_year(m[3]), m[1].to_i, m[2].to_i])}],
|
20
|
+
[%r{\A(\d\d?)/(\d?\d)#{TIME_RE_STRING}\z}o, proc{|m| add_parsed_time_parts(m, {:civil=>[Time.now.year, m[1].to_i, m[2].to_i]}, 3)}],
|
21
|
+
[%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])}],
|
22
|
+
[%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
|
+
[%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)}]]
|
25
|
+
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
|
+
[%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
|
+
DEFAULT_PARSERS[:num] = [[%r{\A(\d{2,8})#{TIME_RE_STRING}\z}io, proc do |n|
|
28
|
+
m = n[1]
|
29
|
+
add_parsed_time_parts(n, (
|
30
|
+
case m.length
|
31
|
+
when 2
|
32
|
+
t = Time.now
|
33
|
+
{:civil=>[t.year, t.mon, m.to_i]}
|
34
|
+
when 3
|
35
|
+
{:ordinal=>[Time.now.year, m.to_i]}
|
36
|
+
when 4
|
37
|
+
{:civil=>[Time.now.year, m[0..1].to_i, m[2..3].to_i]}
|
38
|
+
when 5
|
39
|
+
{:ordinal=>[two_digit_year(m[0..1]), m[2..4].to_i]}
|
40
|
+
when 6
|
41
|
+
{:civil=>[two_digit_year(m[0..1]), m[2..3].to_i, m[4..5].to_i]}
|
42
|
+
when 7
|
43
|
+
{:ordinal=>[m[0..3].to_i, m[4..6].to_i]}
|
44
|
+
when 8
|
45
|
+
{:civil=>[m[0..3].to_i, m[4..5].to_i, m[6..7].to_i]}
|
46
|
+
end
|
47
|
+
), 2)
|
48
|
+
end
|
49
|
+
]]
|
50
|
+
|
51
|
+
STRPTIME_PROC_H = proc{|h,x| h[:hour] = x.to_i}
|
52
|
+
STRPTIME_PROC_M = proc{|h,x| h[:min] = x.to_i}
|
53
|
+
STRPTIME_PROC_P = proc{|h,x| h[:meridian] = x.downcase == 'pm' ? :pm : :am}
|
54
|
+
STRPTIME_PROC_S = proc{|h,x| h[:sec] = x.to_i}
|
55
|
+
STRPTIME_PROC_s = proc do |h,x|
|
56
|
+
j, i = x.to_i.divmod(86400)
|
57
|
+
hours, i = i.divmod(3600)
|
58
|
+
minutes, seconds = i.divmod(60)
|
59
|
+
h.merge!(:jd=>j+UNIXEPOCH, :hour=>hours, :min=>minutes, :sec=>seconds)
|
60
|
+
end
|
61
|
+
STRPTIME_PROC_z = proc{|h,x| x=x.gsub(':',''); h[:offset] = (x == 'Z' ? 0 : x[0..2].to_i*3600 + x[3..4].to_i*60)}
|
62
|
+
|
63
|
+
# Public Class Methods
|
64
|
+
|
65
|
+
# Create a new DateTime with the given year, month, day of month, hour, minute, second, microsecond and offset.
|
66
|
+
def self.civil(year, mon, day, hour=0, min=0, sec=0, usec=0, offset=0)
|
67
|
+
new!(:civil=>[year, mon, day], :parts=>[hour, min, sec, usec], :offset=>offset)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Create a new DateTime with the given commercial week year, commercial week, commercial week day, hour, minute
|
71
|
+
# second, microsecond, and offset.
|
72
|
+
def self.commercial(cwyear, cweek, cwday=5, hour=0, min=0, sec=0, usec=0, offset=0)
|
73
|
+
new!(:commercial=>[cwyear, cweek, cwday], :parts=>[hour, min, sec, usec], :offset=>offset)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Create a new DateTime with the given julian date, hour, minute, second, microsecond, and offset.
|
77
|
+
def self.jd(jd, hour=0, min=0, sec=0, usec=0, offset=0)
|
78
|
+
new!(:jd=>jd, :parts=>[hour, min, sec, usec], :offset=>offset)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Create a new DateTime with the given julian day, fraction of the day (0.5 is Noon), and offset.
|
82
|
+
def self.jd_fract(jd, fract=0.0, offset=0)
|
83
|
+
new!(:jd=>jd, :fract=>fract, :offset=>offset)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Create a new DateTime with the current date and time.
|
87
|
+
def self.now
|
88
|
+
t = Time.now
|
89
|
+
new!(:civil=>[t.year, t.mon, t.day], :parts=>[t.hour, t.min, t.sec, t.usec], :offset=>t.utc_offset)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Create a new DateTime with the given year, day of year, hour, minute, second, microsecond, and offset.
|
93
|
+
def self.ordinal(year, yday, hour=0, min=0, sec=0, usec=0, offset=0)
|
94
|
+
new!(:ordinal=>[year, yday], :parts=>[hour, min, sec, usec], :offset=>offset)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Private Class Methods
|
98
|
+
|
99
|
+
def self._expand_strptime_format(v)
|
100
|
+
case v
|
101
|
+
when '%c' then '%a %b %e %H:%M:%S %Y'
|
102
|
+
when '%T', '%X' then '%H:%M:%S'
|
103
|
+
when '%R' then '%H:%M'
|
104
|
+
when '%r' then '%I:%M:%S %p'
|
105
|
+
when '%+' then '%a %b %e %H:%M:%S %z %Y'
|
106
|
+
else super(v)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def self._strptime_part(v)
|
111
|
+
case v
|
112
|
+
when 'H', 'I' then ['(\d\d)', STRPTIME_PROC_H]
|
113
|
+
when 'k', 'l' then ['(\d?\d)', STRPTIME_PROC_H]
|
114
|
+
when 'M' then ['(\d\d)', STRPTIME_PROC_M]
|
115
|
+
when 'P', 'p' then ['([ap]m)', STRPTIME_PROC_P]
|
116
|
+
when 'S' then ['(\d\d)', STRPTIME_PROC_S]
|
117
|
+
when 's' then ['(\d+)', STRPTIME_PROC_s]
|
118
|
+
when 'z', 'Z' then ['(Z|[+-](?:\d{4}|\d\d:\d\d))', STRPTIME_PROC_z]
|
119
|
+
else super(v)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# m:
|
124
|
+
# * i + 0 : hour
|
125
|
+
# * i + 1 : minute
|
126
|
+
# * i + 2 : second
|
127
|
+
# * i + 3 : sec fraction
|
128
|
+
# * i + 4 : meridian indicator
|
129
|
+
# * i + 5 : time zone
|
130
|
+
def self.add_parsed_time_parts(m, h, i=4)
|
131
|
+
hour = m[i].to_i
|
132
|
+
meridian = m[i+4]
|
133
|
+
hour = hour_with_meridian(hour, /a/io.match(meridian) ? :am : :pm) if meridian
|
134
|
+
offset = if m[i+5]
|
135
|
+
x = m[i+5].gsub(':','')
|
136
|
+
x == 'Z' ? 0 : x[0..2].to_i*3600 + x[3..4].to_i*60
|
137
|
+
else
|
138
|
+
Time.now.utc_offset
|
139
|
+
end
|
140
|
+
h.merge!(:parts=>[hour, m[i+1].to_i, m[i+2].to_i, (m[i+3].to_f/0.000001).to_i], :offset=>offset)
|
141
|
+
h
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.default_parser_hash
|
145
|
+
DEFAULT_PARSERS
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.default_parser_list
|
149
|
+
DEFAULT_PARSER_LIST
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.hour_with_meridian(hour, meridian)
|
153
|
+
raise(ArgumentError, 'invalid date') unless hour and hour >= 1 and hour <= 12
|
154
|
+
if meridian == :am
|
155
|
+
hour == 12 ? 0 : hour
|
156
|
+
else
|
157
|
+
hour < 12 ? hour + 12 : hour
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.new_from_parts(date_hash)
|
162
|
+
date_hash[:hour] = hour_with_meridian(date_hash[:hour], date_hash[:meridian]) if date_hash[:meridian]
|
163
|
+
d = now
|
164
|
+
weights = {:cwyear=>1, :year=>1, :cweek=>2, :cwday=>3, :yday=>3, :month=>2, :day=>3, :hour=>4, :min=>5, :sec=>6}
|
165
|
+
columns = {}
|
166
|
+
min = 7
|
167
|
+
max = 0
|
168
|
+
date_hash.each do |k,v|
|
169
|
+
if w = weights[k]
|
170
|
+
min = w if w < min
|
171
|
+
max = w if w > max
|
172
|
+
end
|
173
|
+
end
|
174
|
+
offset = date_hash[:offset] || d.offset
|
175
|
+
hour = date_hash[:hour] || (min > 4 ? d.hour : 0)
|
176
|
+
minute = date_hash[:min] || (min > 5 ? d.min : 0)
|
177
|
+
sec = date_hash[:sec] || 0
|
178
|
+
if date_hash[:jd]
|
179
|
+
jd(date_hash[:jd], hour, minute, sec, 0, offset)
|
180
|
+
elsif date_hash[:year] || date_hash[:yday] || date_hash[:month] || date_hash[:day] || !(date_hash[:cwyear] || date_hash[:cweek])
|
181
|
+
if date_hash[:yday]
|
182
|
+
ordinal(date_hash[:year]||d.year, date_hash[:yday], hour, minute, sec, 0, offset)
|
183
|
+
else
|
184
|
+
civil(date_hash[:year]||d.year, date_hash[:month]||(min > 2 ? d.mon : 1), date_hash[:day]||(min > 3 ? d.day : 1), hour, minute, sec, 0, offset)
|
185
|
+
end
|
186
|
+
elsif date_hash[:cwyear] || date_hash[:cweek] || date_hash[:cwday]
|
187
|
+
commercial(date_hash[:cwyear]||d.cwyear, date_hash[:cweek]||(min > 2 ? d.cweek : 1), date_hash[:cwday]||(min > 3 ? d.cwday : 1), hour, minute, sec, 0, offset)
|
188
|
+
else
|
189
|
+
raise ArgumentError, 'invalid date'
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def self.parser_hash
|
194
|
+
PARSERS
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.parser_list
|
198
|
+
PARSER_LIST
|
199
|
+
end
|
200
|
+
|
201
|
+
def self.strptime_default
|
202
|
+
'%Y-%m-%dT%H:%M:%S'
|
203
|
+
end
|
204
|
+
|
205
|
+
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
|
206
|
+
|
207
|
+
reset_parsers!
|
208
|
+
|
209
|
+
# Instance Methods
|
210
|
+
|
211
|
+
# This datetime's offset from UTC, in seconds.
|
212
|
+
attr_reader :offset
|
213
|
+
alias utc_offset offset
|
214
|
+
|
215
|
+
# Called by DateTime.new!, should be a hash with the following possible keys:
|
216
|
+
#
|
217
|
+
# * :civil, :commericial, :jd, :ordinal : See ThirdBase::Date#initialize
|
218
|
+
# * :fract : The fraction of the day (0.5 is Noon)
|
219
|
+
# * :offset : offset from UTC, in seconds.
|
220
|
+
# * :parts : an array with 4 elements, hour, minute, second, and microsecond
|
221
|
+
#
|
222
|
+
# Raises an ArgumentError if an invalid date is used. DateTime objects are immutable once created.
|
223
|
+
def initialize(opts)
|
224
|
+
@offset = opts[:offset]
|
225
|
+
raise(ArgumentError, 'invalid datetime') unless @offset.is_a?(Integer) and @offset <= 43200 and @offset >= -43200
|
226
|
+
if opts[:parts]
|
227
|
+
@hour, @min, @sec, @usec = opts[:parts]
|
228
|
+
raise(ArgumentError, 'invalid datetime') unless @hour.is_a?(Integer) and @min.is_a?(Integer) and @sec.is_a?(Integer) and @usec.is_a?(Integer)
|
229
|
+
elsif opts[:fract]
|
230
|
+
@fract = opts[:fract]
|
231
|
+
raise(ArgumentError, 'invalid datetime') unless @fract.is_a?(Float) and @fract < 1.0 and @fract >= 0.0
|
232
|
+
else
|
233
|
+
raise(ArgumentError, 'invalid datetime')
|
234
|
+
end
|
235
|
+
super(opts)
|
236
|
+
end
|
237
|
+
|
238
|
+
# Return a new datetune with the given number of days added to this datetime. If d is a Float
|
239
|
+
# adds a fractional date, with possible loss of precision. If d is an integer,
|
240
|
+
# the returned date has the same time components as the current date. In both
|
241
|
+
# cases, the offset for the new date is the same as for this date.
|
242
|
+
def +(d)
|
243
|
+
case d
|
244
|
+
when Float
|
245
|
+
d, f = d.to_f.divmod(1)
|
246
|
+
f = fract + f
|
247
|
+
m, f = f.divmod(1)
|
248
|
+
self.class.jd_fract(jd+d+m, f, @offset)
|
249
|
+
when Integer
|
250
|
+
new_jd(jd+d)
|
251
|
+
else
|
252
|
+
raise(TypeError, "d must be a Float or Integer")
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# Return a new datetune with the given number of days subtracted from this datetime.
|
257
|
+
# If d is a DateTime, returns the difference between the two datetimes as a Float,
|
258
|
+
# considering both datetimes date, time, and offest.
|
259
|
+
def -(d)
|
260
|
+
case d
|
261
|
+
when self.class
|
262
|
+
(jd - d.jd) + (fract - d.fract) + (@offset - d.offset)/86400.0
|
263
|
+
when Integer, Float
|
264
|
+
self + -d
|
265
|
+
else
|
266
|
+
raise TypeError, "d should be #{self.class}, Float, or Integer"
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
# Compares two datetimes. If the given datetime is an Integer, returns 1 unless
|
271
|
+
# this datetime's time components are all 0, in which case it returns 0.
|
272
|
+
# If the given datetime is a Float, calculates this date's julian date plus the
|
273
|
+
# date fraction and compares it to the given datetime, and returns 0 only if the
|
274
|
+
# two are very close together. This code does not take into account time offsets.
|
275
|
+
def <=>(datetime)
|
276
|
+
case datetime
|
277
|
+
when Integer
|
278
|
+
if ((d = (jd <=> datetime)) == 0)
|
279
|
+
(hour == 0 and min == 0 and sec == 0 and usec == 0) ? 0 : 1
|
280
|
+
else
|
281
|
+
d
|
282
|
+
end
|
283
|
+
when Float
|
284
|
+
diff = jd+fract - datetime
|
285
|
+
if diff.abs <= 1.15740740740741e-011
|
286
|
+
0
|
287
|
+
else
|
288
|
+
diff > 0.0 ? 1 : -1
|
289
|
+
end
|
290
|
+
when self.class
|
291
|
+
((d = super) == 0) && ((d = (hour <=> datetime.hour)) == 0) && ((d = (min <=> datetime.min)) == 0) && ((d = (sec <=> datetime.sec)) == 0) && ((d = (usec <=> datetime.usec)) == 0)
|
292
|
+
d
|
293
|
+
else
|
294
|
+
raise TypeError, "d should be #{self.class}, Float, or Integer"
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# Two DateTimes are equal only if their dates and time components are the same, not counting the offset.
|
299
|
+
def ==(datetime)
|
300
|
+
return false unless DateTime === datetime
|
301
|
+
super and hour == datetime.hour and min == datetime.min and sec == datetime.sec and usec == datetime.usec
|
302
|
+
end
|
303
|
+
alias_method :eql?, :==
|
304
|
+
|
305
|
+
# Returns the fraction of the day for this datetime (Noon is 0.5)
|
306
|
+
def fract
|
307
|
+
@fract ||= (@hour*3600+@min*60+@sec+@usec/1000000.0)/86400.0
|
308
|
+
end
|
309
|
+
|
310
|
+
# Returns the hour of this datetime.
|
311
|
+
def hour
|
312
|
+
@hour ||= time_parts[0]
|
313
|
+
end
|
314
|
+
|
315
|
+
# Returns the minute of this datetime.
|
316
|
+
def min
|
317
|
+
@min ||= time_parts[1]
|
318
|
+
end
|
319
|
+
|
320
|
+
# Returns the second of this datetime.
|
321
|
+
def sec
|
322
|
+
@sec ||= time_parts[2]
|
323
|
+
end
|
324
|
+
|
325
|
+
# Returns the microsecond of this datetime.
|
326
|
+
def usec
|
327
|
+
@usec ||= time_parts[3]
|
328
|
+
end
|
329
|
+
|
330
|
+
# Return the offset as a time zone string (+/-HHMM).
|
331
|
+
def zone
|
332
|
+
strftime('%z')
|
333
|
+
end
|
334
|
+
|
335
|
+
private
|
336
|
+
|
337
|
+
def _strftime(v)
|
338
|
+
case v
|
339
|
+
when 'c' then strftime('%a %b %e %H:%M:%S %Y')
|
340
|
+
when 'H' then '%02d' % hour
|
341
|
+
when 'I' then '%02d' % ((hour % 12).nonzero? or 12)
|
342
|
+
when 'k' then '%2d' % hour
|
343
|
+
when 'l' then '%2d' % ((hour % 12).nonzero? or 12)
|
344
|
+
when 'M' then '%02d' % min
|
345
|
+
when 'P' then hour < 12 ? 'am' : 'pm'
|
346
|
+
when 'p' then hour < 12 ? 'AM' : 'PM'
|
347
|
+
when 'R' then strftime('%H:%M')
|
348
|
+
when 'r' then strftime('%I:%M:%S %p')
|
349
|
+
when 'S' then '%02d' % sec
|
350
|
+
when 's' then '%d' % ((jd - UNIXEPOCH)*86400 + hour*3600 + min*60 + sec - @offset)
|
351
|
+
when 'T', 'X' then strftime('%H:%M:%S')
|
352
|
+
when '+' then strftime('%a %b %e %H:%M:%S %z %Y')
|
353
|
+
when 'Z' then @offset == 0 ? 'Z' : _strftime('z')
|
354
|
+
when 'z' then "%+03d:%02d" % (@offset/60).divmod(60)
|
355
|
+
else super(v)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def fract_to_hmsu(p)
|
360
|
+
hour, p = (p.to_f*24).divmod(1)
|
361
|
+
min, p = (p*60).divmod(1)
|
362
|
+
sec, sec_fract = (p*60).divmod(1)
|
363
|
+
[hour, min, sec, (sec_fract*1000000).to_i]
|
364
|
+
end
|
365
|
+
|
366
|
+
def new_civil(y, m, d)
|
367
|
+
self.class.new(y, m, d, hour, min, sec, usec, @offset)
|
368
|
+
end
|
369
|
+
|
370
|
+
def new_jd(j)
|
371
|
+
self.class.jd(j, hour, min, sec, usec, @offset)
|
372
|
+
end
|
373
|
+
|
374
|
+
def strftime_default
|
375
|
+
'%Y-%m-%dT%H:%M:%S%z'
|
376
|
+
end
|
377
|
+
|
378
|
+
def time_parts
|
379
|
+
unless @hour && @min && @sec && @usec
|
380
|
+
@hour, @min, @sec, @usec = fract_to_hmsu(fract)
|
381
|
+
end
|
382
|
+
[@hour, @min, @sec, @usec]
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
data/lib/third_base.rb
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
require(File.join(File.dirname(__FILE__), '..', 'compat_spec_helper'))
|
2
|
+
|
3
|
+
describe ThirdBase::CompatClassMethods do
|
4
|
+
it "#new0 should be the same as new!" do
|
5
|
+
Date.new0(:jd=>2454156).should == Date.new!(:jd=>2454156)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "#new1 should be the same as jd" do
|
9
|
+
Date.new1(2454156).should == Date.jd(2454156)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "#new2 should be the same as ordinal" do
|
13
|
+
Date.new2(2008, 10).should == Date.ordinal(2008, 10)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "#new3 should be the same as new" do
|
17
|
+
Date.new3(2008, 10, 2).should == Date.new(2008, 10, 2)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "#neww should be the same as commercial" do
|
21
|
+
Date.neww(2008, 10, 2).should == Date.commercial(2008, 10, 2)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "#_parse should parse the date string and return a hash" do
|
25
|
+
Date._parse('2008-10-20 11:12:13-08:00').should == {:mon=>10, :zone=>'-08:00', :sec=>13, :sec_fraction=>0.0, :year=>2008, :hour=>11, :offset=>-28800, :mday=>20, :min=>12}
|
26
|
+
Date._parse('2008-10-20 11:12:13-08:00', true).should == {:mon=>10, :zone=>'-08:00', :sec=>13, :sec_fraction=>0.0, :year=>2008, :hour=>11, :offset=>-28800, :mday=>20, :min=>12}
|
27
|
+
end
|
28
|
+
|
29
|
+
it "#ajd_to_amjd should convert a astronomical julian date to a astronomical modified julian date" do
|
30
|
+
Date.ajd_to_amjd(2400002).should == 1
|
31
|
+
end
|
32
|
+
|
33
|
+
it "#ajd_to_amjd should convert a astronomical julian date to a julian date" do
|
34
|
+
Date.ajd_to_jd(1).should == 1
|
35
|
+
Date.ajd_to_jd(1, 1).should == 1
|
36
|
+
end
|
37
|
+
|
38
|
+
it "#amjd_to_amd should convert a astronomical modified julian date to a astronomical julian date" do
|
39
|
+
Date.amjd_to_ajd(1).should == 2400002
|
40
|
+
end
|
41
|
+
|
42
|
+
it "#civil_to_jd should convert a civil date into a Julian day number" do
|
43
|
+
Date.civil_to_jd(2007, 2, 24).should == 2454156
|
44
|
+
Date.civil_to_jd(2007, 2, 24, 1).should == 2454156
|
45
|
+
end
|
46
|
+
|
47
|
+
it "#commercial_to_jd should convert a commercial date (year - week - day of week) into a Julian day number" do
|
48
|
+
Date.commercial_to_jd(2007, 45, 1).should == 2454410
|
49
|
+
Date.commercial_to_jd(2007, 45, 1, 1).should == 2454410
|
50
|
+
end
|
51
|
+
|
52
|
+
it "#day_fraction_to_time should convert a fraction of a day to an array of time parts" do
|
53
|
+
Date.day_fraction_to_time(0.5).should == [12, 0, 0, 0.0]
|
54
|
+
end
|
55
|
+
|
56
|
+
it "#gregorian? should return whether the first argument is greater than the second" do
|
57
|
+
Date.gregorian?(1, 1).should == false
|
58
|
+
Date.gregorian?(2, 1).should == true
|
59
|
+
end
|
60
|
+
|
61
|
+
it "#ns? should be the same as gregorian?" do
|
62
|
+
Date.ns?(2, 1).should == Date.gregorian?(2, 1)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "#leap? should determine whether a year is a leap year" do
|
66
|
+
Date.leap?(1900).should == false
|
67
|
+
Date.leap?(1901).should == false
|
68
|
+
Date.leap?(1899).should == false
|
69
|
+
Date.leap?(1904).should == true
|
70
|
+
Date.leap?(1896).should == true
|
71
|
+
Date.leap?(1999).should == false
|
72
|
+
Date.leap?(2001).should == false
|
73
|
+
Date.leap?(2000).should == true
|
74
|
+
end
|
75
|
+
|
76
|
+
it "#gregorian_leap? should be the same as leap?" do
|
77
|
+
Date.gregorian_leap?(2000).should == Date.leap?(2000)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "#jd_to_ajd should convert a Julian day into a astronomical julian date" do
|
81
|
+
Date.jd_to_ajd(2454156, 0).should == 2454156
|
82
|
+
Date.jd_to_ajd(2454156, 1, 1).should == 2454156
|
83
|
+
end
|
84
|
+
|
85
|
+
it "#jd_to_civil should convert a Julian day into a civil date" do
|
86
|
+
Date.jd_to_civil(2454156).should == [2007, 2, 24]
|
87
|
+
Date.jd_to_civil(2454156, 1).should == [2007, 2, 24]
|
88
|
+
end
|
89
|
+
|
90
|
+
it "#jd_to_commercial should convert a Julian day number into a commercial date" do
|
91
|
+
Date.jd_to_commercial(2454410).should == [2007, 45, 1]
|
92
|
+
Date.jd_to_commercial(2454410, 1).should == [2007, 45, 1]
|
93
|
+
end
|
94
|
+
|
95
|
+
it "#jd_to_ld should convert a Julian day into a number of days since italian calendar reform day" do
|
96
|
+
Date.jd_to_ld(2299161).should == 1
|
97
|
+
end
|
98
|
+
|
99
|
+
it "#jd_to_mjd should convert a Julian day into a modified julian date" do
|
100
|
+
Date.jd_to_mjd(2400002).should == 1
|
101
|
+
end
|
102
|
+
|
103
|
+
it "#jd_to_ordinal should convert a Julian day number into an ordinal date" do
|
104
|
+
Date.jd_to_ordinal(2454156).should == [2007, 55]
|
105
|
+
Date.jd_to_ordinal(2454156, 1).should == [2007, 55]
|
106
|
+
end
|
107
|
+
|
108
|
+
it "#jd_to_wday should be able to convert a Julian day number into a week day number" do
|
109
|
+
Date.jd_to_wday(2454482).should == 3
|
110
|
+
end
|
111
|
+
|
112
|
+
it "#julian? should return whether the first argument is less than the second" do
|
113
|
+
Date.julian?(1, 1).should == false
|
114
|
+
Date.julian?(1, 2).should == true
|
115
|
+
end
|
116
|
+
|
117
|
+
it "#os? should be the same as julian?" do
|
118
|
+
Date.os?(1, 2).should == Date.julian?(1, 2)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "#julian leap? should determine whether a year is a leap year in the julian calendar" do
|
122
|
+
Date.julian_leap?(1900).should == true
|
123
|
+
Date.julian_leap?(1901).should == false
|
124
|
+
Date.julian_leap?(1899).should == false
|
125
|
+
Date.julian_leap?(1904).should == true
|
126
|
+
Date.julian_leap?(1896).should == true
|
127
|
+
Date.julian_leap?(1999).should == false
|
128
|
+
Date.julian_leap?(2001).should == false
|
129
|
+
Date.julian_leap?(2000).should == true
|
130
|
+
end
|
131
|
+
|
132
|
+
it "#ld_to_jd should convert a number of days since italian calendar reform day to a julian date" do
|
133
|
+
Date.ld_to_jd(1).should == 2299161
|
134
|
+
end
|
135
|
+
|
136
|
+
it "#mjd_to_jd should convert a modified julian date into a julian date" do
|
137
|
+
Date.mjd_to_jd(1).should == 2400002
|
138
|
+
end
|
139
|
+
|
140
|
+
it "#ordinal_to_jd should convert an ordinal date (year-day) to a Julian day number" do
|
141
|
+
Date.ordinal_to_jd(2007, 55).should == 2454156
|
142
|
+
end
|
143
|
+
|
144
|
+
it "#time_to_day_fraction should convert time parts to a fraction" do
|
145
|
+
Date.time_to_day_fraction(12, 0, 0).should == 0.5
|
146
|
+
end
|
147
|
+
|
148
|
+
it "#valid_civil? should return corresponding julian date if valid and nil if not" do
|
149
|
+
Date.valid_civil?(2007, 2, 24).should == 2454156
|
150
|
+
Date.valid_civil?(2007, 2, 24, 1).should == 2454156
|
151
|
+
Date.valid_civil?(2007, 2, 30, 1).should == nil
|
152
|
+
end
|
153
|
+
|
154
|
+
it "#valid_date? should be the same as valid_civil?" do
|
155
|
+
Date.valid_date?(2007, 2, 24, 1).should == Date.valid_civil?(2007, 2, 24, 1)
|
156
|
+
end
|
157
|
+
|
158
|
+
it "#exist? should be the same as valid_date?" do
|
159
|
+
Date.exist?(2007, 2, 24, 1).should == Date.valid_date?(2007, 2, 24, 1)
|
160
|
+
end
|
161
|
+
|
162
|
+
it "#exist3? should be the same as valid_date?" do
|
163
|
+
Date.exist3?(2007, 2, 24, 1).should == Date.valid_date?(2007, 2, 24, 1)
|
164
|
+
end
|
165
|
+
|
166
|
+
it "#valid_commercial? should return corresponding julian date if valid and nil if not" do
|
167
|
+
Date.valid_commercial?(2007, 45, 1).should == 2454410
|
168
|
+
Date.valid_commercial?(2007, 45, 1, 1).should == 2454410
|
169
|
+
Date.valid_commercial?(2007, 54, 1, 1).should == nil
|
170
|
+
end
|
171
|
+
|
172
|
+
it "#existw? should be the same as valid_date?" do
|
173
|
+
Date.existw?(2007, 45, 1, 1).should == Date.valid_commercial?(2007, 45, 1, 1)
|
174
|
+
end
|
175
|
+
|
176
|
+
it "#valid_jd? should return the given julian date" do
|
177
|
+
Date.valid_jd?(2454156).should == 2454156
|
178
|
+
Date.valid_jd?(2454156, 1).should == 2454156
|
179
|
+
end
|
180
|
+
|
181
|
+
it "#exist1? should be the same as valid_jd?" do
|
182
|
+
Date.exist1?(2454156, 1).should == Date.valid_jd?(2454156, 1)
|
183
|
+
end
|
184
|
+
|
185
|
+
it "#valid_ordinal? should return corresponding julian date if valid and nil if not" do
|
186
|
+
Date.valid_ordinal?(2007, 55).should == 2454156
|
187
|
+
Date.valid_ordinal?(2007, 55, 1).should == 2454156
|
188
|
+
Date.valid_ordinal?(2007, 367, 1).should == nil
|
189
|
+
end
|
190
|
+
|
191
|
+
it "#exist2? should be the same as valid_ordinal?" do
|
192
|
+
Date.exist2?(2007, 55, 1).should == Date.valid_ordinal?(2007, 55)
|
193
|
+
end
|
194
|
+
|
195
|
+
it "#valid_time? should return corresponding day fraction if valid and nil if not" do
|
196
|
+
Date.valid_time?(12, 0, 0).should == 0.5
|
197
|
+
Date.valid_time?(25, 0, 0).should == nil
|
198
|
+
Date.valid_time?(12, 61, 0).should == nil
|
199
|
+
Date.valid_time?(12, 0, 61).should == nil
|
200
|
+
end
|
201
|
+
|
202
|
+
it "#zone_to_diff? should return the offset in seconds if a valid time zone, or 0 if not" do
|
203
|
+
Date.zone_to_diff('-0800').should == -28800
|
204
|
+
Date.zone_to_diff('+08:00').should == 28800
|
205
|
+
Date.zone_to_diff('YY').should == 0
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require(File.join(File.dirname(__FILE__), '..', 'compat_spec_helper'))
|
2
|
+
|
3
|
+
describe ThirdBase::CompatInstanceMethods do
|
4
|
+
before do
|
5
|
+
@d = Date.civil(2008, 10, 11)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "#ajd should be the same as jd" do
|
9
|
+
@d.ajd.should == @d.jd
|
10
|
+
end
|
11
|
+
|
12
|
+
it "#amjd should be the astronomical modified julian date" do
|
13
|
+
@d.amjd.should == 54750
|
14
|
+
end
|
15
|
+
|
16
|
+
it "#gregorian, #england, #julian, #italy, #new_start, and #newsg should be the same as self" do
|
17
|
+
@d.gregorian.should == @d
|
18
|
+
@d.england.should == @d
|
19
|
+
@d.julian.should == @d
|
20
|
+
@d.italy.should == @d
|
21
|
+
@d.new_start.should == @d
|
22
|
+
@d.newsg.should == @d
|
23
|
+
@d.new_start(1).should == @d
|
24
|
+
@d.newsg(1).should == @d
|
25
|
+
end
|
26
|
+
|
27
|
+
it "#gregorian? and ns? should be true" do
|
28
|
+
@d.gregorian?.should == true
|
29
|
+
@d.ns?.should == true
|
30
|
+
end
|
31
|
+
|
32
|
+
it "#julian? and os? should be false" do
|
33
|
+
@d.julian?.should == false
|
34
|
+
@d.os?.should == false
|
35
|
+
end
|
36
|
+
|
37
|
+
it "#ld should be the days since italian calendar reform day" do
|
38
|
+
@d.ld.should == 155591
|
39
|
+
end
|
40
|
+
|
41
|
+
it "#mday should be the same as day" do
|
42
|
+
@d.mday.should == @d.day
|
43
|
+
end
|
44
|
+
|
45
|
+
it "#mjd should be the modified julian date" do
|
46
|
+
@d.mjd.should == 54750
|
47
|
+
end
|
48
|
+
|
49
|
+
it "#start and #sg should equal 0" do
|
50
|
+
@d.start.should == 0
|
51
|
+
@d.sg.should == 0
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|