Rewrite of business time calculation. Is now working with timezones.

This commit is contained in:
Martin Edenhofer 2013-06-07 15:51:56 +02:00
parent 3e3f268566
commit 9df719b8bf
7 changed files with 580 additions and 297 deletions

View file

@ -557,10 +557,6 @@ class Ticket < ApplicationModel
end end
} }
# get and set calendar settings
if sla_selected
TimeCalculation.config( sla_selected.data, sla_selected.timezone, time )
end
return sla_selected return sla_selected
end end
@ -609,13 +605,13 @@ class Ticket < ApplicationModel
# first response # first response
if sla_selected.first_response_time if sla_selected.first_response_time
self.first_response_escal_date = TimeCalculation.dest_time( created_at, sla_selected.first_response_time ) self.first_response_escal_date = TimeCalculation.dest_time( created_at, sla_selected.first_response_time, sla_selected.data, sla_selected.timezone )
# set ticket escalation # set ticket escalation
self.escalation_time = self._escalation_calculation_higher_time( self.escalation_time, self.first_response_escal_date, self.first_response ) self.escalation_time = self._escalation_calculation_higher_time( self.escalation_time, self.first_response_escal_date, self.first_response )
end end
if self.first_response# && !self.first_response_in_min if self.first_response# && !self.first_response_in_min
self.first_response_in_min = TimeCalculation.business_time_diff( self.created_at, self.first_response ) self.first_response_in_min = TimeCalculation.business_time_diff( self.created_at, self.first_response, sla_selected.data, sla_selected.timezone )
end end
# set sla time # set sla time
if sla_selected.first_response_time && self.first_response_in_min if sla_selected.first_response_time && self.first_response_in_min
@ -629,13 +625,13 @@ class Ticket < ApplicationModel
last_update = self.created_at last_update = self.created_at
end end
if sla_selected.update_time if sla_selected.update_time
self.update_time_escal_date = TimeCalculation.dest_time( last_update, sla_selected.update_time ) self.update_time_escal_date = TimeCalculation.dest_time( last_update, sla_selected.update_time, sla_selected.data, sla_selected.timezone )
# set ticket escalation # set ticket escalation
self.escalation_time = self._escalation_calculation_higher_time( self.escalation_time, self.update_time_escal_date, false ) self.escalation_time = self._escalation_calculation_higher_time( self.escalation_time, self.update_time_escal_date, false )
end end
if self.last_contact_agent if self.last_contact_agent
self.update_time_in_min = TimeCalculation.business_time_diff( self.created_at, self.last_contact_agent ) self.update_time_in_min = TimeCalculation.business_time_diff( self.created_at, self.last_contact_agent, sla_selected.data, sla_selected.timezone )
end end
# set sla time # set sla time
@ -646,13 +642,13 @@ class Ticket < ApplicationModel
# close time # close time
if sla_selected.close_time if sla_selected.close_time
self.close_time_escal_date = TimeCalculation.dest_time( self.created_at, sla_selected.close_time ) self.close_time_escal_date = TimeCalculation.dest_time( self.created_at, sla_selected.close_time, sla_selected.data, sla_selected.timezone )
# set ticket escalation # set ticket escalation
self.escalation_time = self._escalation_calculation_higher_time( self.escalation_time, self.close_time_escal_date, self.close_time ) self.escalation_time = self._escalation_calculation_higher_time( self.escalation_time, self.close_time_escal_date, self.close_time )
end end
if self.close_time# && !self.close_time_in_min if self.close_time# && !self.close_time_in_min
self.close_time_in_min = TimeCalculation.business_time_diff( self.created_at, self.close_time ) self.close_time_in_min = TimeCalculation.business_time_diff( self.created_at, self.close_time, sla_selected.data, sla_selected.timezone )
end end
# set sla time # set sla time
if sla_selected.close_time && self.close_time_in_min if sla_selected.close_time && self.close_time_in_min

View file

