class Workpattern::Week

The representation of a week might not be obvious so I am writing about it here. It will also help me if I ever need to come back to this in the future.

Each day is represented by a binary number where a 1 represents a working minute and a 0 represents a resting minute.

@private

Attributes

days[RW]
finish[RW]
hours_per_day[RW]
start[RW]
total[W]
week_total[W]

Public Class Methods

new(start, finish, type = WORK_TYPE, hours_per_day = HOURS_IN_DAY) click to toggle source
# File lib/workpattern/week.rb, line 14
def initialize(start, finish, type = WORK_TYPE, hours_per_day = HOURS_IN_DAY)
  @hours_per_day = hours_per_day
  @start = Time.gm(start.year, start.month, start.day)
  @finish = Time.gm(finish.year, finish.month, finish.day)
  @days = Array.new(LAST_DAY_OF_WEEK)
  FIRST_DAY_OF_WEEK.upto(LAST_DAY_OF_WEEK) do |i|
    @days[i] = Day.new(hours_per_day, type)
  end
end

Public Instance Methods

<=>(other) click to toggle source
# File lib/workpattern/week.rb, line 24
def <=>(other)
  return -1 if start < other.start
  return 0 if start == other.start
  1
end
calc(a_date, a_duration, a_day = SAME_DAY) click to toggle source
# File lib/workpattern/week.rb, line 58
def calc(a_date, a_duration, a_day = SAME_DAY) 
  if a_duration == 0
    return a_date, a_duration
  elsif a_duration > 0      
    return add(a_date, a_duration)
  else      
    subtract(a_date, a_duration, a_day)
  end       
end
diff(start_date, finish_date) click to toggle source
# File lib/workpattern/week.rb, line 76
def diff(start_date, finish_date)
  if start_date > finish_date
    start_date, finish_date = finish_date, start_date
  end

  if jd(start_date) == jd(finish_date)
    return diff_in_same_day(start_date, finish_date)
  else
    return diff_in_same_weekpattern(start_date, finish_date)
  end
end
duplicate() click to toggle source
# File lib/workpattern/week.rb, line 48
def duplicate
  duplicate_week = Week.new(@start, @finish)
  FIRST_DAY_OF_WEEK.upto(LAST_DAY_OF_WEEK) do |i|
    duplicate_week.days[i] = @days[i].clone
          duplicate_week.days[i].hours_per_day = @days[i].hours_per_day
          duplicate_week.days[i].pattern = @days[i].pattern
  end
  duplicate_week
end
resting?(time) click to toggle source
# File lib/workpattern/week.rb, line 72
def resting?(time)
  @days[time.wday].resting?(time.hour, time.min)
end
total() click to toggle source
# File lib/workpattern/week.rb, line 34
def total
  elapsed_days < 8 ? week_total : range_total
end
week_total() click to toggle source
# File lib/workpattern/week.rb, line 30
def week_total
  elapsed_days > 6 ? full_week_working_minutes : part_week_total_minutes
end
working?(time) click to toggle source
# File lib/workpattern/week.rb, line 68
def working?(time)
  @days[time.wday].working?(time.hour, time.min)
end
workpattern(days, from_time, to_time, type) click to toggle source
# File lib/workpattern/week.rb, line 38
def workpattern(days, from_time, to_time, type)
  DAYNAMES[days].each do |day|
    if type == WORK_TYPE
      @days[day].set_working(from_time, to_time)
    else
      @days[day].set_resting(from_time, to_time)
    end
  end
end

Private Instance Methods

add(a_date, a_duration) click to toggle source
# File lib/workpattern/week.rb, line 135
def add(a_date, a_duration)

  r_date, r_duration = add_to_end_of_day(a_date, a_duration)

  r_date, r_duration = add_to_finish_day r_date, r_duration
  r_date, r_duration = add_full_weeks r_date, r_duration
  r_date, r_duration = add_remaining_days r_date, r_duration
  [r_date, r_duration, false]
