require 'date' require 'time' # # Extra methods for integer to calculate periods in seconds. # class Integer # @return [Integer] def seconds; self; end # @return [Integer] n minutes of seconds def minutes; self*60; end # @return [Integer] n hours of seconds def hours; self*3600; end # @return [Integer] n days of seconds def days; self*86400; end # @return [Integer] n weeks of seconds def weeks; self*604800; end alias_method :day, :days alias_method :hour, :hours alias_method :minute, :minutes alias_method :week, :weeks end # # Extra methods for Time. # class Time # # Method to work out when something is due, with different classes of hours. # # @param [Integer] n The number of hours in the future # @param [String] type One of wallclock, working, or daytime. # # @return [Time] # def in_x_hours(n, type="wallclock") raise ArgumentError, "n must be numeric" unless n.is_a?(Numeric) raise ArgumentError, "type must be a string" unless type.is_a?(String) t = self.dup # # Do this in seconds rather than hours # n = (n*3600).to_i test = case type when "working" "working_hours?" when "daytime" "daytime_hours?" else "wallclock_hours?" end step = 3600 # # Work out how much time to subtract now # while n >= 0 # # If we're currently OK, and we won't be OK after the next step (or # vice-versa) decrease step size, and try again # if (t.__send__(test) != (t+step).__send__(test)) # # Unless we're on the smallest step, try a smaller one. # unless step == 1 step /= 60 else n -= step if t.__send__(test) t += step # # Set the step size back to an hour # step = 3600 end next end # # Decrease the time by the step size if we're currently OK. # n -= step if t.__send__(test) t += step end # # Substract any overshoot. # t += n if n < 0 t end # Returns the bank_holidays array, or an empty array if bank_holidays hasn't # been set. # # def bank_holidays if defined? Mauve::Server and Mauve::Server.instance @bank_holidays = Mauve::Server.instance.bank_holidays else @bank_holidays ||= [] end @bank_holidays end # Returns an array of ranges of working hours # # def working_hours if defined? Mauve::Configuration and Mauve::Configuration.current Mauve::Configuration.current.working_hours else # From our SLA: # "Working hours" means 9.30am to 5.30pm, Monday to Friday, excluding # English bank holidays. [9.5...17.5] end end def dead_zone if defined? Mauve::Configuration and Mauve::Configuration.current Mauve::Configuration.current.dead_zone else [3.0...7.0] end end def daytime_hours if defined? Mauve::Configuration and Mauve::Configuration.current Mauve::Configuration.current.daytime_hours else [8.0...22.0] end end def fractional_hour self.hour + self.min.to_f/60 + self.sec.to_f/3600 end # This relies on bank_holidays being set. # def bank_holiday? today = Date.new(self.year, self.month, self.day) self.bank_holidays.any?{|bh| bh == today} end # Test to see if we're in working hours. The working day is from 8.30am until # 17:00 # # @return [Boolean] def working_hours? (1..5).include?(self.wday) and x_in_list_of_y(fractional_hour, self.working_hours) and !self.bank_holiday? end # Test to see if it is currently daytime. The daytime day is 14 hours long # # @return [Boolean] def daytime_hours? x_in_list_of_y(fractional_hour, self.daytime_hours) end # We're always in wallclock hours # # @return [true] def wallclock_hours? true end # Test to see if we're in the DEAD ZONE! This is from 3 - 6am every day. # # @return [Boolean] def dead_zone? x_in_list_of_y(fractional_hour, self.dead_zone) end # Format the time as a string, relative to +now+ # # @param [Time] now The time we're using as a base # @raise [ArgumentError] if +now+ is not a Time # @return [String] # def to_s_relative(now = Time.now) # # Make sure now is the correct class # now = now if now.is_a?(DateTime) raise ArgumentError, "now must be a Time" unless now.is_a?(Time) diff = (now.to_f - self.to_f).round.to_i.abs n = nil if diff < 120 n = nil elsif diff < 3600 n = diff/60.0 unit = "minute" elsif diff < 172800 n = diff/3600.0 unit = "hour" elsif diff < 5184000 n = diff/86400.0 unit = "day" else n = diff/2592000.0 unit = "month" end unless n.nil? n = n.round.to_i unit += "s" if n != 1 end # The FUTURE if self > now return "shortly" if n.nil? "in #{n} #{unit}" else return "just now" if n.nil? "#{n} #{unit}"+" ago" end end def to_s_human _now = Time.now if _now.strftime("%F") == self.strftime("%F") self.strftime("%R today") # Tomorrow is in 24 hours elsif (_now + 86400).strftime("%F") == self.strftime("%F") self.strftime("%R tomorrow") # Yesterday is in 24 ago elsif (_now - 86400).strftime("%F") == self.strftime("%F") self.strftime("%R yesterday") # Next week starts in 6 days. elsif self > _now and self < (_now + 86400 * 6) self.strftime("%R on %A") else self.strftime("%R on %a %d %b %Y") end end # Checks to see if x is contained in y # # @param [Array] y Array to search for +x+ # @param [Object] x # @return [Boolean] def x_in_list_of_y(x,y) y.any? do |range| if range.respond_to?("include?") range.include?(x) else range == x end end end end #module Mauve # class Time < Time # # def to_s # self.iso8601 # end # # def to_mauvetime # self # end # # end #end