Class Chronos::Datetime
In: lib/chronos/datetime/gregorian.rb
lib/chronos/datetime.rb
Parent: Object

Summary

Datetime represents dates, times and combinations thereof.

Synopsis Example:

  require 'chronos/gregorian'
  date = Datetime.civil(year, month, day)
  datetime = date.at(hour, minute, second)
  datetimezonelanguage = datetime.in("Europe/Zurich", "de-de")
  dtz = Datetime.civil(y, m, d).at(hour, min, sec).in("Europe/Zurich", "de-de")
  datetime = Datetime.ordinal(year, day_of_year).at(0,0).in("UTC+1", "en-us")

Description

A Datetime represents a singular point on a time axis which has its origin on the backdated gregorian date 0000-01-01 (january 1st in the year 0). A Datetime consists of the days and picoseconds since that origin. The range of Datetimes ruby implementation is only limited by your memory, the C implementation can represent any date within +/- 2^63 days around the origin. That means you can have dates before the assumed beginning of this universe which should be enough even for scientific purposes.

Notes

The methods <, <=, ==, >=, > and between? are implemented via Comparable Chronos::Datetime is calendar system agnostic and does NOT provide any calendar specific methods, use the subclasses such as Datetime::Gregorian for those.

Methods

+   -   <=>   at   change_zone   components   date?   datetime?   epoch   export   import   in   inspect   method_missing   new   now   offset   strip_date   strip_time   time?   today  

Included Modules

Comparable

Classes and Modules

Class Chronos::Datetime::Gregorian

Constants

Inspect = "#<%s daynumber=%p picosecondnumber=%p timezone=%p language=%p>".freeze

Attributes

day_number  [R]  the absolute day_number - the internal representation of the date
language  [R]  the language used to output names (weekday-names, month-names)
overflow  [R]  the amount of days the dates representation is shifted (caused by a time- part with an offset that ‘overflows’ into the previous or next day)
ps_number  [R]  the absolute second_number - the internal representation of the time together with fraction
timezone  [R]  the Zone instance used to retrieve offset

Public Class methods

From a hash with components, mainly intended for parsers. Datetime::components accepts :daynumber, :picosecondnumber, :timezone and :language Also see each calendar systems class for what parts they accept, e.g. Chronos::Datetime::Gregorian::components.

[Source]

     # File lib/chronos/datetime.rb, line 128
128:     def self.components(components)
129:       daynumber, ps_number, timezone, language = *components.values_at(:daynumber, :picosecondnumber, :timezone, :language)
130:       raise ArgumentError, "Neither :daynumber nor :picosecondnumber given" unless (daynumber or ps_number)
131:       new(daynumber, ps_number, timezone, language)
132:     end

create a datetime with date and time part from a unix-epoch-stamp for timezone/language append a .in(timezone, language) or set a global (see Chronos::Datetime)

[Source]

     # File lib/chronos/datetime.rb, line 120
120:     def self.epoch(unix_epoch_time, timezone=nil, language=nil)
121:       import(Time.at(unix_epoch_time), timezone, language)
122:     end

Convert a Date, DateTime or Time to Chronos::Datetime object

[Source]

    # File lib/chronos/datetime.rb, line 57
