workpattern 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +4 -0
- data/README.md +48 -3
- data/lib/workpattern.rb +58 -134
- data/lib/workpattern/clock.rb +40 -25
- data/lib/workpattern/day.rb +93 -29
- data/lib/workpattern/hour.rb +65 -26
- data/lib/workpattern/utility/base.rb +20 -2
- data/lib/workpattern/version.rb +3 -1
- data/lib/workpattern/week.rb +50 -35
- data/lib/workpattern/workpattern.rb +125 -78
- metadata +2 -2
data/CHANGELOG
CHANGED
data/README.md
CHANGED
@@ -4,6 +4,25 @@
|
|
4
4
|
|
5
5
|
Simple addition and subtraction of minutes on dates taking account of real-life working and resting periods.
|
6
6
|
|
7
|
+
## So What?
|
8
|
+
|
9
|
+
The core Ruby classes that represent date and time allow calculations by adding a duration such as days or
|
10
|
+
minutes to a date and returning the new `Date` or `DateTime` as the result. Although there
|
11
|
+
are 60 seconds in every minute and 60 minutes in every hour, there aren't always 24 hours in every day, and
|
12
|
+
if there was, we still wouldn't be working during all of them. We would be doing other things like eating,
|
13
|
+
sleeping, travelling and having a bit of leisure time. Workpattern refers to this time as Resting time.
|
14
|
+
It refers to the time when we're busy doing stuff as Working time.
|
15
|
+
|
16
|
+
When it comes to scheduling work, whether part of a project, teachers in a classroom or even bed availability
|
17
|
+
in a hospital, the working day can have anything from 0 hours to the full 24 hours. Most office based work
|
18
|
+
is something like 7.5 or 8 hours a day except weekends, public holidays and vacations when no work takes
|
19
|
+
place.
|
20
|
+
|
21
|
+
The `Workpattern` library was born to allow date related calculations to take into account real life
|
22
|
+
working and resting times. It gets told about working and resting periods and can then perform calculations
|
23
|
+
on a given date. It can add and subtract a number of minutes, calculate the working minutes between two dates
|
24
|
+
and say whether a specific minute is working or resting.
|
25
|
+
|
7
26
|
This gem has the potential to serve as the engine for scheduling algorithms that are the core of products such as Microsoft Project and Oracle Primavera P6 as well as other applications that need to know when they can perform work and when they can’t.
|
8
27
|
|
9
28
|
## Install
|
@@ -12,19 +31,19 @@ This gem has the potential to serve as the engine for scheduling algorithms that
|
|
12
31
|
|
13
32
|
## Getting Started
|
14
33
|
|
15
|
-
The first step is to create a
|
34
|
+
The first step is to create a `Workpattern` to hold all the working and resting times. I'll start in 2011 and let it run for 10 years.
|
16
35
|
|
17
36
|
``` ruby
|
18
37
|
mywp=Workpattern.new('My Workpattern',2011,10)
|
19
38
|
```
|
20
39
|
|
21
|
-
My
|
40
|
+
My `Workpattern` will be created as a 24 hour a day full working time. Now it has to be told about the resting periods. First the weekends.
|
22
41
|
|
23
42
|
``` ruby
|
24
43
|
mywp.resting(:days => :weekend)
|
25
44
|
```
|
26
45
|
|
27
|
-
then the days in the week have specific working and resting times using the *Workpattern.clock* method, although anything that responds to
|
46
|
+
then the days in the week have specific working and resting times using the *Workpattern.clock* method, although anything that responds to `#hour` and `#min` methods will do ...
|
28
47
|
|
29
48
|
``` ruby
|
30
49
|
mywp.resting(:days =>:weekday, :from_time=>Workpattern.clock(0,0),:to_time=>Workpattern.clock(8,59))
|
@@ -47,6 +66,32 @@ result_date = mywp.calc(mydate,1920) # => 6/9/11@18:00
|
|
47
66
|
* Pull requests are very welcome, however I have never participated in Open Source so will be a bit slow as I am learning. Please be patient with me. Please include spec and/or feature coverage for every patch, and create a topic branch for every separate change you make.
|
48
67
|
* Advice, such as pointing out how I should really code in Ruby will be gratefully received.
|
49
68
|
|
69
|
+
## Things To Do
|
70
|
+
|
71
|
+
In its current form this library is being made available to see if there is any interest in using
|
72
|
+
it. At the moment it can perform the following:
|
73
|
+
|
74
|
+
* define the working and resting minutes for any 24 hour day
|
75
|
+
* given a date it can return the resulting date after adding or subtracting a number of minutes
|
76
|
+
* calculate the number of working minutes between two dates
|
77
|
+
* report whether a specific minute in time is working or resting
|
78
|
+
|
79
|
+
This is what I consider to be the basics, but there are a number of functional and non-functial areas I
|
80
|
+
would like to address in a future version.
|
81
|
+
|
82
|
+
## Functional
|
83
|
+
|
84
|
+
* Merge two Workpatterns together to create a new one allowing either resting or working to take precedence
|
85
|
+
* Given a date, find the next working or resting minute either before or after it.
|
86
|
+
* Handle both 23 and 25 hour days that occur when the clocks change.
|
87
|
+
* Extract patterns from the workpattern so they can be persisted in a database.
|
88
|
+
* Decide how to handle different Timezones apart from UTC.
|
89
|
+
|
90
|
+
## Non-Functional
|
91
|
+
|
92
|
+
* Improve the documentation and introduce real world use as an example
|
93
|
+
* Improve my ability to write Ruby code
|
94
|
+
|
50
95
|
## License
|
51
96
|
|
52
97
|
(The MIT License)
|
data/lib/workpattern.rb
CHANGED
@@ -24,202 +24,126 @@ require 'workpattern/workpattern'
|
|
24
24
|
#
|
25
25
|
# Documentation: Barrie Callender <barrie@callenb.org>
|
26
26
|
#
|
27
|
-
# == Overview
|
28
|
-
#
|
29
|
-
# The core Ruby classes that represent date and time allow calculations by
|
30
|
-
# adding a duration such as days or minutes to a date and returning the new
|
31
|
-
# <tt>Date</tt> or <tt>DateTime</tt> as the result.
|
32
|
-
#
|
33
|
-
# Although there are 60 seconds in every minute and 60 minutes in every hour, there
|
34
|
-
# aren't always 24 hours in every day, and if there was, we still wouldn't
|
35
|
-
# be working during all of them. We would be doing other things like eating,
|
36
|
-
# sleeping, travelling and having a bit of leisure time. Workpattern refers to this
|
37
|
-
# time as Resting time. It refers to the time when we're busy doing stuff as
|
38
|
-
# Working time.
|
39
|
-
#
|
40
|
-
# When it comes to scheduling work, whether part of a project, teachers in a
|
41
|
-
# classroom or even bed availability in a hospital, the working day can have
|
42
|
-
# anything from 0 hours to the full 24 hours. Most office based work
|
43
|
-
# is something like 7.5 or 8 hours a day except weekends, public holidays and
|
44
|
-
# vacations when no work takes place.
|
45
|
-
#
|
46
|
-
# The <tt>Workpattern</tt> library was born to allow date related calculations to take
|
47
|
-
# into account real life working and resting times. It gets told about working
|
48
|
-
# and resting periods and can then perform calculations on a given date. It can
|
49
|
-
# add and subtract a number of minutes, calculate the working minutes between
|
50
|
-
# two dates and say whether a specific minute is working or resting.
|
51
|
-
#
|
52
|
-
# == Illustration
|
53
|
-
#
|
54
|
-
# In real life we may be reasonably confident that it will take us 32 hours to
|
55
|
-
# write a document. If we started on a Thursday at 9:00am we wouldn't be
|
56
|
-
# working 32 hours without interruption (let's pretend we're not software
|
57
|
-
# developers for this one!). We'd go home at the end of one working day and
|
58
|
-
# not return until the next. The weekend would not include working on the
|
59
|
-
# document either. We would probably work 8 hours on Thursday, Friday, Monday
|
60
|
-
# and Tuesday to complete the work.
|
61
|
-
#
|
62
|
-
# The <tt>Workpattern</tt> library will be able to tell you that if you started at
|
63
|
-
# 9:00 am on Thursday, you should be finished at 6:00 pm on Tuesday - allowing an hour
|
64
|
-
# for lunch each day! For it to do that it has to know when you can work and
|
65
|
-
# when you are resting.
|
66
|
-
#
|
67
|
-
# == An Example Session
|
68
|
-
#
|
69
|
-
# Using the illustration as a basis, we want to find out when I will finish the document.
|
70
|
-
# It is going to take me 32 hours to complete the document and I'm going to start on it
|
71
|
-
# as soon as I arrive at work on the morning of Thursday 1st September 2011. My working
|
72
|
-
# day starts at 9:00am,finishes at 6:00pm and I take an hour for lunch. I don't work
|
73
|
-
# on the weekend.
|
74
|
-
#
|
75
|
-
# The first step is to create a <tt>Workpattern</tt> to hold all the working and resting times.
|
76
|
-
# I'll start in 2011 and let it run for 10 years.
|
77
|
-
#
|
78
|
-
# mywp=Workpattern.new('My Workpattern',2011,10)
|
79
|
-
#
|
80
|
-
# My <tt>Workpattern</tt> will be created as a 24 hour a day full working time. Now it has to
|
81
|
-
# be told about the resting periods. First the weekends.
|
82
|
-
#
|
83
|
-
# mywp.resting(:days => :weekend)
|
84
|
-
#
|
85
|
-
# then the days in the week have specific working and resting times using the
|
86
|
-
# <tt>Time::hm</tt> method added by <tt>Workpattern</tt> ...
|
87
|
-
#
|
88
|
-
# mywp.resting(:days =>:weekday, :from_time=>Workpattern.clock(0,0),:to_time=>Workpattern.clock(8,59))
|
89
|
-
# mywp.resting(:days =>:weekday, :from_time=>Workpattern.clock(12,0),:to_time=>Workpattern.clock(12,59))
|
90
|
-
# mywp.resting(:days =>:weekday, :from_time=>Workpattern.clock(18,0),:to_time=>Workpattern.clock(23,59))
|
91
|
-
#
|
92
|
-
# Now we have the working and resting periods setup we can just add 32 hours as
|
93
|
-
# minutes (1920) to our date.
|
94
|
-
#
|
95
|
-
# mydate=DateTime.civil(2011,9,1,9,0)
|
96
|
-
# result_date = mywp.calc(mydate,1920) # => 6/9/11@18:00
|
97
|
-
#
|
98
|
-
# == Things To Do
|
99
|
-
#
|
100
|
-
# In its current form this library is being made available to see if there is any interest
|
101
|
-
# in using it. At the moment it can perform the following:
|
102
|
-
# * define the working and resting minutes for any 24 hour day
|
103
|
-
# * given a date it can return the resulting date after adding or subtracting a number of minutes
|
104
|
-
# * calculate the number of working minutes between two dates
|
105
|
-
# * report whether a specific minute in time is working or resting
|
106
|
-
# This is what I consider to be the basics, but there are a number of functional and
|
107
|
-
# non-functial areas I would like to address in a future version.
|
108
|
-
#
|
109
|
-
# === Functional
|
110
|
-
#
|
111
|
-
# * Merge two Workpatterns together to create a new one allowing either resting or working to take precedence
|
112
|
-
# * Given a date, find the next working or resting minute either before or after it.
|
113
|
-
# * Handle both 23 and 25 hour days that occur when the clocks change.
|
114
|
-
# * Extract patterns from the workpattern so they can be persisted in a database.
|
115
|
-
# * Decide how to handle different Timezones apart from UTC.
|
116
|
-
#
|
117
|
-
# === Non-Functional
|
118
|
-
#
|
119
|
-
# * Improve the documentation and introduce real world use as an example
|
120
|
-
# * Improve my ability to write Ruby code
|
121
|
-
#
|
122
27
|
module Workpattern
|
123
28
|
|
124
29
|
# Represents a full working hour
|
30
|
+
# @since 0.2.0
|
125
31
|
WORKING_HOUR = 2**60-1
|
32
|
+
|
126
33
|
# Represents a full resting hour
|
34
|
+
# @since 0.2.0
|
127
35
|
RESTING_HOUR = 0
|
36
|
+
|
128
37
|
# The default workpattern name
|
38
|
+
# @since 0.2.0
|
129
39
|
DEFAULT_WORKPATTERN_NAME = 'default'
|
40
|
+
|
130
41
|
# The default base year
|
42
|
+
# @since 0.2.0
|
131
43
|
DEFAULT_BASE_YEAR = 2000
|
44
|
+
|
132
45
|
# The default span in years
|
46
|
+
# @since 0.2.0
|
133
47
|
DEFAULT_SPAN = 100
|
48
|
+
|
134
49
|
# Hour in terms of days
|
50
|
+
# @since 0.2.0
|
135
51
|
HOUR = Rational(1,24)
|
52
|
+
|
136
53
|
# Minute in terms of days
|
54
|
+
# @since 0.2.0
|
137
55
|
MINUTE = Rational(1,1440)
|
138
|
-
|
56
|
+
|
57
|
+
# Earliest or first time in the day
|
58
|
+
# @since 0.0.1
|
139
59
|
FIRST_TIME_IN_DAY=Clock.new(0,0)
|
140
|
-
|
60
|
+
|
61
|
+
# Latest or last time in the day
|
62
|
+
# @since 0.0.1
|
141
63
|
LAST_TIME_IN_DAY=Clock.new(23,59)
|
142
|
-
|
64
|
+
|
65
|
+
# Specifies a working pattern
|
66
|
+
# @since 0.0.1
|
143
67
|
WORK = 1
|
144
|
-
|
68
|
+
|
69
|
+
# Specifies a resting pattern
|
70
|
+
# @since 0.0.1
|
145
71
|
REST = 0
|
72
|
+
|
146
73
|
# Represents the days of the week to be used in applying working and resting patterns.
|
74
|
+
# Values exist for each day of the week as well as for the weekend (Saturday and Sunday),
|
75
|
+
# the week (Monday to Friday) and all days in the week.
|
147
76
|
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
# <tt>:sun, :mon, :tue, :wed, :thu, :fri, :sat</tt> a day of the week.
|
151
|
-
# <tt>:all</tt> all days of the week.
|
152
|
-
# <tt>:weekend</tt> Saturday and Sunday.
|
153
|
-
# <tt>:weekday</tt> Monday to Friday inclusive.
|
154
|
-
#
|
77
|
+
# @since 0.0.1
|
155
78
|
DAYNAMES={:sun => [0],:mon => [1], :tue => [2], :wed => [3], :thu => [4], :fri => [5], :sat => [6],
|
156
79
|
:weekday => [1,2,3,4,5],
|
157
80
|
:weekend => [0,6],
|
158
81
|
:all => [0,1,2,3,4,5,6]}
|
159
82
|
|
160
|
-
#
|
83
|
+
# Covenience method to obtain a new <tt>Workpattern</tt>
|
161
84
|
#
|
162
|
-
#
|
85
|
+
# A negative <tt>span</tt> counts back from the <tt>base</tt> year
|
163
86
|
#
|
164
|
-
#
|
87
|
+
# @param [String] name Every workpattern has a unique name.
|
88
|
+
# @param [Integer] base Workpattern starts on the 1st January of this year.
|
89
|
+
# @param [Integer] span Workpattern spans this number of years ending on 31st December.
|
90
|
+
# @return [Workpattern]
|
165
91
|
#
|
166
|
-
#
|
167
|
-
# Every workpattern has a unique name.
|
168
|
-
# +base+::
|
169
|
-
# The starting year for the range of dates the Calendar
|
170
|
-
# can use. Always the 1st january.
|
171
|
-
# +span+::
|
172
|
-
# Duration of the Calendar in years. If <tt>span</tt> is negative
|
173
|
-
# then the range counts backwards from the <tt>base</tt>.
|
92
|
+
# @since 0.2.0
|
174
93
|
#
|
175
94
|
def self.new(name=DEFAULT_WORKPATTERN_NAME, base=DEFAULT_BASE_YEAR, span=DEFAULT_SPAN)
|
176
95
|
return Workpattern.new(name, base,span)
|
177
96
|
end
|
178
97
|
|
179
|
-
#
|
98
|
+
# Covenience method to obtain an Array of all the known <tt>Workpattern</tt> objects
|
99
|
+
#
|
100
|
+
# @return [Array] all <tt>Workpattern</tt> objects
|
180
101
|
#
|
181
|
-
#
|
102
|
+
# @since 0.2.0
|
182
103
|
#
|
183
104
|
def self.to_a()
|
184
105
|
return Workpattern.to_a
|
185
106
|
end
|
186
107
|
|
187
|
-
#
|
108
|
+
# Covenience method to obtain an existing <tt>Workpattern</tt>
|
188
109
|
#
|
189
|
-
#
|
110
|
+
# @param [String] name The name of the Workpattern to retrieve.
|
111
|
+
# @return [Workpattern]
|
190
112
|
#
|
191
|
-
#
|
192
|
-
#
|
193
|
-
# +name+:: The name of the Workpattern.
|
113
|
+
# @since 0.2.0
|
194
114
|
#
|
195
115
|
def self.get(name)
|
196
116
|
return Workpattern.get(name)
|
197
117
|
end
|
198
118
|
|
199
|
-
#
|
200
|
-
#
|
201
|
-
# Convenience method to delete the named <tt>Workpattern::Workpattern</tt>
|
119
|
+
# Convenience method to delete the named <tt>Workpattern</tt>
|
202
120
|
#
|
203
|
-
#
|
121
|
+
# @param [String] name The name of the Workpattern to be deleted.
|
204
122
|
#
|
205
|
-
#
|
123
|
+
# @since 0.2.0
|
206
124
|
#
|
207
125
|
def self.delete(name)
|
208
126
|
Workpattern.delete(name)
|
209
127
|
end
|
210
128
|
|
211
|
-
# :call-seq: clear
|
212
|
-
#
|
213
129
|
# Convenience method to delete all Workpatterns.
|
214
130
|
#
|
131
|
+
# @since 0.2.0
|
132
|
+
#
|
215
133
|
def self.clear
|
216
134
|
Workpattern.clear
|
217
135
|
end
|
218
136
|
|
219
|
-
#
|
220
|
-
#
|
221
|
-
# Convenience method to create a Clock object. This can be used for specifying times.
|
137
|
+
# Convenience method to create a Clock object. This can be used for specifying times
|
138
|
+
# if you don't want to create a <tt>DateTime</tt> object
|
222
139
|
#
|
140
|
+
# @param [Integer] hour the number of hours.
|
141
|
+
# @param [Integer] min the number of minutes
|
142
|
+
# @return [Clock]
|
143
|
+
# @see Clock
|
144
|
+
#
|
145
|
+
# @since 0.2.0
|
146
|
+
#
|
223
147
|
def self.clock(hour,min)
|
224
148
|
return Clock.new(hour,min)
|
225
149
|
end
|
data/lib/workpattern/clock.rb
CHANGED
@@ -1,27 +1,33 @@
|
|
1
1
|
module Workpattern
|
2
2
|
# Represents time on a clock in hours and minutes.
|
3
3
|
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
4
|
+
# @example
|
5
|
+
# myClock=Clock.new(3,32)
|
6
|
+
# myClock.minutes #=> 212
|
7
|
+
# myClock.hour #=> 3
|
8
|
+
# myClock.min #=> 32
|
9
|
+
# myClock.time #=> Time.new(1963,6,10,3,32)
|
10
|
+
# myClock.to_s #=> 3:32 212
|
10
11
|
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
12
|
+
# aClock=Clock.new(27,80)
|
13
|
+
# aClock.minutes #=> 1700
|
14
|
+
# aClock.hour #=> 4
|
15
|
+
# aClock.min #=> 20
|
16
|
+
# aClock.time #=> Time.new(1963,6,10,4,20)
|
17
|
+
# aClock.to_s #=> 4:20 1700
|
18
|
+
#
|
19
|
+
# @since 0.2.0
|
16
20
|
#
|
17
21
|
class Clock
|
18
|
-
|
19
|
-
#
|
20
|
-
# initialises <tt>Clock</tt> using the hours and minutes supplied
|
22
|
+
|
23
|
+
# Initialises an instance of <tt>Clock</tt> using the hours and minutes supplied
|
21
24
|
# or 0 if they are absent. Although there are 24 hours in a day
|
22
25
|
# (0-23) and 60 minutes in an hour (0-59), <tt>Clock</tt> calculates
|
23
26
|
# the full hours and remaining minutes of whatever is supplied.
|
24
27
|
#
|
28
|
+
# @param [Integer] hour number of hours
|
29
|
+
# @param [Integer] min number of minutes
|
30
|
+
#
|
25
31
|
def initialize(hour=0,min=0)
|
26
32
|
@hour=hour
|
27
33
|
@min=min
|
@@ -30,34 +36,43 @@ module Workpattern
|
|
30
36
|
@min=total_minutes % 60
|
31
37
|
end
|
32
38
|
|
33
|
-
#
|
34
|
-
#
|
39
|
+
# Returns the total number of minutes
|
40
|
+
#
|
41
|
+
# @return [Integer] total minutes represented by the Clock object
|
35
42
|
#
|
36
43
|
def minutes
|
37
44
|
return (@hour*60)+@min
|
38
45
|
end
|
39
46
|
|
40
|
-
#
|
41
|
-
#
|
47
|
+
# Returns the hour of the clock (0-23)
|
48
|
+
#
|
49
|
+
# @return [Integer] hour of Clock from 0 to 23.
|
42
50
|
#
|
43
51
|
def hour
|
44
52
|
return @hour % 24
|
45
53
|
end
|
46
54
|
|
47
|
-
#
|
48
|
-
#
|
55
|
+
# Returns the minute of the clock (0-59)
|
56
|
+
#
|
57
|
+
# @return [Integer] minute of Clock from 0 to 59
|
49
58
|
#
|
50
59
|
def min
|
51
60
|
return @min % 60
|
52
61
|
end
|
53
62
|
|
54
|
-
#
|
55
|
-
# returns a <tt>Time</tt> object with the correct
|
63
|
+
# Returns a <tt>Time</tt> object with the correct
|
56
64
|
# <tt>hour</tt> and <tt>min</tt> values. The date
|
57
|
-
# is 10th June 1963
|
58
|
-
#
|
65
|
+
# is 10th June 1963.
|
66
|
+
#
|
67
|
+
# @return [DateTime] The time using the date of 10th June 1963 (My Birthday)
|
59
68
|
def time
|
60
69
|
return DateTime.new(1963,6,10,hour,min)
|
61
|
-
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
# @return [String] representation of <tt>Clock</tt> value as 'hh:mn minutes'
|
74
|
+
def to_s
|
75
|
+
hour.to_s.concat(':').concat(min.to_s).concat(' ').concat(minutes.to_s)
|
76
|
+
end
|
62
77
|
end
|
63
78
|
end
|