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.
Files changed (54) hide show
  1. data/CHANGELOG.rdoc +27 -0
  2. data/HISTORY.rdoc +4 -0
  3. data/LICENSE.txt +52 -0
  4. data/MANIFEST.txt +51 -0
  5. data/NOTES.rdoc +85 -0
  6. data/README.rdoc +125 -0
  7. data/Rakefile +34 -0
  8. data/TODO.rdoc +63 -0
  9. data/bench/completebench.rb +24 -0
  10. data/ext/cchronos/extconf.rb +5 -0
  11. data/ext/chronos_core/extconf.rb +5 -0
  12. data/lib/chronos.rb +208 -0
  13. data/lib/chronos/calendar.rb +16 -0
  14. data/lib/chronos/calendar/gregorian.rb +94 -0
  15. data/lib/chronos/data/zones.tab +424 -0
  16. data/lib/chronos/datetime.rb +299 -0
  17. data/lib/chronos/datetime/gregorian.rb +698 -0
  18. data/lib/chronos/duration.rb +141 -0
  19. data/lib/chronos/duration/gregorian.rb +261 -0
  20. data/lib/chronos/durationtotext.rb +42 -0
  21. data/lib/chronos/exceptions.rb +16 -0
  22. data/lib/chronos/gregorian.rb +27 -0
  23. data/lib/chronos/interval.rb +132 -0
  24. data/lib/chronos/interval/gregorian.rb +80 -0
  25. data/lib/chronos/locale/parsers/de_CH.rb +50 -0
  26. data/lib/chronos/locale/parsers/en_US.rb +1 -0
  27. data/lib/chronos/locale/parsers/generic.rb +21 -0
  28. data/lib/chronos/locale/strings/de_DE.yaml +76 -0
  29. data/lib/chronos/locale/strings/en_US.yaml +76 -0
  30. data/lib/chronos/minimalistic.rb +37 -0
  31. data/lib/chronos/numeric/gregorian.rb +100 -0
  32. data/lib/chronos/ruby.rb +6 -0
  33. data/lib/chronos/version.rb +21 -0
  34. data/lib/chronos/zone.rb +212 -0
  35. data/rake/initialize.rb +116 -0
  36. data/rake/lib/assesscode.rb +59 -0
  37. data/rake/lib/bonesplitter.rb +245 -0
  38. data/rake/lib/projectclass.rb +69 -0
  39. data/rake/tasks/copyright.rake +24 -0
  40. data/rake/tasks/gem.rake +119 -0
  41. data/rake/tasks/git.rake +40 -0
  42. data/rake/tasks/loc.rake +33 -0
  43. data/rake/tasks/manifest.rake +63 -0
  44. data/rake/tasks/meta.rake +16 -0
  45. data/rake/tasks/notes.rake +36 -0
  46. data/rake/tasks/post_load.rake +18 -0
  47. data/rake/tasks/rdoc.rake +73 -0
  48. data/rake/tasks/rubyforge.rake +67 -0
  49. data/rake/tasks/spec.rake +55 -0
  50. data/spec/bacon_helper.rb +43 -0
  51. data/spec/lib/chronos/datetime/gregorian_spec.rb +314 -0
  52. data/spec/lib/chronos/datetime_spec.rb +219 -0
  53. data/spec/lib/chronos_spec.rb +91 -0
  54. 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