@ -1,99 +0,0 @@
module BusinessTime
class BusinessMinutes
def initialize(minutes)
@minutes = minutes
end
def ago
Time.zone ? before(Time.zone.now) : before(Time.now)
end
def from_now
Time.zone ? after(Time.zone.now) : after(Time.now)
end
def after(time)
after_time = Time.roll_forward(time)
# Step through the minutes, skipping over non-business minutes
days = @minutes / 60 / 24
hours = ( @minutes - ( days * 60 * 24 ) ) / 60
minutes = @minutes - ( (days * 60 * 24 ) + (hours * 60) )
if @minutes > 60 * 12
end
local_sec = @minutes * 60
loop = true
while (loop == true) do
a = after_time
if local_sec >= 60 * 60
after_time = after_time + 1.hour
else
after_time = after_time + 1.minute
end
# Ignore minutes before opening and after closing
if (after_time > Time.end_of_workday(after_time))
after_time = after_time + off_minutes
if local_sec < 60 * 60
after_time = after_time - 60
else
after_time = after_time - 60 * 60
end
next
end
# Ignore weekends and holidays
while !Time.workday?(after_time)
after_time = Time.beginning_of_workday(after_time + 1.day)
a = after_time
end
diff = after_time - a
local_sec = local_sec - diff
if local_sec <= 0
loop = false
next
end
end
after_time
end
alias_method :since, :after
def before(time)
before_time = Time.roll_forward(time)
# Step through the hours, skipping over non-business hours
@minutes.times do
before_time = before_time - 1.minute
# Ignore hours before opening and after closing
if (before_time < Time.beginning_of_workday(before_time))
before_time = before_time - off_minutes
end
# Ignore weekends and holidays
while !Time.workday?(before_time)
before_time = before_time - 1.day
end
end
before_time
end
private
def off_minutes
return @gap if @gap
if Time.zone
gap_end = Time.zone.parse(BusinessTime::Config.beginning_of_workday)
gap_begin = (Time.zone.parse(BusinessTime::Config.end_of_workday)-1.day)
else
gap_end = Time.parse(BusinessTime::Config.beginning_of_workday)
gap_begin = (Time.parse(BusinessTime::Config.end_of_workday) - 1.day)
end
@gap = gap_end - gap_begin
end
end
end

View file

@ -1,11 +0,0 @@
# hook into fixnum so we can say things like:
# 5.business_minutes.from_now
# 4.business_minutes.before(some_date_time)
class Fixnum
include BusinessTime
def business_minutes
BusinessMinutes.new(self)
end
alias_method :business_minute, :business_minutes
end

View file

