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

Summary

Can represent dates and times in gregorian notation, provides various methods for different parts like days, daynames, week, month, monthname, year, iterating etc.

Synopsis

  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")

Methods

+   -   at   at   change_zone   civil   commercial   commercial_year   current   date_components   day   day_name   day_of_month   day_of_week   day_of_year   days_in_month   days_since   format   fraction   hour   inspect   iso_8601   leap_year?   leap_year?   minute   month   month_name   ordinal   previous   ps_components   second   succeeding   to_s   usec   week   weeks   year  

Constants

ISO_8601_Datetime = "%04d-%02d-%02dT%02d:%02d:%02d%s%02d:%02d".freeze
ISO_8601_Date = "%04d-%02d-%02d".freeze
ISO_8601_Time = "%02d:%02d:%02d%s%02d:%02d".freeze
Inspect = "#<%s %s (%p, %p)>".freeze
DAYS_IN_MONTH1 = [0,31,28,31,30,31,30,31,31,30,31,30,31].freeze
DAYS_IN_MONTH2 = [0,31,29,31,30,31,30,31,31,30,31,30,31].freeze
DAYS_UNTIL_MONTH1 = [0,31,59,90,120,151,181,212,243,273,304,334,365].freeze
DAYS_UNTIL_MONTH2 = [0,31,60,91,121,152,182,213,244,274,305,335,366].freeze
DAY_OF_WEEK = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6, }.freeze   symbol => index (reverse map for succ/current/previous)

Public Class methods

create a datetime with time part only from hour, minute, second, fraction of second (alternatively you can use a float as second) for timezone/language append a .in(timezone, language) or set a global (see Chronos::Datetime)

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 108
108:       def self.at(hour, minute=0, second=0, fraction=0.0, timezone=nil, language=nil)
109:         timezone = Chronos.timezone(timezone)
110:         new(nil, ps_components(hour, minute, second, fraction, nil, timezone.offset), timezone, language)
111:       end

create a datetime with date part only from year, month and day_of_month for timezone/language append a .in(timezone, language) or set a global (see Chronos::Datetime)

[Source]

    # File lib/chronos/datetime/gregorian.rb, line 71
71:       def self.civil(year, month, day_of_month, hour=nil, minute=nil, second=nil, timezone=nil, language=nil)
72:         ps = nil
73:         if hour || minute || second || timezone then
74:           timezone = Chronos.timezone(timezone)
75:           ps       = ps_components(hour, minute, second, nil, nil, timezone.offset)
76:         end
77:         new(date_components(year, month, nil, nil, day_of_month, nil), ps, timezone, language)
78:       end

see Datetime#format for timezone/language append a .in(timezone, language) or set a global (see Chronos::Datetime)

[Source]

    # File lib/chronos/datetime/gregorian.rb, line 83
83:       def self.commercial(year, week, day_of_week, hour=nil, minute=nil, second=nil, timezone=nil, language=nil)
84:         ps = nil
85:         if hour || minute || second || timezone then
86:           timezone = Chronos.timezone(timezone)
87:           ps       = ps_components(hour, minute, second, nil, nil, timezone.offset)
88:         end
89:         new(date_components(year, nil, week, nil, nil, day_of_week), ps, timezone, language)
90:       end