57:     def self.import(obj, timezone=nil, language=nil)
58:       case obj
59:         when ::Chronos::Datetime
60:           if obj.class == self.class then
61:             obj
62:           else
63:             new(obj.day_number, obj.ps_number, timezone||obj.timezone, language||obj.language)
64:           end
65:           
66:         # uses Chronos::Datetime::Gregorian::ordinal and Chronos::Datetime::Gregorian::time's code
67:         when ::Time
68:           time        = obj.utc
69:           year        = time.year
70:           day_of_year = time.yday
71:           leaps       = (year/4.0).ceil-(year/100.0).ceil+(year/400.0).ceil
72:           daynumber   = year*365+leaps+day_of_year
73:           ps_number    = ((time.hour*3600+time.min*60+time.sec)*1_000_000+time.usec)*1_000_000
74:           new(daynumber, ps_number, timezone || time.strftime("%Z"), language)
75: 
76:         # uses Chronos::Datetime::Gregorian::ordinal and Chronos::Datetime::Gregorian::time's code
77:         when ::DateTime
78:           year           = obj.year
79:           day_of_year    = obj.yday
80:           leaps          = (year/4.0).ceil-(year/100.0).ceil+(year/400.0).ceil
81:           daynumber      = year*365+leaps+day_of_year
82:           seconds        = (obj.hour*3600+obj.min*60+obj.sec+(obj.sec_fraction*86400).to_f)
83:           over, seconds  = (seconds-(obj.offset*86400).to_i).divmod(86400)
84:           ps_number       = seconds*1_000_000_000_000
85:           daynumber     += over
86:           new(daynumber, ps_number, timezone || obj.strftime("%Z"), language)
87: 
88:         # uses Chronos::Datetime::Gregorian::ordinal's code
89:         when ::Date # *must* be after ::DateTime as ::DateTime is a child of ::Date and would trigger on this too
90:           year        = obj.year
91:           day_of_year = obj.yday
92:           leaps       = (year/4.0).ceil-(year/100.0).ceil+(year/400.0).ceil
93:           daynumber   = year*365+leaps+day_of_year
94:           new(daynumber, nil, timezone, language)
95:       end
96:     end

Delegate all methods to the current calendary

[Source]

    # File lib/chronos/datetime.rb, line 47
47:     def self.method_missing(*args, &block)
48:       calendar = Chronos.calendar
49:       if calendar && klass = const_get(calendar) then
50:         klass.__send__(*args, &block)
51:       else
52:         super
53:       end
54:     end

Create a new datetime from daynumber, secondnumber, timezone and language

[Source]

     # File lib/chronos/datetime.rb, line 150
150:     def initialize(day, picosecond, timezone=nil, language=nil)
151:       @day_number = day ? day.round : nil
152:       @ps_number  = picosecond ? picosecond.round : nil
153:       @timezone   = Chronos.timezone(timezone)
154:       @language   = Chronos.language(language)
155:       @offset     = (@timezone && @timezone.offset) || 0
156:       if @ps_number then
157:         @overflow = (@ps_number.div(1_000_000_000_000)+@offset).div(86400)
158:       else
159:         @overflow = 0 # overflow is created by time + timezone offset + dst
160:       end
161:     end

create a datetime with date and time part set to the current system time and date

[Source]

     # File lib/chronos/datetime.rb, line 100
100:     def self.now(timezone=nil, language=nil)
101:       import(Time.now, timezone, language)
102:     end

create a datetime with only the date part set to the current system date for timezone/language append a .in(timezone, language) or set a global (see Chronos::Datetime)

[Source]

     # File lib/chronos/datetime.rb, line 107
107:     def self.today(timezone=nil, language=nil)
108:       # uses Chronos::Datetime::Gregorian::ordinal's code
109:       time        = Time.now.utc
110:       year        = time.year
111:       day_of_year = time.yday
112:       leaps       = (year/4.0).ceil-(year/100.0).ceil+(year/400.0).ceil
113:       daynumber   = year*365+leaps+day_of_year
114:       new(daynumber, nil, timezone || time.strftime("%Z"), language)
115:     end

Public Instance methods

You can add a Duration

[Source]

     # File lib/chronos/datetime.rb, line 214
214:     def +(duration)
215:       duration = Chronos::Duration.import(duration)
216:       if @ps_number then
217:         over, ps = (@ps_number+duration.picoseconds).divmod(PS_IN_DAY)
218:       else
219:         over = 0
220:         ps   = nil
221:       end
222:       day_number = @day_number+duration.days.floor+over if @day_number
223:         
224:       self.class.new(
225:         day_number,
226:         ps,
227:         @timezone,
228:         @language
229:       )
230:     end

[Source]

     # File lib/chronos/datetime.rb, line 232
232:     def -(other)
233:       if other.respond_to?(:to_duration) then
234:         self+(-other)
235:       else
236:         Interval.new(self, self.class.import(other))
237:       end
238:     end

compare two datetimes. not allowed if only one of both doesn‘t have no date. if only one of both doesn‘t have time, 0h 0m 0.0s is used as time.

[Source]

     # File lib/chronos/datetime.rb, line 243