end
add_full_weeks(a_date, a_duration) click to toggle source
# File lib/workpattern/week.rb, line 164
def add_full_weeks(a_date, a_duration)

  while (a_duration != 0) && (a_duration >= self.week_total) && ((jd(a_date) + (6*86400)) <= jd(self.finish))
    a_duration -= self.week_total
    a_date += (7*86400)
  end

  [a_date, a_duration]
end
add_remaining_days(a_date, a_duration) click to toggle source
# File lib/workpattern/week.rb, line 174
def add_remaining_days(a_date, a_duration)
  while (a_duration != 0) && (jd(a_date) <= jd(self.finish))
    a_date, a_duration = add_to_end_of_day(a_date,a_duration)
  end
  [a_date, a_duration]
end
add_to_end_of_day(a_date, a_duration) click to toggle source
# File lib/workpattern/week.rb, line 145
def add_to_end_of_day(a_date, a_duration)
  r_date, r_duration, r_day = @days[a_date.wday].calc(a_date,a_duration)

  if r_day == NEXT_DAY
    r_date = start_of_next_day(r_date)

  end

  [r_date, r_duration]
end
add_to_finish_day(a_date, a_duration) click to toggle source
# File lib/workpattern/week.rb, line 156
def add_to_finish_day(a_date, a_duration)
  while ( a_duration != 0) && (a_date.wday != next_day(self.finish).wday) && (jd(a_date) <= jd(self.finish))
    a_date, a_duration = add_to_end_of_day(a_date,a_duration)
  end

  [a_date, a_duration]
end
diff_in_same_day(start_date, finish_date) click to toggle source
# File lib/workpattern/week.rb, line 264
def diff_in_same_day(start_date, finish_date)
  minutes = @days[start_date.wday].working_minutes(start_date, finish_date - MINUTE)
  [minutes, finish_date]
end
diff_in_same_weekpattern(start_date, finish_date) click to toggle source
# File lib/workpattern/week.rb, line 231
def diff_in_same_weekpattern(start_date, finish_date)
  minutes = @days[start_date.wday].working_minutes(start_date, LAST_TIME_IN_DAY)
  run_date = start_of_next_day(start_date)
  while (run_date.wday != start.wday) && (jd(run_date) < jd(finish)) && (jd(run_date) != jd(finish_date))      
    minutes += @days[run_date.wday].working_minutes
          run_date += DAY
  end

  while ((jd(run_date) + (7 * DAY)) < jd(finish_date))  && ((jd(run_date) + (7 * DAY)) < jd(finish))
    minutes += week_total
          run_date += (7 * DAY)
  end

  while (jd(run_date) < jd(finish_date)) && (jd(run_date) <= jd(finish))
    minutes += @days[run_date.wday].working_minutes
          run_date += DAY
  end

  if run_date != finish_date
    
    if (jd(run_date) == jd(finish_date)) && (jd(run_date) <= jd(finish))
      minutes += @days[run_date.wday].working_minutes(run_date, finish_date - MINUTE)
            run_date = finish_date
    elsif (jd(run_date) <= jd(finish)) 
      minutes += @days[run_date.wday].working_minutes
            run_date += DAY
    end
  end

  [minutes, run_date]

end
elapsed_days() click to toggle source
# File lib/workpattern/week.rb, line 90
def elapsed_days
  (finish - start).to_i / DAY + 1
end
full_week_working_minutes() click to toggle source
# File lib/workpattern/week.rb, line 94
def full_week_working_minutes
  minutes_in_day_range FIRST_DAY_OF_WEEK, LAST_DAY_OF_WEEK
end
handle_midnight(a_date, a_duration, a_day) click to toggle source
# File lib/workpattern/week.rb, line 196
def handle_midnight(a_date, a_duration, a_day)
  
  if a_day == PREVIOUS_DAY
    a_date -= DAY
    a_date = Time.gm(a_date.year, a_date.month, a_date.day,LAST_TIME_IN_DAY.hour, LAST_TIME_IN_DAY.min)

    if @days[a_date.wday].working?(a_date.hour, a_date.min)
      a_duration += 1
    end     
  end

  [a_date, a_duration, SAME_DAY]