Get a day_number from various date components. If at least one date component is set, a day_number will be generated. The default for year is the current year, the default for month, week, dayofyear, dayofmonth and dayofweek is 1. day_of_month_mode has 3 possible values:

  • :restrict:: This mode will raise if day_of_month is invalid (default)
  • :reduce:: This mode will reduce the day_of_month to its maximum in case it it exceeds the maximum
  • :overflow:: This mode will increase the month + year until it becomes valid

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 179
179:       def self.date_components(year, month, week, dayofyear, dayofmonth, dayofweek, day_of_month_mode=:restrict)
180:         return nil unless (year || month || week || dayofyear || dayofmonth || dayofweek)
181:         day_number = nil
182: 
183:         year ||= Time.now.year
184: 
185:         # year-month-day_of_month
186:         if (month || dayofmonth) then
187:           month      ||= 1
188:           dayofmonth ||= 1
189:           # calculate how many days passed until this year
190:           leap  = year.leap_year?
191:           raise ArgumentError, "Invalid month (#{year}-#{month}-#{day_of_month})" if month < 1 or month > 12
192:           maxdayofmonth = (leap ? DAYS_IN_MONTH2 : DAYS_IN_MONTH1)[month]
193:           if dayofmonth > maxdayofmonth then
194:             case day_of_month_mode
195:               when :restrict
196:                 raise ArgumentError, "Invalid day of month (#{year}-#{month}-#{dayofmonth})"
197:               when :reduce
198:                 dayofmonth = maxdayofmonth
199:               when :overflow
200:                 raise "Not yet implemented (day_of_month_mode = :overflow)"
201:             end
202:           end
203:           doy   = (leap ? DAYS_UNTIL_MONTH2 : DAYS_UNTIL_MONTH1)[month-1]+dayofmonth
204:           day_number = days_since(year)+doy
205: 
206:         # year-week-day_of_week
207:         elsif (week || dayofweek) then
208:           week      ||= 1
209:           dayofweek ||= 1
210:           fdy         = days_since(year)+1
211:           fwd         = (fdy+4)%7
212:           off         = (10-fwd)%7-3
213:           day_number  = fdy+off+(week-1)*7+dayofweek
214:         
215:         # year-day_of_year
216:         else
217:           dayofyear ||= 1
218:           day_number   = days_since(year)+dayofyear
219:         end
220:         day_number
221:       end

returns the number of days in a given month for a given year

[Source]

    # File lib/chronos/datetime/gregorian.rb, line 57
57:       def self.days_in_month(month, year=nil)
58:         (year.leap_year? ? DAYS_IN_MONTH2 : DAYS_IN_MONTH1).at(month)
59:       end

returns the number of days since origin TODO: check with negative years TODO: use integer arithmetic only instead (divmod + test for zero)

[Source]

    # File lib/chronos/datetime/gregorian.rb, line 64
64:       def self.days_since(year)
65:         year*365+(year/4.0).ceil-(year/100.0).ceil+(year/400.0).ceil
66:       end

parses an ISO 8601 string this can be either date, time or date and time date parts must be fully qualified (year+month+day or year+day_of_year or year+week+day_of_week) this is in here too to be consistent with Datetime#to_s, for other parsers see Chronos::Parse

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 119
119:       def self.iso_8601(string, language=nil)
120:         day_number = nil
121:         ps_number  = nil
122:         zone       = nil
123: 
124:         # date & time
125:         if string.include?('T') then
126:           case string
127:             #       (year          )   (month )   (day    )     hour       minute     second fraction      timezone
128:             when /\A(-?\d\d|-?\d{4})(?:-?(\d\d)(?:-?(\d\d))?)?T(\d\d)(?::?(\d\d)(?::?(\d\d(?:\.\d+)?)?)?)?(Z|[-+]\d\d:\d\d)?\z/
129:               year       = $1.to_i
130:               month      = $2.to_i
131:               day        = $3.to_i
132:               zone       = Chronos.timezone($7)
133:               ps_number  = ps_components($4.to_i, $5.to_i, $6.include?('.') ? $6.to_f : $6.to_i, nil, nil, zone.offset)
134:               day_number = date_components(year, month, nil, nil, day, nil)
135:             when /\A(-?\d\d|-?\d{4})(?:-?W(\d\d)(?:-?(\d))?)?T(\d\d)(?::?(\d\d)(?::?(\d\d(?:\.\d+)?)?)?)?(Z|[-+]\d\d:\d\d)?\z/
136:               year       = $1.to_i
137:               week       = $2.to_i
138:               day        = $3.to_i
139:               zone       = Chronos.timezone($7)
140:               ps_number  = ps_components($4.to_i, $5.to_i, $6.include?('.') ? $6.to_f : $6.to_i, nil, nil, zone.offset)
141:               day_number = date_components(year, nil, week, nil, nil, day)
142:             when /\A(-?\d\d|-?\d{4})(?:-?(\d{3}))?T(\d\d)(?::?(\d\d)(?::?(\d\d(?:\.\d+)?)?)?)?(Z|[-+]\d\d:\d\d)?\z/
143:               year       = $1.to_i
144:               day        = $2.to_i
145:               zone       = Chronos.timezone($6)
146:               ps_number  = ps_components($3.to_i, $4.to_i, $5.include?('.') ? $5.to_f : $5.to_i, nil, zone.offset)
147:               day_number = date_components(year, nil, nil, day, nil, nil)
148:           end
149:         # date | time
150:         else
151:           case string
152:             when //
153:               date_components()
154:           end
155:         end
156: 
157:         new(day_number, ps_number, zone, language)
158:       end

