third_base 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|