chronos 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +27 -0
- data/HISTORY.rdoc +4 -0
- data/LICENSE.txt +52 -0
- data/MANIFEST.txt +51 -0
- data/NOTES.rdoc +85 -0
- data/README.rdoc +125 -0
- data/Rakefile +34 -0
- data/TODO.rdoc +63 -0
- data/bench/completebench.rb +24 -0
- data/ext/cchronos/extconf.rb +5 -0
- data/ext/chronos_core/extconf.rb +5 -0
- data/lib/chronos.rb +208 -0
- data/lib/chronos/calendar.rb +16 -0
- data/lib/chronos/calendar/gregorian.rb +94 -0
- data/lib/chronos/data/zones.tab +424 -0
- data/lib/chronos/datetime.rb +299 -0
- data/lib/chronos/datetime/gregorian.rb +698 -0
- data/lib/chronos/duration.rb +141 -0
- data/lib/chronos/duration/gregorian.rb +261 -0
- data/lib/chronos/durationtotext.rb +42 -0
- data/lib/chronos/exceptions.rb +16 -0
- data/lib/chronos/gregorian.rb +27 -0
- data/lib/chronos/interval.rb +132 -0
- data/lib/chronos/interval/gregorian.rb +80 -0
- data/lib/chronos/locale/parsers/de_CH.rb +50 -0
- data/lib/chronos/locale/parsers/en_US.rb +1 -0
- data/lib/chronos/locale/parsers/generic.rb +21 -0
- data/lib/chronos/locale/strings/de_DE.yaml +76 -0
- data/lib/chronos/locale/strings/en_US.yaml +76 -0
- data/lib/chronos/minimalistic.rb +37 -0
- data/lib/chronos/numeric/gregorian.rb +100 -0
- data/lib/chronos/ruby.rb +6 -0
- data/lib/chronos/version.rb +21 -0
- data/lib/chronos/zone.rb +212 -0
- data/rake/initialize.rb +116 -0
- data/rake/lib/assesscode.rb +59 -0
- data/rake/lib/bonesplitter.rb +245 -0
- data/rake/lib/projectclass.rb +69 -0
- data/rake/tasks/copyright.rake +24 -0
- data/rake/tasks/gem.rake +119 -0
- data/rake/tasks/git.rake +40 -0
- data/rake/tasks/loc.rake +33 -0
- data/rake/tasks/manifest.rake +63 -0
- data/rake/tasks/meta.rake +16 -0
- data/rake/tasks/notes.rake +36 -0
- data/rake/tasks/post_load.rake +18 -0
- data/rake/tasks/rdoc.rake +73 -0
- data/rake/tasks/rubyforge.rake +67 -0
- data/rake/tasks/spec.rake +55 -0
- data/spec/bacon_helper.rb +43 -0
- data/spec/lib/chronos/datetime/gregorian_spec.rb +314 -0
- data/spec/lib/chronos/datetime_spec.rb +219 -0
- data/spec/lib/chronos_spec.rb +91 -0
- metadata +111 -0
@@ -0,0 +1,141 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2007-2008 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
require 'chronos'
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
module Chronos
|
14
|
+
|
15
|
+
# An immutable class representing the amount of intervening time in a time interval.
|
16
|
+
# A duration has no start- nor end-point.
|
17
|
+
# Also see Interval
|
18
|
+
class Duration
|
19
|
+
FormatToS = "%dd %dps (%s)".freeze
|
20
|
+
FormatInspect = "#<%s:0x%08x %dd %dps (%s)>".freeze
|
21
|
+
|
22
|
+
def self.with(parts)
|
23
|
+
new(
|
24
|
+
parts[:days] || parts[:d] || 0,
|
25
|
+
parts[:picoseconds] || parts[:ps] || 0,
|
26
|
+
parts[:language]
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.import(duration)
|
31
|
+
duration.to_duration
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_reader :days
|
35
|
+
attr_reader :picoseconds
|
36
|
+
attr_reader :language
|
37
|
+
|
38
|
+
def self.days(n, language=nil)
|
39
|
+
new(n, 0, language)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.seconds(n, language=nil)
|
43
|
+
new(0, n*PS_IN_SECOND, language)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Create a Duration of given picoseconds length
|
47
|
+
def initialize(days, picoseconds, language=nil)
|
48
|
+
@days, @picoseconds = *picoseconds.divmod(PS_IN_DAY)
|
49
|
+
@days += days
|
50
|
+
@language = Chronos.language(language)
|
51
|
+
end
|
52
|
+
|
53
|
+
def +@
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
def -@
|
58
|
+
self.class.new(*(self.to_a(true).map { |e| -e }+[@language]))
|
59
|
+
end
|
60
|
+
|
61
|
+
def +(other)
|
62
|
+
self.class.new(*(self.to_a(true).zip(other.to_a).map { |a,b| a+b }+[@language]))
|
63
|
+
end
|
64
|
+
|
65
|
+
def -(other)
|
66
|
+
self.class.new(*(self.to_a(true).zip(other.to_a).map { |a,b| a-b }+[@language]))
|
67
|
+
end
|
68
|
+
|
69
|
+
def *(other)
|
70
|
+
self.class.new(*(self.to_a(true).map { |e| e*other }+[@language]))
|
71
|
+
end
|
72
|
+
|
73
|
+
def /(other)
|
74
|
+
self.class.new(*(self.to_a(true).map { |e| e/other }+[@language]))
|
75
|
+
end
|
76
|
+
|
77
|
+
def div(other)
|
78
|
+
self.class.new(*(self.to_a(true).map { |e| e.div(other) }+[@language]))
|
79
|
+
end
|
80
|
+
|
81
|
+
def quo(other)
|
82
|
+
self.class.new(*(self.to_a(true).map { |e| e.quo(other) }+[@language]))
|
83
|
+
end
|
84
|
+
|
85
|
+
def %(other)
|
86
|
+
raise "Not yet implemented"
|
87
|
+
# Duration % Duration -> modulo per unit, e.g. duration % 1.hour -> ps % (1*PS_IN_HOUR)
|
88
|
+
# Duration % Symbol -> shortcut, e.g. duration % :hour -> duration % 1.hour
|
89
|
+
end
|
90
|
+
|
91
|
+
# Split the duration into durations with each only one of the atomic units set
|
92
|
+
def split
|
93
|
+
lang = [@language]
|
94
|
+
klass = self.class
|
95
|
+
ary = to_a(true)
|
96
|
+
(0...(ary.size)).zip(ary).map { |i,e|
|
97
|
+
init = Array.new(ary.size, 0)+lang
|
98
|
+
init[i] = e
|
99
|
+
klass.new(*init)
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
# An array with the atomic units and the language of this Duration
|
104
|
+
def to_a(exclude_language=nil)
|
105
|
+
exclude_language ? [@days, @picoseconds] : [@days, @picoseconds, @language]
|
106
|
+
end
|
107
|
+
|
108
|
+
def to_hash
|
109
|
+
{
|
110
|
+
:days => @days,
|
111
|
+
:d => @days,
|
112
|
+
:ps => @picoseconds,
|
113
|
+
:picoseconds => @picoseconds,
|
114
|
+
:language => @language,
|
115
|
+
}
|
116
|
+
end
|
117
|
+
|
118
|
+
def to_duration
|
119
|
+
self
|
120
|
+
end
|
121
|
+
|
122
|
+
def values_at(*keys)
|
123
|
+
to_hash.values_at(*keys)
|
124
|
+
end
|
125
|
+
|
126
|
+
def durations_at(*keys)
|
127
|
+
keys.zip(values_at(*keys)).map { |key, value|
|
128
|
+
self.class.with(key => value, :language => @language)
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
# return a readable representation
|
133
|
+
def to_s
|
134
|
+
sprintf(self.class::FormatToS, *self)
|
135
|
+
end
|
136
|
+
|
137
|
+
def inspect # :nodoc:
|
138
|
+
sprintf(self.class::FormatInspect, self.class, object_id<<1, *self)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2007-2008 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
require 'chronos'
|
10
|
+
require 'chronos/calendar/gregorian'
|
11
|
+
require 'chronos/datetime/gregorian'
|
12
|
+
require 'chronos/duration/gregorian'
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
module Chronos
|
17
|
+
class Duration
|
18
|
+
|
19
|
+
class Gregorian < ::Chronos::Duration
|
20
|
+
FormatToS = "%dps %d months (%s)".freeze
|
21
|
+
FormatInspect = "#<%s:0x%08x %dps %d months (%s)>".freeze
|
22
|
+
ShortTime = ["%d".freeze, "%02d".freeze, "%02d".freeze, "%02d".freeze].freeze
|
23
|
+
|
24
|
+
# if you want to estimate minimum seconds in months
|
25
|
+
MinSecondsInMonths = [
|
26
|
+
0,
|
27
|
+
2419200,
|
28
|
+
5097600,
|
29
|
+
7689600,
|
30
|
+
10368000,
|
31
|
+
12960000,
|
32
|
+
15638400,
|
33
|
+
18316800,
|
34
|
+
20908800,
|
35
|
+
23587200,
|
36
|
+
26179200,
|
37
|
+
28857600,
|
38
|
+
31536000
|
39
|
+
]
|
40
|
+
# if you want to estimate maximum seconds in months
|
41
|
+
MaxSecondsInMonths = [
|
42
|
+
0, # 0 days
|
43
|
+
2678401, # 31 days + leapsecond (june/december)
|
44
|
+
5356800, # 62 days (july, august)
|
45
|
+
7948801, # 92 days + leapsecond (june, july, august)
|
46
|
+
10627201, # 123 days + leapsecond
|
47
|
+
13219201,
|
48
|
+
15897601,
|
49
|
+
18489602,
|
50
|
+
21168002,
|
51
|
+
23760002,
|
52
|
+
26438402,
|
53
|
+
29116802,
|
54
|
+
31622402,
|
55
|
+
]
|
56
|
+
After = Hash.new { |h,(a,b)| raise ArgumentError, "Can't get #{a} after #{b}" }.merge({
|
57
|
+
[:picoseconds, :nanoseconds] => 1_000,
|
58
|
+
[:picoseconds, :microseconds] => 1_000_000,
|
59
|
+
[:picoseconds, :milliseconds] => 1_000_000_000,
|
60
|
+
[:picoseconds, :seconds] => 1_000_000_000_000,
|
61
|
+
[:picoseconds, :minutes] => 60_000_000_000_000,
|
62
|
+
[:picoseconds, :hours] => 3_600_000_000_000_000,
|
63
|
+
[:picoseconds, :days] => 86_400_000_000_000_000,
|
64
|
+
[:picoseconds, :weeks] => 345_600_000_000_000_000,
|
65
|
+
[:nanoseconds, :microseconds] => 1_000,
|
66
|
+
[:nanoseconds, :milliseconds] => 1_000_000,
|
67
|
+
[:nanoseconds, :seconds] => 1_000_000_000,
|
68
|
+
[:nanoseconds, :minutes] => 60_000_000_000,
|
69
|
+
[:nanoseconds, :hours] => 3_600_000_000_000,
|
70
|
+
[:nanoseconds, :days] => 86_400_000_000_000,
|
71
|
+
[:nanoseconds, :weeks] => 345_600_000_000_000,
|
72
|
+
[:microseconds, :milliseconds] => 1_000,
|
73
|
+
[:microseconds, :seconds] => 1_000_000,
|
74
|
+
[:microseconds, :minutes] => 60_000_000,
|
75
|
+
[:microseconds, :hours] => 3_600_000_000,
|
76
|
+
[:microseconds, :days] => 86_400_000_000,
|
77
|
+
[:microseconds, :weeks] => 345_600_000_000,
|
78
|
+
[:milliseconds, :seconds] => 1_000,
|
79
|
+
[:milliseconds, :minutes] => 60_000,
|
80
|
+
[:milliseconds, :hours] => 3_600_000,
|
81
|
+
[:milliseconds, :days] => 86_400_000,
|
82
|
+
[:milliseconds, :weeks] => 345_600_000,
|
83
|
+
[:seconds, :minutes] => 60,
|
84
|
+
[:seconds, :hours] => 3_600,
|
85
|
+
[:seconds, :days] => 86_400,
|
86
|
+
[:seconds, :weeks] => 345_600,
|
87
|
+
[:minutes, :hours] => 60,
|
88
|
+
[:minutes, :days] => 1_440,
|
89
|
+
[:minutes, :weeks] => 10_080,
|
90
|
+
[:hours, :days] => 24,
|
91
|
+
[:hours, :weeks] => 168,
|
92
|
+
[:days, :weeks] => 7,
|
93
|
+
[:months, :years] => 12,
|
94
|
+
})
|
95
|
+
|
96
|
+
def self.with(parts)
|
97
|
+
y,m,w,d,h,min,s,ms,us,ns,ps = *Hash.new(0).merge(parts).values_at(
|
98
|
+
:years,
|
99
|
+
:months,
|
100
|
+
:weeks,
|
101
|
+
:days,
|
102
|
+
:hours,
|
103
|
+
:minutes,
|
104
|
+
:seconds,
|
105
|
+
:milliseconds,
|
106
|
+
:microseconds,
|
107
|
+
:nanoseconds,
|
108
|
+
:picoseconds
|
109
|
+
)
|
110
|
+
seconds = s+min*60+h*3600+d*86400+w*604800
|
111
|
+
ps += seconds*1_000_000_000_000+ms*1_000_000_000+us*1_000_000+ns*1_000
|
112
|
+
days, ps = ps.divmod(PS_IN_DAY)
|
113
|
+
months = m+y*12
|
114
|
+
new(months, days, ps, parts[:language])
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.import(duration)
|
118
|
+
duration.respond_to?(:to_gregorian_duration) ? duration.to_gregorian_duration : super
|
119
|
+
end
|
120
|
+
|
121
|
+
# seconds+months
|
122
|
+
def initialize(months, days, picoseconds, language=nil)
|
123
|
+
super(days, picoseconds, language)
|
124
|
+
@months = months
|
125
|
+
end
|
126
|
+
|
127
|
+
def picoseconds(after=nil)
|
128
|
+
after ? picoseconds%After[[:picoseconds, after]] : @picoseconds+@days*PS_IN_DAY
|
129
|
+
end
|
130
|
+
|
131
|
+
def nanoseconds(after=nil)
|
132
|
+
after ? nanoseconds%After[[:nanoseconds, after]] : picoseconds.quo(PS_IN_NANOSECOND)
|
133
|
+
end
|
134
|
+
|
135
|
+
def microseconds(after=nil)
|
136
|
+
after ? microseconds%After[[:microseconds, after]] : picoseconds.quo(PS_IN_MICROSECOND)
|
137
|
+
end
|
138
|
+
|
139
|
+
def milliseconds(after=nil)
|
140
|
+
after ? milliseconds%After[[:milliseconds, after]] : picoseconds.quo(PS_IN_MILLISECOND)
|
141
|
+
end
|
142
|
+
|
143
|
+
def seconds(after=nil)
|
144
|
+
after ? seconds%After[[:seconds, after]] : picoseconds.quo(PS_IN_SECOND)
|
145
|
+
end
|
146
|
+
|
147
|
+
def minutes(after=nil)
|
148
|
+
after ? minutes%After[[:minutes, after]] : picoseconds.quo(PS_IN_MINUTE)
|
149
|
+
end
|
150
|
+
|
151
|
+
def hours(after=nil)
|
152
|
+
after ? hours%After[[:hours, after]] : picoseconds.quo(PS_IN_HOUR)
|
153
|
+
end
|
154
|
+
|
155
|
+
def days(after=nil)
|
156
|
+
after ? days%After[[:days, after]] : picoseconds.quo(PS_IN_DAY)
|
157
|
+
end
|
158
|
+
|
159
|
+
def weeks
|
160
|
+
picoseconds.quo(PS_IN_WEEK)
|
161
|
+
end
|
162
|
+
|
163
|
+
def months(after=nil)
|
164
|
+
after ? days%After[[:days, after]] : @months
|
165
|
+
end
|
166
|
+
|
167
|
+
def years
|
168
|
+
@months.quo(12)
|
169
|
+
end
|
170
|
+
|
171
|
+
def decades
|
172
|
+
@months.quo(144)
|
173
|
+
end
|
174
|
+
|
175
|
+
def centuries
|
176
|
+
@months.quo(1200)
|
177
|
+
end
|
178
|
+
|
179
|
+
def to_duration
|
180
|
+
Duration.new(@days, @picoseconds, @language)
|
181
|
+
end
|
182
|
+
|
183
|
+
def to_gregorian_duration
|
184
|
+
self
|
185
|
+
end
|
186
|
+
|
187
|
+
def to_a(exclude_language=nil)
|
188
|
+
exclude_language ? [@picoseconds, @months] : [@picoseconds, @months, @language]
|
189
|
+
end
|
190
|
+
|
191
|
+
def to_hash
|
192
|
+
{
|
193
|
+
:years => years,
|
194
|
+
:months => @months,
|
195
|
+
:weeks => weeks,
|
196
|
+
:days => days,
|
197
|
+
:hours => hours,
|
198
|
+
:minutes => minutes,
|
199
|
+
:seconds => seconds,
|
200
|
+
:milliseconds => milliseconds,
|
201
|
+
:microseconds => microseconds,
|
202
|
+
:nanoseconds => nanoseconds,
|
203
|
+
:picoseconds => @picoseconds,
|
204
|
+
:language => @language,
|
205
|
+
}
|
206
|
+
end
|
207
|
+
|
208
|
+
# Return a String in form of DDD:HH:MM:SS.fff
|
209
|
+
# fraction_digits:: How many digits to display after the ., defaults to 0
|
210
|
+
# num_elements:: How many elements to display at least
|
211
|
+
# * 1 is only SS
|
212
|
+
# * 2 is MM:SS
|
213
|
+
# * 3 is HH:MM:SS
|
214
|
+
# * 4 is DDD:HH:SS
|
215
|
+
def short_time(fraction_digits=nil, num_elements=nil)
|
216
|
+
elements = [
|
217
|
+
days.floor,
|
218
|
+
hours(:days).floor,
|
219
|
+
minutes(:hours).floor,
|
220
|
+
seconds(:minutes)
|
221
|
+
]
|
222
|
+
elements.shift while (elements.size > num_elements && elements.first.first.zero?)
|
223
|
+
display = ShortTime[-elements.size..-1]
|
224
|
+
display[-1] = "%#{fraction_digits+3}.#{fraction_digits}f" if (fraction_digits && fraction_digits > 0)
|
225
|
+
sprintf(display.join(":"), *elements)
|
226
|
+
end
|
227
|
+
|
228
|
+
# return a readable representation
|
229
|
+
def to_s(drop=:all_zeros, language=nil)
|
230
|
+
elements1 = @months.zero? ? [] : [
|
231
|
+
[years.floor, :year],
|
232
|
+
[days(:weeks).floor, :month],
|
233
|
+
]
|
234
|
+
elements2 = @picoseconds.zero? ? [] : [
|
235
|
+
[weeks.floor, :week],
|
236
|
+
[days(:weeks).floor, :day],
|
237
|
+
[hours(:days).floor, :hour],
|
238
|
+
[minutes(:hours).floor, :minute],
|
239
|
+
[seconds(:minutes).floor, :second],
|
240
|
+
[milliseconds(:seconds).floor, :millisecond],
|
241
|
+
[microseconds(:milliseconds).floor, :microseconds],
|
242
|
+
[nanoseconds(:microseconds).floor, :nanosecond],
|
243
|
+
[picoseconds(:nanoseconds).floor, :picosecond],
|
244
|
+
]
|
245
|
+
elements = elements1+elements2
|
246
|
+
case drop
|
247
|
+
when :all_zeros
|
248
|
+
elements.reject! { |count,*rest| count.zero? }
|
249
|
+
when :leading_zeros
|
250
|
+
elements.shift while elements.first.first.zero?
|
251
|
+
when nil
|
252
|
+
else
|
253
|
+
raise ArgumentError, "Unknown directive, #{drop.inspect}"
|
254
|
+
end
|
255
|
+
elements.empty? ? "0" : elements.map { |count, unit|
|
256
|
+
"#{count} #{Chronos.string(language ? Chronos.language(language) : @language, unit, count)}"
|
257
|
+
}.join(", ")
|
258
|
+
end
|
259
|
+
end # Datetime::Gregorian
|
260
|
+
end # Datetime
|
261
|
+
end # Chronos
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2007-2008 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
require 'chronos'
|
10
|
+
require 'enumerator'
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
class Duration
|
15
|
+
ToTextEn = {
|
16
|
+
-(1/0.0) => proc { |s| sprintf "%d weeks ago", -s.div(604800) },
|
17
|
+
-1209600 => proc { |s| "one week ago" },
|
18
|
+
-604800 => proc { |s| sprintf "%d days ago", -s.div(86500) },
|
19
|
+
-172800 => proc { |s| "yesterday" },
|
20
|
+
0 => proc { |s| "today" },
|
21
|
+
86400 => proc { |s| "tomorrow" },
|
22
|
+
172800 => proc { |s| sprintf "in %d days", (s/86400).ceil },
|
23
|
+
604800 => proc { |s| "in one week" },
|
24
|
+
1209600 => proc { |s| sprintf "in %d weeks", (s/604800).ceil },
|
25
|
+
}.to_a.sort
|
26
|
+
|
27
|
+
def initialize(seconds, months=0)
|
28
|
+
@seconds = seconds
|
29
|
+
@months = months
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_text
|
33
|
+
ToTextEn.each_cons(2) { |(v,t), (v2,t2)|
|
34
|
+
return t.call(@seconds) if @seconds >= v && @seconds < v2
|
35
|
+
}
|
36
|
+
ToTextEn.last.last.call(@seconds)
|
37
|
+
end
|
38
|
+
alias to_s to_text
|
39
|
+
end
|
40
|
+
|
41
|
+
time = Time.today-2*7*24*3600
|
42
|
+
30.times { puts "#{time.strftime('%Y-%m-%d')}: #{Duration.new(time-Time.today)}"; time+=86400 }
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2007-2008 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
require 'chronos'
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
module Chronos
|
14
|
+
class NoDatePart < StandardError; end
|
15
|
+
class NoTimePart < StandardError; end
|
16
|
+
end
|