@ -1,130 +0,0 @@
# Add workday and weekday concepts to the Time class
class Time
class << self
# Gives the time at the end of the workday, assuming that this time falls on a
# workday.
# Note: It pretends that this day is a workday whether or not it really is a
# workday.
def end_of_workday(day)
format = "%B %d %Y #{BusinessTime::Config.end_of_workday}"
Time.zone ? Time.zone.parse(day.strftime(format)) :
Time.parse(day.strftime(format))
end
# Gives the time at the beginning of the workday, assuming that this time
# falls on a workday.
# Note: It pretends that this day is a workday whether or not it really is a
# workday.
def beginning_of_workday(day)
format = "%B %d %Y #{BusinessTime::Config.beginning_of_workday}"
Time.zone ? Time.zone.parse(day.strftime(format)) :
Time.parse(day.strftime(format))
end
# True if this time is on a workday (between 00:00:00 and 23:59:59), even if
# this time falls outside of normal business hours.
def workday?(day)
Time.weekday?(day) &&
!BusinessTime::Config.holidays.include?(day.to_date)
end
# True if this time falls on a weekday.
def weekday?(day)
BusinessTime::Config.weekdays.include? day.wday
end
def before_business_hours?(time)
time < beginning_of_workday(time)
end
def after_business_hours?(time)
time > end_of_workday(time)
end
# Rolls forward to the next beginning_of_workday
# when the time is outside of business hours
def roll_forward(time)
if (Time.before_business_hours?(time) || !Time.workday?(time))
next_business_time = Time.beginning_of_workday(time)
elsif Time.after_business_hours?(time + 1)
next_business_time = Time.beginning_of_workday(time) + 1.day
else
next_business_time = time.clone
end
while !Time.workday?(next_business_time)
next_business_time += 1.day
end
next_business_time
end
end
end
class Time
def business_time_until(to_time)
# Make sure that we will calculate time from A to B "clockwise"
direction = 1
if self < to_time
time_a = self
time_b = to_time
else
time_a = to_time
time_b = self
direction = -1
end
# Align both times to the closest business hours
time_a = Time::roll_forward(time_a)
time_b = Time::roll_forward(time_b)
# If same date, then calculate difference straight forward
# if time_a.to_date == time_b.to_date
# result = time_b - time_a
# return result *= direction
# end
# Both times are in different dates
result = Time.parse(time_a.strftime('%Y-%m-%d ') + BusinessTime::Config.end_of_workday) - time_a # First day
result += time_b - Time.parse(time_b.strftime('%Y-%m-%d ') + BusinessTime::Config.beginning_of_workday) # Last day
# All days in between
#puts "--- #{time_a}-#{time_b} - #{direction}"
# duration_of_working_day = Time.parse(BusinessTime::Config.end_of_workday) - Time.parse(BusinessTime::Config.beginning_of_workday)
# result += (time_a.to_date.business_days_until(time_b.to_date) - 1) * duration_of_working_day
result = 0
# All days in between
time_c = time_a
while time_c.to_i < time_b.to_i do
end_of_workday = Time.end_of_workday(time_c)
if !Time.workday?(time_c)
time_c = Time.beginning_of_workday(time_c) + 1.day
# puts 'VACATIONS! ' + time_c.to_s
end
if time_c.to_date == time_b.to_date
if end_of_workday < time_b
result += end_of_workday - time_c
break
else
result += time_b - time_c
break
end
else
result += end_of_workday - time_c
time_c = Time::roll_forward(end_of_workday)
end
result += 1 if end_of_workday.to_s =~ /23:59:59/
end
# Make sure that sign is correct
result *= direction
end
end

View file