[Source]

    # File lib/chronos/datetime/gregorian.rb, line 52
52:       def self.leap_year?(year)
53:         year.leap_year?
54:       end

create a datetime with date part only from year and day_of_year for timezone/language append a .in(timezone, language) or set a global (see Chronos::Datetime)

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 95
 95:       def self.ordinal(year, day_of_year, hour=nil, minute=nil, second=nil, timezone=nil, language=nil)
 96:         ps = nil
 97:         if hour || minute || second || timezone then
 98:           timezone = Chronos.timezone(timezone)
 99:           ps       = ps_components(hour, minute, second, nil, nil, timezone.offset)
100:         end
101:         new(date_components(year, nil, nil, day_of_year, nil, nil), ps, timezone, language)
102:       end

convert hours, minutes, seconds and fraction to picoseconds required by ::new

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 161
161:       def self.ps_components(hour, minute, second, fraction=nil, ps=nil, offset=nil)
162:         (
163:           (hour||0)*3600+
164:           (minute||0)*60+
165:           (second||0)+
166:           (fraction||0)-(offset||0)
167:         )*PS_IN_SECOND+
168:         (ps||0)
169:       end

Public Instance methods

You can add a Duration

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 225
225:       def +(duration)
226:         duration        = duration.class == Chronos::Duration::Gregorian ? duration : Chronos::Duration::Gregorian.import(duration)
227:         year, month     = (year()*12+(month()-1)+duration.months).divmod(12)
228:         over, ps_number = (@ps_number+duration.picoseconds).divmod(Chronos::PS_IN_DAY)
229:         day_number      = Chronos::Datetime::Gregorian.date_components(year, month+1, nil, nil, day_of_month(), nil, :reduce)+over
230:         Chronos::Datetime::Gregorian.new(day_number, ps_number, @timezone, @language)
231:       end

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 233
233:       def -(duration_or_datetime)
234:         klass = duration_or_datetime.class
235:         if klass == Chronos::Duration::Gregorian then
236:           self+(-duration_or_datetime)
237:         elsif klass == Chronos::Datetime::Gregorian then
238:           raise "not yet implemented"
239:         elsif duration_or_datetime.respond_to?(:to_duration) then
240:           self+(-Chronos::Duration::Gregorian.import(duration_or_datetime))
241:         else
242:           raise "not yet implemented"
243:         end
244:       end

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

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 247
247:       def at(hour, minute=0, second=0, fraction=0.0)
248:         overflow, second = *(hour*3600+minute*60+second+fraction-@timezone.offset).divmod(86400)
249:         self.class.new(
250:           @day_number+overflow,
251:           second*1_000_000_000_000,
252:           @timezone,
253:           @language
254:         )
255:       end

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

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 258
258:       def change_zone(timezone=nil, language=nil)
259:         timezone ||= @timezone
260:         timezone = Zone[timezone] unless timezone.kind_of?(Zone)
261:         Datetime.new(@day_number, @ps_number, timezone, language)
262:       end

the gregorian commercial year - always starts with a monday, always ends with a sunday, has either exactly 52 or 53 weeks.

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 315
315:       def commercial_year
316:         if week == 1 && day_of_year > 14
317:           year+1
318:         elsif week > 51 && day_of_year < 14 then
319:           year-1
320:         else
321:           year
322:         end
323:       end

