module Workpattern # @author Barrie Callender # @!attribute values # @return [Array] each hour of the day # @!attribute hours # @return [Integer] number of hours in the day # @!attribute first_hour # @return [Integer] first working hour in the day # @!attribute first_min # @return [Integer] first working minute in first working hour in the day # @!attribute last_hour # @return [Integer] last working hour in the day # @!attribute last_min # @return [Integer] last working minute in last working hour in the day # @!attribute total # @return [Integer] total number of minutes in the day # # Represents the 24 hours of a day. # # @since 0.2.0 # @todo implement a day with different number of hours in it to support daylight saving # class Day include Workpattern::Utility attr_accessor :values, :hours, :first_hour, :first_min, :last_hour, :last_min, :total # The new Day object can be created as either working or resting. # # @param [Integer] type is working (1) or resting (0) # def initialize(type=1) @hours=24 hour=WORKING_HOUR if type==1 hour=RESTING_HOUR if type==0 @values=Array.new(@hours) {|index| hour } set_attributes end # Creates a duplicate of the current Day instance. # # @return [Day] # def duplicate duplicate_day = Day.new() duplicate_values=Array.new(@values.size) @values.each_index {|index| duplicate_values[index]=@values[index] } duplicate_day.values=duplicate_values duplicate_day.hours = @hours duplicate_day.first_hour=@first_hour duplicate_day.first_min=@first_min duplicate_day.last_hour=@last_hour duplicate_day.last_min=@last_min duplicate_day.total = @total duplicate_day.refresh return duplicate_day end # Recalculates characteristics for this day # def refresh set_attributes end # Sets all minutes in a date range to be working or resting. # # @param [(#hour,#min)] start_time is the start of the range to set # @param [(#hour, #min)] finish_time is the finish of the range to be set # @param [Integer] type is either working (1) or resting (0) # def workpattern(start_time,finish_time,type) if start_time.hour==finish_time.hour @values[start_time.hour]=@values[start_time.hour].wp_workpattern(start_time.min,finish_time.min,type) else test_hour=start_time.hour @values[test_hour]=@values[test_hour].wp_workpattern(start_time.min,59,type) while ((test_hour+1)duration to # time. The duration can be negative in # which case it subtracts from time. # # An addition where there are less working minutes left in # the day than are being added will result in the time # returned being 00:00 on the following day. # # A subtraction where there are less working minutes left in # the day than are being added will result in the time # returned being the previous day with the midnight flag set to true. # # @param [DateTime] time when the calculation starts from # @param [Integer] duration is the number of minutes to add or subtract if it is negative # @param [Boolean] midnight is a flag used in subtraction to pretend the time is actually midnight # @return [DateTime,Integer,Boolean] Calculated time along with any remaining duration and the midnight flag # def calc(time,duration,midnight=false) if (duration<0) return subtract(time,duration, midnight) elsif (duration>0) return add(time,duration) else return time,duration, false end end # Returns true if the given minute is working and false if it is resting # # @param [(#hour, #min)] start is the time in the day to inspect # @return [Boolean] true if the time is working and false if it is resting # def working?(start) return true if minutes(start.hour,start.min,start.hour,start.min)==1 return false end # Returns the difference in working minutes between two times. # # @param [(#hour, #min)] start start time in the range # @param [(#hour, #min)] finish finish time in the range # @return [Integer] number of working minutes # def diff(start,finish) start,finish=finish,start if ((start <=> finish))==1 # calculate to end of hour # if (start.jd==finish.jd) # same day duration=minutes(start.hour,start.min,finish.hour, finish.min) duration -=1 if working?(finish) start=finish else duration=minutes(start.hour,start.min,23, 59) start=start+((23-start.hour)*HOUR) +((60-start.min)*MINUTE) end return duration, start end # Returns the total number of minutes between two times. # # @param [Integer] start_hour first hour in range # @param [Integer] start_min first minute of first hour in range # @param [Integer] finish_hour last hour in range # @param [Integer] finish_min last minute of last hour in range # @return [Integer] minutes between supplied hours and minutes # # @todo can this method and #diff method be combined? # def minutes(start_hour,start_min,finish_hour,finish_min) if (start_hour > finish_hour) || ((finish_hour==start_hour) && (start_min > finish_min)) start_hour,start_min,finish_hour,finish_min=finish_hour,finish_min,start_hour,finish_min end if (start_hour==finish_hour) retval=@values[start_hour].wp_minutes(start_min,finish_min) else retval=@values[start_hour].wp_minutes(start_min,59) while (start_hour+1DateTime or oo:oo # when there is no working minutes in the day. Used by the #subtract method # # @param [DateTime] time day for which the first working time is sought. # @return [DateTime] the first working time of the day # def first_working_minute(time) if @first_hour.nil? return time - (HOUR*time.hour) - (MINUTE*time.min) else time = time - HOUR * (time.hour - @first_hour) time = time - MINUTE * (time.min - @first_min ) return time end end # Handles the subtraction of a duration from a time in the day. # # @param [DateTime] time when the subtraction starts from # @param [Integer] duration is the number of minutes to subtract from the time # @param [Boolean] midnight is a flag used in subtraction to pretend the time is actually midnight # @return [DateTime,Integer,Boolean] Calculated time along with any remaining duration and the midnight flag # def subtract(time,duration,midnight=false) if (time.hour==0 && time.min==0) if midnight duration+=minutes(23,59,23,59) time=time+(HOUR*23)+(MINUTE*59) return calc(time,duration) else return time.prev_day, duration,true end elsif (time.hour==@first_hour && time.min==@first_min) time=time-(HOUR*@first_hour) - (MINUTE*@first_min) return time.prev_day, duration, true elsif (time.min>0) available_minutes=minutes(0,0,time.hour,time.min-1) else available_minutes=minutes(0,0,time.hour-1,59) end if ((duration+available_minutes)<0) # not enough minutes in the day time = midnight_before(time.prev_day) duration = duration + available_minutes return time, duration, true elsif ((duration+available_minutes)==0) duration=0 time=first_working_minute(time) else minutes_this_hour=@values[time.hour].wp_minutes(0,time.min-1) this_hour=time.hour until (duration==0) if (minutes_this_hourduration to the given time # When there are not enough minutes in the day it returns the date # for the start of the following day. # # @param [DateTime] time when the calculation starts from # @param [Integer] duration is the number of minutes to add # @return [DateTime,Integer] Calculated time along with any remaining duration # def add(time,duration) available_minutes=minutes(time.hour,time.min,@hours-1,59) if ((duration-available_minutes)>0) # not enough minutes left in the day result_date= time.next_day - (HOUR*time.hour) - (MINUTE*time.min) duration = duration - available_minutes else total=@values[time.hour].wp_minutes(time.min,59) if (total==duration) # this hour satisfies result_date=time - (MINUTE*time.min) + (MINUTE*@values[time.hour].wp_last) + MINUTE duration = 0 else result_date = time until (duration==0) if (totalDateTime for which the following hour is required. # @return [DateTime] the start of the next hour following the DateTime supplied # def next_hour(start) return start+HOUR-(start.min*MINUTE) end # Returns the number of working minutes left in the current hour # # @param [DateTime] start is the DateTime for which the remaining working minutes # in the hour are required # @return [Integer] number of remaining working minutes # def minutes_left_in_hour(start) return @values[start.hour].wp_diff(start.min,60) end end end