chronos 0.1.0

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