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)