chronos 0.1.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/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
|