similar to Datetime#succ returns a new date with the given unit altered as wished Datetime#current tries to not modify any of the other parameters, i.e. no overflows are passed down

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 522
522:       def current(unit, at=0)
523:         case unit
524:           when :second
525:             ps_number = (@ps_number-(at*PS_IN_SECOND).to_i)
526:             Datetime.new(@day_number, ps_number, fraction, @timezone, @language)
527:           when :minute
528:             ps_number = (@ps_number-(minute*PS_IN_MINUTE)+(at*PS_IN_MINUTE).floor)
529:             Datetime.new(@day_number, ps_number, @timezone, @language)
530:           when :hour
531:             ps_number = (@ps_number-(hour*PS_IN_HOUR)+(at*PS_IN_HOUR).floor)
532:             Datetime.new(@day_number, ps_number, @timezone, @language)
533:           when :day
534:             raise ArgumentError, "Does not make sense"
535:           when :monday,:tuesday,:wednesday,:thursday,:friday,:saturday,:sunday
536:             begin
537:               Datetime.new(@day_number-(@day_number+4)%7+DAY_OF_WEEK[unit], @ps_number, @timezone, @language)
538:             rescue
539:               raise NoDatePart unless @day_number
540:               raise
541:             end
542:           when :week
543:             year = year().to_f
544:             leaps = (year/4.0).ceil-(year/100.0).ceil+(year/400.0).ceil
545:             fdy = year*365+leaps+1 # first day of year
546:             fwd = (fdy+4)%7 # first day of years weekday
547:             off = (10-fwd)%7-3   # calculate offset of the first week
548:             Datetime.new(fdy+off+at*7+day_of_week(), @ps_number, @timezone, @language)
549:           when :month
550:             month  = at.floor
551:             year   = year().to_f
552:             leap   = year.leap_year?
553:             raise ArgumentError, "Invalid day of month (#{year}-#{month}-#{day_of_month})" if day_of_month > (leap ? DAYS_IN_MONTH2 : DAYS_IN_MONTH1)[month]
554:             leaps  = (year/4.0).ceil-(year/100.0).ceil+(year/400.0).ceil
555:             doy    = (leap ? DAYS_UNTIL_MONTH2 : DAYS_UNTIL_MONTH1)[month]+day_of_month
556:             Datetime.new(year*365+leaps+doy, @ps_number, @timezone, @language)
557:           when :year
558:             month = month()
559:             year  = at.floor.to_f
560:             leap  = year.leap_year?
561:             raise ArgumentError, "Invalid day of month (#{year}-#{month}-#{day_of_month})" if day_of_month > (leap ? DAYS_IN_MONTH2 : DAYS_IN_MONTH1)[month]
562:             leaps  = (year/4.0).ceil-(year/100.0).ceil+(year/400.0).ceil
563:             doy    = (leap ? DAYS_UNTIL_MONTH2 : DAYS_UNTIL_MONTH1)[month-1]+day_of_month
564:             Datetime.new(year*365+leaps+doy, @ps_number, @timezone, @language)
565:         end
566:       end
day()

Alias for day_of_month

the dayname in the given language or the Datetime-instances default language

  Datetime.civil(2000,1,1).day_name # => "Saturday"

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 344
344:       def day_name(language=nil)
345:         language ||= @language
346:         begin
347:           Chronos.string(language ? Chronos.language(language) : @language, :dayname, (@day_number+@overflow+4)%7)
348:         rescue
349:           raise NoDatePart unless @day_number
350:           raise
351:         end
352:       end

this dates day of month (if it has a date part)

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 390
390:       def day_of_month
391:         @day_of_month ||= month_and_day_of_month[1]
392:       end

the day of week of this date. 0: monday, 6: sunday 7 days, 2000-01-01 beeing a 5 (saturday) the additional parameter can be used to shift the monday to that number

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 333
333:       def day_of_week(monday=0)
334:         begin
335:           (@day_number+@overflow+4+monday)%7
336:         rescue
337:           raise NoDatePart unless @day_number
338:           raise
339:         end
340:       end