243:     def <=>(other)
244:       return nil if @day_number.nil? ^ other.day_number.nil? # either both or none must be nil
245:       [@day_number||0,@ps_number||0] <=> [other.day_number||0, other.ps_number||0]
246:     end

add a/modify the time component to/of a date only datetime

[Source]

     # File lib/chronos/datetime.rb, line 170
170:     def at(hour, minute=0, second=0, fraction=0.0)
171:       self.class.new(
172:         @day_number,
173:         (hour*3600+minute*60+second+fraction)*1_000_000_000_000,
174:         @timezone,
175:         @language
176:       )
177:     end

Change to another timezone, also gives the opportunity to change language

[Source]

     # File lib/chronos/datetime.rb, line 195
195:     def change_zone(timezone=nil, language=nil)
196:       timezone ||= @timezone
197:       timezone = Zone[timezone] unless timezone.kind_of?(Zone)
198:       self.class.new(@day_number, @ps_number, timezone, language)
199:     end

true if this instance has a date part

[Source]

     # File lib/chronos/datetime.rb, line 254
254:     def date?
255:       !!@day_number # we do not expose the internal structure, not that somebody starts relying on it returning the daynumber
256:     end

true if this instance has date and time part

[Source]

     # File lib/chronos/datetime.rb, line 249
249:     def datetime?
250:       @day_number && @ps_number
251:     end

convert to ::Time (core Time class) be aware that due to a lack of possibility to provide the timezone, all results are returned

  • in utc if this Datetime instance has a timezone set
  • in the local timezone if this instance has no timezone set

will raise if the Datetime object is time_only? TODO: make independent of Datetime::Gregorian

[Source]

     # File lib/chronos/datetime.rb, line 270
270:     def export(to_class)
271:       if to_class == Time then
272:         raise TypeError, "Can't export a Datetime without date part to Time" unless date?
273:         ref   = ::Chronos::Datetime::Gregorian.new(@day_number, @ps_number)
274:         items = [ref.year, ref.month, ref.day_of_month]
275:         items.push ref.hour, ref.minute, ref.second, ref.usec*1000000 if @ps_number
276:         if @timezone then
277:           Time.utc(*items)
278:         else
279:           Time.local(*items)
280:         end
281:       elsif to_class == DateTime then
282:       elsif to_class == Date
283:       else
284:         raise ArgumentError, "Can't export to #{to_class}"
285:       end
286:     end

converts the datetime object to given timezone/language keeps the time the same as is, if you want to know the corresponding time for a given other timezone, see change_zone TODO: go over this again, seems wrong

[Source]

     # File lib/chronos/datetime.rb, line 183
183:     def in(timezone=nil, language=nil)
184:       timezone = Chronos.timezone(timezone)
185:       if timezone then
186:         overflow, ps_number = *(@ps_number-timezone.offset*1_000_000_000_000).divmod(86400_000_000_000_000)
187:       else
188:         overflow  = 0
189:         ps_number = @ps_number
190:       end
191:       self.class.new(@day_number+overflow, ps_number, timezone, language)
192:     end

[Source]

     # File lib/chronos/datetime.rb, line 288
288:     def inspect
289:       sprintf Inspect, self.class, @day_number, @ps_number, @timezone, @language
290:     end

[Source]

     # File lib/chronos/datetime.rb, line 163
163:     def offset
164:       @offset_duration ||= begin
165:         Duration::Gregorian.new(@offset*PS_IN_SECOND, 0, @language)
166:       end
167:     end

returns a time-only datetime from this

[Source]

     # File lib/chronos/datetime.rb, line 208
208:     def strip_date
209:       raise TypeError, "This Datetime does not contain a time" unless @ps_number
210:       self.class.new(nil, @ps_number, @timezone, @language)
211:     end

returns a date-only datetime from this

[Source]

     # File lib/chronos/datetime.rb, line 202
202:     def strip_time
203:       raise TypeError, "This Datetime does not contain a date" unless @day_number
204:       self.class.new(@day_number+@overflow, nil, @timezone, @language)
205:     end

true if this instance has a time part

[Source]

     # File lib/chronos/datetime.rb, line 259
259:     def time?
260:       !!@ps_number # we do not expose the internal structure, not that somebody starts relying on it returning the picosecondnumber
261:     end

[Validate]