@ -1,10 +1,5 @@
require 'business_time'
require 'business_time/business_minutes'
require 'business_time/core_ext/fixnum_minute'
require 'business_time/core_ext/time_fix'
module TimeCalculation module TimeCalculation
def self.config(config, timezone, start_time) def self.working_hours(start_time, config, timezone)
time_diff = 0 time_diff = 0
if timezone if timezone
begin begin
@ -15,50 +10,286 @@ module TimeCalculation
puts e.backtrace puts e.backtrace
end end
end end
beginning_of_workday = Time.parse("1977-10-27 #{config['beginning_of_workday']}") + time_diff beginning_of_workday = Time.parse("1977-10-27 #{config['beginning_of_workday']}")
if beginning_of_workday end_of_workday = Time.parse("1977-10-27 #{config['end_of_workday']}") - 3600
config['beginning_of_workday'] = "#{beginning_of_workday.hour}:#{beginning_of_workday.min}" config_ok = false
end working_hours = {}
end_of_workday = Time.parse("1977-10-27 #{config['end_of_workday']}") + time_diff [:Mon, :Tue, :Wed, :Thu, :Fri, :Sat, :Sun].each {|day|
if end_of_workday working_hours[day] = []
config['end_of_workday'] = "#{end_of_workday.hour}:#{end_of_workday.min}" if config[day.to_s] == true || config[day.to_s] == day.to_s
end config_ok = true
BusinessTime::Config.beginning_of_workday = config['beginning_of_workday'] (0..23).each {|hour|
BusinessTime::Config.end_of_workday = config['end_of_workday'] time = Time.parse("1977-10-27 #{hour}:00:00")
days = [] if time >= beginning_of_workday && time <= end_of_workday
['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].each {|day| working_hours[day].push true
if config[day] else
days.push day.downcase.to_sym working_hours[day].push nil
end
}
end end
} }
BusinessTime::Config.work_week = days
holidays = [] if !config_ok
if config['holidays'] raise "sla config is invalid! " + config.inspect
config['holidays'].each {|holiday| end
date = Date.parse( holiday )
holidays.push date.to_date # shift working hours / if needed
if time_diff && time_diff != 0
hours_to_shift = (time_diff / 3600 ).round
move_items = {
:Mon => [],
:Tue => [],
:Wed => [],
:Thu => [],
:Fri => [],
:Sat => [],
:Sun => [],
}
(1..hours_to_shift).each {|count|
working_hours.each {|day, value|
if working_hours[day]
to_move = working_hours[day].shift
if day == :Mon
move_items[:Tue].push to_move
elsif day == :Tue
move_items[:Wed].push to_move
elsif day == :Wed
move_items[:Thu].push to_move
elsif day == :Thu
move_items[:Fri].push to_move
elsif day == :Fri
move_items[:Sat].push to_move
elsif day == :Sat
move_items[:Sun].push to_move
elsif day == :Sun
move_items[:Mon].push to_move
end
end
}
}
move_items.each {|day, value|
value.each {|item|
working_hours[day].push item
}
} }
end end
BusinessTime::Config.holidays = holidays return working_hours
end end
def self.business_time_diff(start_time, end_time) def self.business_time_diff(start_time, end_time, config, timezone = '')
if start_time.class == String if start_time.class == String
start_time = Time.parse( start_time.to_s + 'UTC' ) start_time = Time.parse( start_time.to_s + 'UTC' )
end end
if end_time.class == String if end_time.class == String
end_time = Time.parse( end_time.to_s + 'UTC' ) end_time = Time.parse( end_time.to_s + 'UTC' )
end end
diff = start_time.business_time_until( end_time ) / 60
working_hours = self.working_hours(start_time, config, timezone)
week_day_map = {
1 => :Mon,
2 => :Tue,
3 => :Wed,
4 => :Thu,
5 => :Fri,
6 => :Sat,
0 => :Sun,
}
count = 0
calculation = true
first_loop = true
while calculation do
week_day = start_time.wday
day = start_time.day
month = start_time.month
year = start_time.year
hour = start_time.hour
# check if it's vacation day
if config
if config['holidays']
if config['holidays'].include?("#{year}-#{month}-#{day}")
# jump to next day
start_time = start_time.beginning_of_day + 86400
next
end
end
end
# check if it's countable day
if working_hours[ week_day_map[week_day] ].empty?
# jump to next day
start_time = start_time.beginning_of_day + 86400
next
end
# fillup to first full hour
if first_loop
diff = end_time - start_time
if diff > 59 * 60
diff = start_time - start_time.beginning_of_hour
end
start_time += diff
# check if it's countable hour
if working_hours[ week_day_map[week_day] ][ hour ]
count += diff
end
end
first_loop = false
# loop to next hour
(hour..23).each { |next_hour|
# check if end time is lower
if start_time >= end_time
calculation = false
break
end
# check if end_time is within this hour
diff = end_time - start_time
if diff > 59 * 60
diff = 3600
end
# keep it in current day
if next_hour == 23
start_time += diff-1
else
start_time += diff
end
# check if it's business hour and count
if working_hours[ week_day_map[week_day] ][ next_hour ]
count += diff
end
}
# loop to next day
start_time = start_time.beginning_of_day + 86400
end
diff = count / 60
diff.round diff.round
end end
def self.dest_time(start_time, diff_in_min) def self.dest_time(start_time, diff_in_min, config, timezone = '')
if start_time.class == String if start_time.class == String
start_time = Time.parse( start_time.to_s + ' UTC' ) start_time = Time.parse( start_time.to_s + ' UTC' )
end end
dest_time = diff_in_min.round.business_minute.after( start_time )
return dest_time # loop
working_hours = self.working_hours(start_time, config, timezone)
week_day_map = {
1 => :Mon,
2 => :Tue,
3 => :Wed,
4 => :Thu,
5 => :Fri,
6 => :Sat,
0 => :Sun,
}
count = diff_in_min * 60
calculation = true
first_loop = true
while calculation do
week_day = start_time.wday
day = start_time.day
month = start_time.month
year = start_time.year
hour = start_time.hour
#puts "start outer loop #{start_time}-#{week_day}-#{year}-#{month}-#{day}-#{hour}|c#{count}"
# check if it's vacation day
if config
if config['holidays']
if config['holidays'].include?("#{year}-#{month}-#{day}")
# jump to next day
start_time = start_time.beginning_of_day + 86400
next
end
end
end
# check if it's countable day
if working_hours[ week_day_map[week_day] ].empty?
# jump to next day
start_time = start_time.beginning_of_day + 86400
next
end
# fillup to first full hour
if first_loop
# get rest of this hour
diff = 3600 - (start_time - start_time.beginning_of_hour)
start_time += diff
# check if it's countable hour
if working_hours[ week_day_map[week_day] ][ hour ]
count -= diff
end
# start on next hour of we moved to next
if diff != 0
hour += 1
end
end
first_loop = false
# loop to next hour
(hour..23).each { |next_hour|
diff = 3600
# check if count positiv
if count <= 0
calculation = false
break
end
# check if it's business hour and count
if working_hours[ week_day_map[week_day] ][ next_hour ]
# check if count is within this hour
if count > 59 * 60
diff = 3600
else
diff = count
end
count -= diff
end
# keep it in current day
if next_hour == 23
start_time += diff-1
else
start_time += diff
end
}
# check if count positiv
if count <= 0
calculation = false
break
end
# loop to next day
start_time = start_time.beginning_of_day + 86400
end
return start_time
end end
end end

