mtortonesi/symian

View on GitHub
lib/symian/work_shift.rb

Summary

Maintainability
B
4 hrs
Test Coverage
module Symian
  class WorkShift

    def initialize(type, params={})
      case type
        when :predefined
          # get workshift id
          raise ArgumentError unless params[:id]
          wsid = params[:id]

          # retrieve predefined workshift
          predefined_workshift = WORKSHIFT_TABLE[wsid]
          raise ArgumentError unless predefined_workshift

          # load start_time and end_time from predefined workshift
          @start_time = predefined_workshift[:start_time]
          @end_time   = predefined_workshift[:end_time]
        when :custom
          # load start_time and end_time from parameters
          raise ArgumentError unless params[:start_time] and params[:end_time]
          @start_time = params[:start_time]
          @end_time   = params[:end_time]
        when :all_day_long
          # nothing to do
        else
          raise ArgumentError
      end

      # save work shift type
      @type = type

      unless @type == :all_day_long
        # normalize start_time and end_time by transforming them from
        # instances of (Date)Time class to integers representing the
        # number of seconds elapsed from last midnight UTC
        @start_time = @start_time.utc.seconds_since_midnight.round
        @end_time   = @end_time.utc.seconds_since_midnight.round
      end

      # check if it is an overnight work shift
      @overnight = (@type == :all_day_long ? false : @end_time < @start_time)
    end


    def active_at?(time)
      return true if @type == :all_day_long

      t = time.utc.seconds_since_midnight.round
      if @overnight
        t <= @end_time or t >= @start_time
      else
        @start_time <= t and t <= @end_time
      end
    end


    def secs_to_end_of_shift(time)
      raise 'secs_to_end_of_shift called for unavailable operator' unless active_at?(time)
      return Infinity if @type == :all_day_long

      t = time.utc.seconds_since_midnight.round
      res = if @overnight
        if t <= @end_time
          @end_time - t
        elsif t >= @start_time
          @end_time + 1.day - t
        else
          # TODO: else raise error
          raise 'Weird error in secs_to_end_of_shift!'
        end
      else
        # TODO: raise error if t < @start_time or t > @end_time
        raise 'Weirder error in secs_to_end_of_shift!' if t < @start_time or t > @end_time
        @end_time - t
      end

      # need to convert to integer
      res.round
    end


    def secs_to_begin_of_shift(time)
      raise 'secs_to_begin_of_shift called for available operator' if active_at?(time)

      t = time.utc.seconds_since_midnight.round
      res = if @overnight
        # TODO: raise error if t < @end_time or t > @start_time
        raise 'Weirder error in secs_to_begin_of_shift!' if t < @end_time or t > @start_time
        @start_time - t
      else
        if t <= @start_time
          @start_time - t
        elsif t >= @end_time
          @start_time + 1.day - t
        else
          # TODO: else raise error
          raise 'Weird error in secs_to_begin_of_shift!'
        end
      end

      # need to convert to integer
      res.round
    end


    def duration
      return Infinity if @type == :all_day_long

      res = if @overnight
        1.day.to_i - @start_time + @end_time
      else
        @end_time - @start_time
      end

      # need to convert to integer
      res.round
    end


    # 24x7 work shift
    WORKSHIFT_24x7 = WorkShift.new(:all_day_long)

    # an infinitely large value
    Infinity = 1.0/0.0

    # the predefined work shift table
    WORKSHIFT_TABLE = {
      brt: { # UTC-3, 9AM to 5PM
        start_time: Time.utc(2000, 'Jan', 1, 12, 0, 0),
        end_time:   Time.utc(2000, 'Jan', 1, 20, 0, 0),
      },
      brst: { # UTC-2, 9AM to 5PM
        start_time: Time.utc(2000, 'Jan', 1, 11, 0, 0),
        end_time:   Time.utc(2000, 'Jan', 1, 19, 0, 0),
      },
      est: { # UTC-5, 9AM to 5PM
        start_time: Time.utc(2000, 'Jan', 1, 14, 0, 0),
        end_time:   Time.utc(2000, 'Jan', 1, 22, 0, 0),
      },
      edt: { # UTC-4, 9AM to 5PM
        start_time: Time.utc(2000, 'Jan', 1, 13, 0, 0),
        end_time:   Time.utc(2000, 'Jan', 1, 21, 0, 0),
      },
      cet: { # UTC+1, 9AM to 5PM
        start_time: Time.utc(2000, 'Jan', 1,  8, 0, 0),
        end_time:   Time.utc(2000, 'Jan', 1, 16, 0, 0),
      },
      cest: { # UTC+2, 9AM to 5PM
        start_time: Time.utc(2000, 'Jan', 1,  7, 0, 0),
        end_time:   Time.utc(2000, 'Jan', 1, 15, 0, 0),
      },
      ist: { # UTC+5:30, 9AM to 5PM
        start_time: Time.utc(2000, 'Jan', 1,  3, 30, 0),
        end_time:   Time.utc(2000, 'Jan', 1, 11, 30, 0),
      },
    }
  end
end