end
jd(time) click to toggle source
# File lib/workpattern/week.rb, line 277
def jd(time)
  Time.gm(time.year, time.month, time.day)
end
minutes_in_day_range(first, last) click to toggle source
# File lib/workpattern/week.rb, line 131
def minutes_in_day_range(first, last)
  @days[first..last].inject(0) { |sum, day| sum + day.working_minutes }
end
minutes_to_finish_day() click to toggle source
# File lib/workpattern/week.rb, line 127
def minutes_to_finish_day
  minutes_in_day_range(FIRST_DAY_OF_WEEK, finish.wday)
end
minutes_to_first_saturday() click to toggle source
# File lib/workpattern/week.rb, line 123
def minutes_to_first_saturday
  minutes_in_day_range(start.wday, LAST_DAY_OF_WEEK)
end
next_day(time) click to toggle source
# File lib/workpattern/week.rb, line 269
def next_day(time)
  time + DAY
end
no_rollover_minutes() click to toggle source
# File lib/workpattern/week.rb, line 102
def no_rollover_minutes
  minutes_in_day_range(start.wday, finish.wday)
end
part_week_total_minutes() click to toggle source
# File lib/workpattern/week.rb, line 98
def part_week_total_minutes
  start.wday <= finish.wday ? no_rollover_minutes : rollover_minutes
end
prev_day(time) click to toggle source
# File lib/workpattern/week.rb, line 273
def prev_day(time)
  time - DAY
end
range_total() click to toggle source
# File lib/workpattern/week.rb, line 110
def range_total
  total_days = elapsed_days

  sum = minutes_to_first_saturday
  total_days -= (7 - start.wday)

  sum += minutes_to_finish_day
  total_days -= (finish.wday + 1)

  sum += week_total * total_days / 7
  sum
end
rollover_minutes() click to toggle source
# File lib/workpattern/week.rb, line 106
def rollover_minutes
  minutes_to_first_saturday + minutes_to_finish_day
end
start_of_next_day(date) click to toggle source
# File lib/workpattern/week.rb, line 181
def start_of_next_day(date)
  next_day(date) - (HOUR * date.hour) - (MINUTE * date.min)
end
subtract(a_date, a_duration, a_day) click to toggle source
# File lib/workpattern/week.rb, line 210
def subtract(a_date, a_duration, a_day)
  a_date, a_duration, a_day = handle_midnight(a_date, a_duration, a_day)
  a_date, a_duration, a_day = subtract_to_start_of_day(a_date, a_duration, a_day)
  
  while (a_duration != 0) && (a_date.wday != start.wday) && (jd(a_date) > jd(start))  
    a_date, a_duration, a_day = handle_midnight(a_date, a_duration, a_day)
    a_date, a_duration, a_day = subtract_to_start_of_day(a_date, a_duration, a_day)
  end

  while (a_duration != 0) && (a_duration >= week_total) && ((jd(a_date) - (6 * DAY)) >= jd(start))
    a_duration += week_total
    a_date -= 7
  end

  while (a_duration != 0) && (jd(a_date) > jd(start))
    a_date, a_duration, a_day = subtract_to_start_of_day(a_date,a_duration,a_day)
  end

  [a_date, a_duration, a_day]
end
subtract_to_start_of_day(a_date, a_duration, a_day) click to toggle source
# File lib/workpattern/week.rb, line 185
def subtract_to_start_of_day(a_date, a_duration, a_day)
  
  
  a_date, a_duration, a_day = handle_midnight(a_date, a_duration, a_day)

  r_date, r_duration, r_day = @days[a_date.wday].calc(a_date, a_duration)

  [r_date, r_duration, r_day]

end