lib/aixm/schedule/time.rb in aixm-1.2.0 vs lib/aixm/schedule/time.rb in aixm-1.2.1
- old
+ new
@@ -21,20 +21,20 @@
# time.covered_by?(AIXM.time('20:00')..AIXM.time('02:00')) # => true
class Time
include AIXM::Concerns::HashEquality
extend Forwardable
- EVENTS = %i(sunrise sunset).freeze
+ EVENTS = { sunrise: :up, sunset: :down }.freeze
PRECEDENCES = { first: :min, last: :max }.freeze
DATELESS_DATE = ::Date.parse('0000-01-01').freeze
# @api private
attr_accessor :time
# Event or alternative to time
#
- # @return [Symbol, nil] any from {EVENTS}
+ # @return [Symbol, nil] any key from {EVENTS}
attr_reader :event
# Minutes added or subtracted from event
#
# @return [Integer, nil]
@@ -45,10 +45,14 @@
# @return [Symbol, nil] any key of {PRECEDENCES}
attr_reader :precedence
# Parse the given representation of time.
#
+ # @note Unlike its twin from the stdlib, this class differs between
+ # +AIXM.time('00:00')+ (beginning of day) and +AIXM.time('24:00')+
+ # (end of day).
+ #
# @example
# AIXM.time('08:00')
# AIXM.time(:sunrise)
# AIXM.time(:sunrise, plus: 30)
# AIXM.time('08:00', or: :sunrise)
@@ -56,12 +60,12 @@
# AIXM.time('08:00', or: :sunrise, minus: 15)
# AIXM.time('08:00', or: :sunrise, whichever_comes: :last)
#
# @param time_or_event [Time, DateTime, String, Symbol] either time as
# stdlib Time or DateTime, "HH:MM" (implicitly UTC), "HH:MM [+-]00:00",
- # "HH:MM UTC" or event from {EVENTS} as Symbol
- # @param or [Symbol] alternative event from {EVENTS}
+ # "HH:MM UTC" or any key from {EVENTS}
+ # @param or [Symbol] alternative event, any key from {EVENTS}
# @param plus [Integer] minutes added to event
# @param minus [Integer] minutes subtracted from event
# @param whichever_comes [Symbol] any key from {PRECEDENCES}
def initialize(time_or_event, or: nil, plus: 0, minus: 0, whichever_comes: :first)
alternative_event = binding.local_variable_get(:or) # necessary since "or" is a keyword
@@ -125,11 +129,12 @@
# @return [AIXM::Schedule::Date]
def at(hour: nil, min: nil, wrap: false)
return self unless hour || min
min ||= time.min
hour ||= time.hour
- hour = (hour + 1) % 24 if wrap && min < time.min
+ hour = hour + 1 if wrap && min < time.min
+ hour = hour % 24 unless min.zero?
self.class.new("%02d:%02d" % [hour, min])
end
# Resolve event to simple time
#
@@ -142,31 +147,47 @@
# time.resolve(on: AIXM.date('2000-08-01'), at: AIXM.xy(lat: 48.8584, long: 2.2945))
# # => 20:50
#
# @param on [AIXM::Date] defaults to today
# @param xy [AIXM::XY]
+ # @param round [Integer, nil] round up (sunrise) or down (sunset) to the
+ # given minutes or +nil+ in order not to round round
# @return [AIXM::Schedule::Time, self]
- def resolve(on:, xy:)
+ def resolve(on:, xy:, round: nil)
if resolved?
self
else
sun_time = self.class.new(Sun.send(event, on.to_date, xy.lat, xy.long).utc + (delta * 60))
- if time
- self.class.new([sun_time.time, self.time].send(PRECEDENCES.fetch(precedence)))
- else
- sun_time
- end
+ sun_time = self.class.new([sun_time.time, self.time].send(PRECEDENCES.fetch(precedence))) if time
+ sun_time = sun_time.round(EVENTS.fetch(event) => round) if round
+ sun_time
end
end
# Whether this time is resolved and doesn't contain an event (anymore).
#
# @return [Boolean]
def resolved?
!event
end
+ # Round this time up or down.
+ #
+ # @param up [Integer, nil] round up to the next given minutes
+ # @param down [Integer, nil] round down to the next given minutes
+ # @return [AIXM::Schedule::Time, self]
+ def round(up: nil, down: nil)
+ step = up || down || fail(ArgumentError, "either up or down is mandatory")
+ rounded_min = min / step * step
+ if rounded_min == min
+ self
+ else
+ rounded_min = (rounded_min + step) % 60 if up
+ at(min: rounded_min, wrap: !!up)
+ end
+ end
+
# Stdlib Time equivalent using the value of {DATELESS_DATE} to represent a
# time only.
#
# @return [Time]
def to_time
@@ -230,10 +251,10 @@
def set_time(hour, min, offset)
@time = ::Time.new(DATELESS_DATE.year, DATELESS_DATE.month, DATELESS_DATE.day, hour, min, 0, offset || 0).utc
end
def event=(value)
- fail ArgumentError if value && !EVENTS.include?(value)
+ fail ArgumentError if value && !EVENTS.has_key?(value)
@event = value
end
def precedence=(value)
fail ArgumentError if value && !PRECEDENCES.has_key?(value)