module IceCube::TimeUtil
Constants
- CLOCK_VALUES
- DAYS
- ICAL_DAYS
- MONTHS
Public Class Methods
Source
# File lib/ice_cube/time_util.rb, line 137 def self.beginning_of_date(date, reference = Time.now) build_in_zone([date.year, date.month, date.day, 0, 0, 0], reference) end
Get the beginning of a date
Source
# File lib/ice_cube/time_util.rb, line 31 def self.build_in_zone(args, reference) if reference.respond_to?(:time_zone) reference.time_zone.local(*args) elsif reference.utc? Time.utc(*args) elsif reference.zone Time.local(*args) else Time.new(*args << reference.utc_offset) end end
Source
# File lib/ice_cube/time_util.rb, line 218 def self.day_of_month(value, date) if value.to_i > 0 [value, days_in_month(date)].min else [1 + days_in_month(date) + value, 1].max end end
Get a day of the month in the month of a given time without overflowing into the next month. Accepts days from positive (start of month forward) or negative (from end of month)
Source
# File lib/ice_cube/time_util.rb, line 197 def self.days_in_month(time) date = Date.new(time.year, time.month, 1) ((date >> 1) - date).to_i end
Get the days in the month for +time
Source
# File lib/ice_cube/time_util.rb, line 239 def self.days_in_n_months(time, month_distance) date = Date.new(time.year, time.month, time.day) ((date >> month_distance) - date).to_i end
The number of days in n months
Source
# File lib/ice_cube/time_util.rb, line 233 def self.days_in_n_years(time, year_distance) date = Date.new(time.year, time.month, time.day) ((date >> year_distance * 12) - date).to_i end
Number of days to n years
Source
# File lib/ice_cube/time_util.rb, line 203 def self.days_in_next_month(time) date = Date.new(time.year, time.month, 1) >> 1 ((date >> 1) - date).to_i end
Get the days in the following month for +time
Source
# File lib/ice_cube/time_util.rb, line 227 def self.days_in_year(time) date = Date.new(time.year, 1, 1) ((date >> 12) - date).to_i end
Number of days in a year
Source
# File lib/ice_cube/time_util.rb, line 210 def self.days_to_next_month(time) date = Date.new(time.year, time.month, time.day) ((date >> 1) - date).to_i end
Count the number of days to the same day of the next month without overflowing shorter months
Source
# File lib/ice_cube/time_util.rb, line 102 def self.deserialize_time(time_or_hash) case time_or_hash when Time, Date time_or_hash when DateTime Time.local(time.year, time.month, time.day, time.hour, time.min, time.sec) when Hash hash = FlexibleHash.new(time_or_hash) hash[:time].in_time_zone(hash[:zone]) when String Time.parse(time_or_hash) end end
Deserialize a time serialized with serialize_time
or in ISO8601 string format
Source
# File lib/ice_cube/time_util.rb, line 244 def self.dst_change(time) one_hour_ago = time - ONE_HOUR if time.dst? ^ one_hour_ago.dst? (time.utc_offset - one_hour_ago.utc_offset) / ONE_HOUR end end
Source
# File lib/ice_cube/time_util.rb, line 142 def self.end_of_date(date, reference = Time.now) build_in_zone([date.year, date.month, date.day, 23, 59, 59], reference) end
Get the end of a date
Source
# File lib/ice_cube/time_util.rb, line 77 def self.ensure_date(date) case date when Date then date else Date.new(date.year, date.month, date.day) end end
Ensure that this is either nil, or a date
Source
# File lib/ice_cube/time_util.rb, line 58 def self.ensure_time(time, reference = nil, date_eod = false) case time when DateTime warn "IceCube: DateTime support is deprecated (please use Time) at: #{caller(3..3).first}" Time.local(time.year, time.month, time.day, time.hour, time.min, time.sec) when Date if date_eod end_of_date(time, reference) elsif reference build_in_zone([time.year, time.month, time.day], reference) else time.to_time end else time end end
Ensure that this is either nil, or a time
Source
# File lib/ice_cube/time_util.rb, line 120 def self.hash(time) [time, time.utc_offset, time.zone].hash end
Get a more precise equality for time objects Ruby provides a Time#hash method, but it fails to account for UTC offset (so the current date may be different) or DST rules (so the hour may be wrong for different schedule occurrences)
Source
# File lib/ice_cube/time_util.rb, line 181 def self.ical_day_to_symbol(str) day = ICAL_DAYS[str] raise ArgumentError, "Invalid day: #{str}" if day.nil? day end
Source
# File lib/ice_cube/time_util.rb, line 43 def self.match_zone(input_time, reference) return unless (time = ensure_time(input_time, reference)) time = if reference.respond_to? :time_zone time.in_time_zone(reference.time_zone) elsif reference.utc? time.getgm elsif reference.zone time.getlocal else time.getlocal(reference.utc_offset) end (Date === input_time) ? beginning_of_date(time, reference) : time end
Source
# File lib/ice_cube/time_util.rb, line 176 def self.normalize_wday(wday, week_start) (wday - sym_to_wday(week_start)) % 7 end
Convert weekday from base sunday to the schedule’s week start.
Source
# File lib/ice_cube/time_util.rb, line 27 def self.now(reference = Time.now) match_zone(Time.at(Time.now.to_i), reference) end
Provides a Time.now without the usec, in the reference zone or utc offset
Source
# File lib/ice_cube/time_util.rb, line 129 def self.restore_deserialized_offset(time, orig_offset_str) return time if time.respond_to?(:time_zone) || time.getlocal(orig_offset_str).utc_offset == time.utc_offset warn "IceCube: parsed Time from nonlocal TZ. Use ActiveSupport to fix DST at: #{caller(1..1).first}" time.localtime(orig_offset_str) end
Check the deserialized time offset string against actual local time offset to try and preserve the original offset for plain Ruby Time. If the offset is the same as local we can assume the same original zone and keep it. If it was serialized with a different offset than local TZ it will lose the zone and not support DST.
Source
# File lib/ice_cube/time_util.rb, line 86 def self.serialize_time(time) case time when Time, Date if time.respond_to?(:time_zone) {time: time.utc, zone: time.time_zone.name} else time end when DateTime Time.local(time.year, time.month, time.day, time.hour, time.min, time.sec) else raise ArgumentError, "cannot serialize #{time.inspect}, expected a Time" end end
Serialize a time appropriate for storing
Source
# File lib/ice_cube/time_util.rb, line 256 def self.subsec(time) if time.respond_to?(:subsec) time.subsec elsif time.respond_to?(:sec_fraction) time.sec_fraction else 0.0 end end
Handle discrepancies between various time types
-
Time has subsec
-
DateTime does not
-
ActiveSupport::TimeWithZone can wrap either type, depending on version or if ‘parse` or `now`/`local` was used to build it.
Source
# File lib/ice_cube/time_util.rb, line 147 def self.sym_to_month(sym) MONTHS.fetch(sym) do |k| MONTHS.values.detect { |i| i.to_s == k.to_s } or raise ArgumentError, "Expecting Integer or Symbol value for month. " \ "No such month: #{k.inspect}" end end
Convert a symbol to a numeric month
Source
# File lib/ice_cube/time_util.rb, line 157 def self.sym_to_wday(sym) DAYS.fetch(sym) do |k| DAYS.values.detect { |i| i.to_s == k.to_s } or raise ArgumentError, "Expecting Integer or Symbol value for weekday. " \ "No such weekday: #{k.inspect}" end end
Convert a symbol to a wday number
Source
# File lib/ice_cube/time_util.rb, line 167 def self.wday_to_sym(wday) return wday if DAYS.key? wday DAYS.invert.fetch(wday) do |i| raise ArgumentError, "Expecting Integer value for weekday. " \ "No such wday number: #{i.inspect}" end end
Convert wday number to day symbol
Source
# File lib/ice_cube/time_util.rb, line 189 def self.which_occurrence_in_month(time, wday) first_occurrence = ((7 - Time.utc(time.year, time.month, 1).wday) + time.wday) % 7 + 1 this_weekday_in_month_count = ((days_in_month(time) - first_occurrence + 1) / 7.0).ceil nth_occurrence_of_weekday = (time.mday - first_occurrence) / 7 + 1 [nth_occurrence_of_weekday, this_weekday_in_month_count] end
Return the count of the number of times wday appears in the month, and which of those time falls on