the gregorian day of year (1-366)

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 326
326:       def day_of_year
327:         @day_of_year ||= year_and_day_of_year[1]
328:       end

format(string, language, timezone) format datetime, similar to strftime. Format strings can contain:

  %a: monthname, can be formatted like %s in sprintf
  %b: dayname, can be formatted like %s in sprintf
  %y: year, 4 digits (0000..9999)
      %-2y: last 2 digits
      %2y:  first 2 digits
  %m: month of year, 1..31, can be formatted as %d in sprintf
  %d: day of month, 1..31, can be formatted as %d in sprintf
  %j: day of year, 1..366, can be formatted as %d in sprintf
  %k: day of week, 0=monday, 6=sunday
      %+k: 1..7 (changes range from 0..6 to 1..7)
      %2k: 0=saturday, 2=monday, 6=friday
      %+2k: 1=saturday, 3=monday, 7=friday
  %w: week of year (iso 8601) 1..53, can be formatted as %d in sprintf

  %H: hour of day, 0..23, can be formatted as %d in sprintf
  %I: hour of day, 1..12, can be formatted as %d in sprintf
  %M: minute of hour 0..59, can be formatted as %d in sprintf
  %S: second of minute, 0..59, can be formatted as %d in sprintf
  %O: offset in format ±HHMM
  %Z: timezone
  %P: meridian indicator (AM/PM)

  %%: Literal % character

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 598
598:       def format(string=nil, language=nil)
599:         unless string
600:           string = if @day_number.nil? then
601:             ISO_8601_Time
602:           elsif @ps_number.nil? then
603:             ISO_8601_Date
604:           else
605:             ISO_8601_Datetime
606:           end
607:         end
608: 
609:         string.gsub(/%(%|\{[^}]\}|.*?[A-Za-z])/) { |m|
610:           case m[-1,1]
611:             when '{'
612:               call,*args = *m[2..-2].split(",")
613:               call       = c.to_sym
614:               args.map! { |arg|
615:                 if arg[0,1] == ":" then
616:                   arg[1..-1].to_sym
617:                 elsif arg =~ /\A\d+\z/ then
618:                   Integer(arg)
619:                 else
620:                   Float(arg)
621:                 end
622:               }
623:   
624:               respond_to?(call)
625:               send(call, *args)
626:             when 'a'
627:               "#{m[0..-2]}s"%month_name
628:             when 'b'
629:               "#{m[0..-2]}s"%day_name
630:             when 'y'
631:               s = "%04d"%year
632:               if m.length > 2 then
633:                 o = m[1..-2].to_i
634:                 o > 0 ? s[0,o] : s[o..-1]
635:               else
636:                 s
637:               end
638:             when 'm'
639:               "#{m[0..-2]}d"%month
640:             when 'd'
641:               "#{m[0..-2]}d"%day_of_month
642:             when 'j'
643:               "#{m[0..-2]}d"%day_of_year
644:             when 'k'
645:               dow = day_of_week
646:               dow = (dow+m[-2,1].to_i)%7 if (m[-2,1] =~ /\d/)
647:               dow += 1 if (m[1,1] == "+")
648:               dow
649:             when 'w'
650:               "#{m[0..-2]}d"%week
651:   
652:             when 'H'
653:               "#{m[0..-2]}d"%hour
654:             when 'I'
655:               "#{m[0..-2]}d"%(hour%12+1)
656:             when 'M'
657:               "#{m[0..-2]}d"%minute
658:             when 'S'
659:               "#{m[0..-2]}d"%second
660:             when 'O'
661:               "%s%02d%02d"%[@offset < 0 ? "-" : "+",@offset.div(3600),@offset.div(60)%60]
662:             when 'Z'
663:               "FIXME"
664:             when 'P'
665:               hour <= 12 ? "AM" : "PM"
666:             when '%'
667:               "%"
668:           end
669:         }
670:       end