View file

@ -455,7 +455,7 @@ class TicketTest < ActiveSupport::TestCase
assert( ticket, "ticket created" ) assert( ticket, "ticket created" )
assert_equal( ticket.escalation_time, nil, 'ticket.escalation_time verify' ) assert_equal( ticket.escalation_time, nil, 'ticket.escalation_time verify' )
# set sla's for timezone "Europe/Berlin" (+1), so UTC times are 8:00-17:00 # set sla's for timezone "Europe/Berlin" wintertime (+1), so UTC times are 8:00-17:00
sla = Sla.create( sla = Sla.create(
:name => 'test sla 1', :name => 'test sla 1',
:condition => {}, :condition => {},
@ -473,17 +473,16 @@ class TicketTest < ActiveSupport::TestCase
:created_by_id => 1, :created_by_id => 1,
) )
ticket = Ticket.find(ticket.id) ticket = Ticket.find(ticket.id)
assert_equal( ticket.escalation_time.gmtime.to_s, '2013-03-21 12:00:00 UTC', 'ticket.escalation_time verify 1' ) assert_equal( ticket.escalation_time.gmtime.to_s, '2013-03-21 11:30:00 UTC', 'ticket.escalation_time verify 1' )
assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-03-21 12:00:00 UTC', 'ticket.first_response_escal_date verify 1' ) assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-03-21 11:30:00 UTC', 'ticket.first_response_escal_date verify 1' )
assert_equal( ticket.update_time_escal_date.gmtime.to_s, '2013-03-21 13:00:00 UTC', 'ticket.update_time_escal_date verify 1' ) assert_equal( ticket.update_time_escal_date.gmtime.to_s, '2013-03-21 12:30:00 UTC', 'ticket.update_time_escal_date verify 1' )
assert_equal( ticket.close_time_escal_date.gmtime.to_s, '2013-03-21 14:00:00 UTC', 'ticket.close_time_escal_date verify 1' ) assert_equal( ticket.close_time_escal_date.gmtime.to_s, '2013-03-21 13:30:00 UTC', 'ticket.close_time_escal_date verify 1' )
delete = sla.destroy delete = sla.destroy
assert( delete, "sla destroy" ) assert( delete, "sla destroy" )
delete = ticket.destroy delete = ticket.destroy
assert( delete, "ticket destroy" ) assert( delete, "ticket destroy" )
ticket = Ticket.create( ticket = Ticket.create(
:title => 'some title äöüß', :title => 'some title äöüß',
:group => Group.lookup( :name => 'Users'), :group => Group.lookup( :name => 'Users'),
@ -498,7 +497,7 @@ class TicketTest < ActiveSupport::TestCase
assert( ticket, "ticket created" ) assert( ticket, "ticket created" )
assert_equal( ticket.escalation_time, nil, 'ticket.escalation_time verify' ) assert_equal( ticket.escalation_time, nil, 'ticket.escalation_time verify' )
# set sla's for timezone "Europe/Berlin" (+1), so UTC times are 8:00-17:00 # set sla's for timezone "Europe/Berlin" summertime (+2), so UTC times are 7:00-16:00
sla = Sla.create( sla = Sla.create(
:name => 'test sla 1', :name => 'test sla 1',
:condition => {}, :condition => {},
@ -516,16 +515,60 @@ class TicketTest < ActiveSupport::TestCase
:created_by_id => 1, :created_by_id => 1,
) )
ticket = Ticket.find(ticket.id) ticket = Ticket.find(ticket.id)
assert_equal( ticket.escalation_time.gmtime.to_s, '2013-10-21 13:00:00 UTC', 'ticket.escalation_time verify 1' ) assert_equal( ticket.escalation_time.gmtime.to_s, '2013-10-21 11:30:00 UTC', 'ticket.escalation_time verify 1' )
assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-10-21 13:00:00 UTC', 'ticket.first_response_escal_date verify 1' ) assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-10-21 11:30:00 UTC', 'ticket.first_response_escal_date verify 1' )
assert_equal( ticket.update_time_escal_date.gmtime.to_s, '2013-10-21 14:00:00 UTC', 'ticket.update_time_escal_date verify 1' ) assert_equal( ticket.update_time_escal_date.gmtime.to_s, '2013-10-21 12:30:00 UTC', 'ticket.update_time_escal_date verify 1' )
assert_equal( ticket.close_time_escal_date.gmtime.to_s, '2013-10-21 15:00:00 UTC', 'ticket.close_time_escal_date verify 1' ) assert_equal( ticket.close_time_escal_date.gmtime.to_s, '2013-10-21 13:30:00 UTC', 'ticket.close_time_escal_date verify 1' )
delete = ticket.destroy delete = ticket.destroy
assert( delete, "ticket destroy" ) assert( delete, "ticket destroy" )
delete = sla.destroy delete = sla.destroy
assert( delete, "sla destroy" ) assert( delete, "sla destroy" )
ticket = Ticket.create(
:title => 'some title äöüß',
:group => Group.lookup( :name => 'Users'),
:customer_id => 2,
:ticket_state => Ticket::State.lookup( :name => 'new' ),
:ticket_priority => Ticket::Priority.lookup( :name => '2 normal' ),
:created_at => '2013-10-21 06:30:00 UTC',
:updated_at => '2013-10-21 06:30:00 UTC',
:updated_by_id => 1,
:created_by_id => 1,
)
assert( ticket, "ticket created" )
assert_equal( ticket.escalation_time, nil, 'ticket.escalation_time verify' )
# set sla's for timezone "Europe/Berlin" summertime (+2), so UTC times are 7:00-16:00
sla = Sla.create(
:name => 'test sla 1',
:condition => {},
:data => {
"Mon"=>"Mon", "Tue"=>"Tue", "Wed"=>"Wed", "Thu"=>"Thu", "Fri"=>"Fri", "Sat"=>"Sat", "Sun"=>"Sun",
"beginning_of_workday" => "9:00",
"end_of_workday" => "18:00",
},
:timezone => 'Europe/Berlin',
:first_response_time => 120,
:update_time => 180,
:close_time => 240,
:active => true,
:updated_by_id => 1,
:created_by_id => 1,
)
ticket = Ticket.find(ticket.id)
assert_equal( ticket.escalation_time.gmtime.to_s, '2013-10-21 09:00:00 UTC', 'ticket.escalation_time verify 1' )
assert_equal( ticket.first_response_escal_date.gmtime.to_s, '2013-10-21 09:00:00 UTC', 'ticket.first_response_escal_date verify 1' )
assert_equal( ticket.update_time_escal_date.gmtime.to_s, '2013-10-21 10:00:00 UTC', 'ticket.update_time_escal_date verify 1' )
assert_equal( ticket.close_time_escal_date.gmtime.to_s, '2013-10-21 11:00:00 UTC', 'ticket.close_time_escal_date verify 1' )
delete = sla.destroy
assert( delete, "sla destroy" )
delete = ticket.destroy
assert( delete, "ticket destroy" )
end end
end end

View file

@ -73,6 +73,8 @@ class WorkingTimeTest < ActiveSupport::TestCase
], ],
}, },
}, },
# test 5
{ {
:start => '2013-02-28 17:00:00', :start => '2013-02-28 17:00:00',
:end => '2013-02-28 23:59:59', :end => '2013-02-28 23:59:59',
@ -87,10 +89,158 @@ class WorkingTimeTest < ActiveSupport::TestCase
'end_of_workday' => '6:00 pm', 'end_of_workday' => '6:00 pm',
}, },
}, },
# test 6
{
:start => '2013-02-28 17:00:00',
:end => '2013-03-08 23:59:59',
:diff => 3660,
:config => {
'Mon' => true,
'Tue' => true,
'Wed' => true,
'Thu' => true,
'Fri' => true,
'beginning_of_workday' => '8:00 am',
'end_of_workday' => '6:00 pm',
},
},
# test 7
{
:start => '2012-02-28 17:00:00',
:end => '2013-03-08 23:59:59',
:diff => 160860,
:config => {
'Mon' => true,
'Tue' => true,
'Wed' => true,
'Thu' => true,
'Fri' => true,
'beginning_of_workday' => '8:00 am',
'end_of_workday' => '6:00 pm',
},
},
# test 8
{
:start => '2013-02-28 17:01:00',
:end => '2013-02-28 18:10:59',
:diff => 61,
:config => {
'Mon' => true,
'Tue' => true,
'Wed' => true,
'Thu' => true,
'Fri' => true,
'beginning_of_workday' => '8:00 am',
'end_of_workday' => '6:00 pm',
},
},
# test 9
{
:start => '2013-02-28 18:01:00',
:end => '2013-02-28 18:10:59',
:diff => 0,
:config => {
'Mon' => true,
'Tue' => true,
'Wed' => true,
'Thu' => true,
'Fri' => true,
'beginning_of_workday' => '8:00 am',
'end_of_workday' => '6:00 pm',
},
},
# test 10 / summertime
{
:start => '2013-02-28 18:01:00',
:end => '2013-02-28 18:10:59',
:diff => 0,
:timezone => 'Europe/Berlin',
:config => {
'Mon' => true,
'Tue' => true,
'Wed' => true,
'Thu' => true,
'Fri' => true,
'beginning_of_workday' => '8:00 am',
'end_of_workday' => '6:00 pm',
},
},
# test 11 / summertime
{
:start => '2013-02-28 17:01:00',
:end => '2013-02-28 17:10:59',
:diff => 0,
:timezone => 'Europe/Berlin',
:config => {
'Mon' => true,
'Tue' => true,
'Wed' => true,
'Thu' => true,
'Fri' => true,
'beginning_of_workday' => '8:00 am',
'end_of_workday' => '6:00 pm',
},
},
# test 12 / wintertime
{
:start => '2013-08-29 17:01:00',
:end => '2013-08-29 17:10:59',
:diff => 0,
:timezone => 'Europe/Berlin',
:config => {
'Mon' => true,
'Tue' => true,
'Wed' => true,
'Thu' => true,
'Fri' => true,
'beginning_of_workday' => '8:00 am',
'end_of_workday' => '6:00 pm',
},
},
# test 13 / summertime
{
:start => '2013-02-28 16:01:00',
:end => '2013-02-28 16:10:59',
:diff => 10,
:timezone => 'Europe/Berlin',
:config => {
'Mon' => true,
'Tue' => true,
'Wed' => true,
'Thu' => true,
'Fri' => true,
'beginning_of_workday' => '8:00 am',
'end_of_workday' => '6:00 pm',
},
},
# test 14 / wintertime
{
:start => '2013-08-29 16:01:00',
:end => '2013-08-29 16:10:59',
:diff => 0,
:timezone => 'Europe/Berlin',
:config => {
'Mon' => true,
'Tue' => true,
'Wed' => true,
'Thu' => true,
'Fri' => true,
'beginning_of_workday' => '8:00 am',
'end_of_workday' => '6:00 pm',
},
},
] ]
tests.each { |test| tests.each { |test|
TimeCalculation.config( test[:config], nil, test[:start] ) diff = TimeCalculation.business_time_diff( test[:start], test[:end], test[:config], test[:timezone] )
diff = TimeCalculation.business_time_diff( test[:start], test[:end] )
assert_equal( diff, test[:diff], 'diff' ) assert_equal( diff, test[:diff], 'diff' )
} }
end end
@ -178,6 +328,7 @@ class WorkingTimeTest < ActiveSupport::TestCase
}, },
}, },
# test 6 # test 6
{ {
:start => '2012-12-17 08:00:00', :start => '2012-12-17 08:00:00',
@ -241,10 +392,112 @@ class WorkingTimeTest < ActiveSupport::TestCase
'end_of_workday' => '6:00 pm', 'end_of_workday' => '6:00 pm',
}, },
}, },
# test 11 / summertime
{
:start => '2013-03-08 21:20:15',
:dest_time => '2013-03-11 09:00:00',
:diff => 120,
:timezone => 'Europe/Berlin',
:config => {
'Mon' => true,
'Tue' => true,
'Wed' => true,
'Thu' => true,
'Fri' => true,
'beginning_of_workday' => '8:00 am',
'end_of_workday' => '6:00 pm',
},
},
# test 12 / wintertime
{
:start => '2013-09-06 21:20:15',
:dest_time => '2013-09-09 08:00:00',
:diff => 120,
:timezone => 'Europe/Berlin',
:config => {
'Mon' => true,
'Tue' => true,
'Wed' => true,
'Thu' => true,
'Fri' => true,
'beginning_of_workday' => '8:00 am',
'end_of_workday' => '6:00 pm',
},
},
# test 13 / wintertime
{
:start => '2013-10-21 06:30:00',
:dest_time => '2013-10-21 09:00:00',
:diff => 120,
:timezone => 'Europe/Berlin',
:config => {
'Mon' => true,
'Tue' => true,
'Wed' => true,
'Thu' => true,
'Fri' => true,
'beginning_of_workday' => '9:00 am',
'end_of_workday' => '6:00 pm',
},
},
# test 14 / wintertime
{
:start => '2013-10-21 04:34:15',
:dest_time => '2013-10-21 09:00:00',
:diff => 120,
:timezone => 'Europe/Berlin',
:config => {
'Mon' => true,
'Tue' => true,
'Wed' => true,
'Thu' => true,
'Fri' => true,
'beginning_of_workday' => '9:00 am',
'end_of_workday' => '6:00 pm',
},
},
# test 15 / wintertime
{
:start => '2013-10-20 22:34:15',
:dest_time => '2013-10-21 09:00:00',
:diff => 120,
:timezone => 'Europe/Berlin',
:config => {
'Mon' => true,
'Tue' => true,
'Wed' => true,
'Thu' => true,
'Fri' => true,
'beginning_of_workday' => '9:00 am',
'end_of_workday' => '6:00 pm',
},
},
# test 16 / wintertime
{
:start => '2013-10-21 07:00:15',
:dest_time => '2013-10-21 09:00:00',
:diff => 120,
:timezone => 'Europe/Berlin',
:config => {
'Mon' => true,
'Tue' => true,
'Wed' => true,
'Thu' => true,
'Fri' => true,
'beginning_of_workday' => '9:00 am',
'end_of_workday' => '6:00 pm',
},
},
] ]
tests.each { |test| tests.each { |test|
TimeCalculation.config( test[:config], nil, test[:start] ) dest_time = TimeCalculation.dest_time( test[:start] + ' UTC', test[:diff], test[:config], test[:timezone] )
dest_time = TimeCalculation.dest_time( test[:start] + ' UTC', test[:diff] )
assert_equal( dest_time.gmtime, Time.parse( test[:dest_time] + ' UTC' ), "dest time - #{test[:dest_time].to_s}" ) assert_equal( dest_time.gmtime, Time.parse( test[:dest_time] + ' UTC' ), "dest time - #{test[:dest_time].to_s}" )
} }
end end