the absolute fraction of a second returned as a rational if Rational was required, a Float otherwise

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 433
433:       def fraction
434:         begin
435:           @ps_number.modulo(PS_IN_SECOND).quo(PS_IN_SECOND)
436:         rescue => e
437:           raise NoTimePart unless @ps_number
438:           raise
439:         end
440:       end

the hour of the day (0..23, if it has a time part)

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 402
402:       def hour
403:         begin
404:           @hour ||= (@ps_number.div(1_000_000_000_000)+@offset).div(3600)
405:         rescue => e
406:           raise NoTimePart unless @ps_number
407:           raise
408:         end
409:       end

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 688
688:       def inspect
689:         sprintf Inspect,
690:           self.class,
691:           self,
692:           @day_number,
693:           @ps_number
694:         # / sprintf
695:       end

returns whether or not the year of this date is a leap-year

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 304
304:       def leap_year?
305:         year.leap_year?
306:       end

the minute of the hour (0..59, if it has a time part)

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 412
412:       def minute
413:         begin
414:           @minute ||= (@ps_number.div(1_000_000_000_000)+@offset).div(60)%60
415:         rescue => e
416:           raise NoTimePart unless @ps_number
417:           raise
418:         end
419:       end

this dates month (if it has a date part)

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 397
397:       def month
398:         @month ||= month_and_day_of_month[0]
399:       end

the monthname in the given language or the Datetime-instances default language Datetime.civil(2000,1,1).month_name # => "January"

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 356
356:       def month_name(language=nil)
357:         Chronos.string(language ? Chronos.language(language) : @language, :monthname, month-1)
358:       end

see Datetime#next

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 569
569:       def previous(unit, step=1, lower_limit=nil, &block)
570:         succeeding(unit, -step, lower_limit, &block)
571:       end

the minute of the minute (0..59, if it has a time part)

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 422
422:       def second
423:         begin
424:           @second ||= (@ps_number.div(1_000_000_000_000)+@offset)%60
425:         rescue => e
426:           raise NoTimePart unless @ps_number
427:           raise
428:         end
429:       end

will raise if you try to do e.g.: Datetime.civil(2000,3,31).next(:month) since april only has 30 days, so 2000,4,31 is invalid same with Datetime.civil(2004,2,29).next(:year) as in 2004, february has a leap-day, but not so in 2005

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 456
456:       def succeeding(unit, step=1, upper_limit=nil)
457:         if block_given?
458:           date = self
459:           if step > 0 then
460:             while((date = date.succeeding(unit,step)) < upper_limit)
461:               yield(date)
462:             end
463:           elsif step < 0 then
464:             while((date = date.succeeding(unit,step)) < upper_limit)
465:               yield(date)
466:             end
467:           else
468:             raise ArgumentError, "Step may not be 0"
469:           end
470:         else
471:           case unit
472:             when :second
473:               overflow, ps_number = *(@ps_number+step*PS_IN_SECOND).divmod(PS_IN_DAY)
474:               day_number = @day_number ? @day_number + overflow : nil
475:               Datetime.new(day_number, ps_number, @timezone, @language)
476:             when :minute
477:               overflow, ps_number = *(@ps_number+(step*PS_IN_MINUTE)).divmod(PS_IN_DAY)
478:               day_number = @day_number ? @day_number + overflow : nil
479:               Datetime.new(day_number, ps_number, @timezone, @language)
480:             when :hour
481:               overflow, ps_number = *(@ps_number+(step*PS_IN_HOUR)).divmod(PS_IN_DAY)
482:               day_number = @day_number ? @day_number + overflow : nil
483:               Datetime.new(day_number, ps_number, @timezone, @language)
484:             when :day
485:               day_number = @day_number + step.floor
486:               Datetime.new(day_number, @ps_number, @timezone, @language)
487:             when :monday,:tuesday,:wednesday,:thursday,:friday,:saturday,:sunday
488:               begin
489:                 Datetime.new(@day_number+(DAY_OF_WEEK[unit]-@day_number-5)%7+1+7*(step >= 1 ? step-1 : step).floor, @ps_number, @timezone, @language)
490:               rescue
491:                 raise NoDatePart unless @day_number
492:                 raise
493:               end
494:             when :week
495:               day_number = @day_number + step.floor*7
496:               Datetime.new(day_number, @ps_number, @timezone, @language)
497:             when :month
498:               overflow, month = *(month()-1+step.floor).divmod(12)
499:               year   = (year()+overflow).to_f
500:               leap   = year.leap_year?
501:               raise ArgumentError, "Invalid day of month (#{year}-#{month}-#{day_of_month})" if day_of_month > (leap ? DAYS_IN_MONTH2 : DAYS_IN_MONTH1)[month]
502:               leaps  = (year/4.0).ceil-(year/100.0).ceil+(year/400.0).ceil
503:               doy    = (leap ? DAYS_UNTIL_MONTH2 : DAYS_UNTIL_MONTH1)[month]+day_of_month
504:               Datetime.new(year*365+leaps+doy, @ps_number, @timezone, @language)
505:             when :year
506:               month = month()
507:               year  = (year()+step.floor).to_f
508:               leap  = year.leap_year?
509:               raise ArgumentError, "Invalid day of month (#{year}-#{month}-#{day_of_month})" if day_of_month > (leap ? DAYS_IN_MONTH2 : DAYS_IN_MONTH1)[month]
510:               leaps  = (year/4.0).ceil-(year/100.0).ceil+(year/400.0).ceil
511:               doy    = (leap ? DAYS_UNTIL_MONTH2 : DAYS_UNTIL_MONTH1)[month-1]+day_of_month
512:               Datetime.new(year*365+leaps+doy, @ps_number, @timezone, @language)
513:           end
514:         end
515:       end

prints the datetime as ISO-8601, examples: datetime: 2007-01-31T14:31:25-04:00 date: 2007-01-31 time: 14:31:25-04:00

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 676
676:       def to_s
677:         if @day_number then
678:           if @ps_number then
679:             sprintf ISO_8601_Datetime, year, month, day, hour, minute, second, @offset < 0 ? '-' : '+', *(@offset/60).floor.divmod(60)
680:           else
681:             sprintf ISO_8601_Date, year, month, day
682:           end
683:         else
684:           sprintf ISO_8601_Time, hour, minute, second, @offset < 0 ? '-' : '+', *(@offset/60).floor.divmod(60)
685:         end
686:       end

the microseconds (0..999999, if it has a time part)

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 443
443:       def usec
444:         begin
445:           @ps_number.div(PS_IN_MICROSECOND).modulo(PS_IN_MICROSECOND)
446:         rescue => e
447:           raise NoTimePart unless @ps_number
448:           raise
449:         end
450:       end

ISO 8601 week

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 361
361:       def week
362:         @week ||= begin
363:           doy  = day_of_year       # day of year
364:           fdy  = @day_number+@overflow-doy+1 # first day of year
365:           fwd  = (fdy+4)%7         # calculate weekday of first day in year
366:           if doy <= 3 && doy <= 7-fwd then  # last week of last year
367:             case fwd
368:               when 6: 52
369:               when 5: (year-1).leap_year? ? 53 : 52
370:               when 4: 53
371:               else    1
372:             end
373:           else # calculate week number
374:             off  = (10-fwd)%7-2   # calculate offset of the first week
375:             week = (doy-off).div(7)+1
376:             if week > 52 then
377:               week = (fwd == 3 || (leap_year? && fwd == 2)) ? 53 : 1
378:             end
379:             week
380:           end
381:         end
382:       end

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 384
384:       def weeks
385:         fwd  = (@day_number+@overflow-day_of_year+5)%7         # calculate weekday of first day in year
386:         (fwd == 3 || (leap_year? && fwd == 2)) ? 53 : 52
387:       end

the gregorian year of this date (only limited by memory)

[Source]

     # File lib/chronos/datetime/gregorian.rb, line 309
309:       def year
310:         @year ||= year_and_day_of_year[0]
311:       end

[Validate]