periodoxical 0.6.2 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +143 -25
- data/lib/periodoxical/version.rb +1 -1
- data/lib/periodoxical.rb +85 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 01bdfec5ba5930f743cc1c4197e0964631c8dde5be1bb14c79082fe363bf72e1
|
4
|
+
data.tar.gz: 6aaaf40f368acbdd78632455889298e6eb6ced8c0ee80f4ed9e96387e7d05536
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc697912db1f3a5439ffd3cdf3371c4be7f4475bb2bbae1f01c2ac42887d6041c45763e1977eabf8f68e5f6ee7c7c0d728e80153796acd419d9741a7a3126d5f
|
7
|
+
data.tar.gz: 7a0c713a31ccca86c48b08bdcf1908056475a589c7adc0b93ed94dd13af0ebd35fe58151340fe2e88ff3da13d653673064c9c1d3500e9dbe61845528ecdbb207
|
data/README.md
CHANGED
@@ -29,8 +29,8 @@ Or install it yourself as:
|
|
29
29
|
|
30
30
|
## Usage
|
31
31
|
|
32
|
-
|
33
|
-
As a Ruby dev, I want to generate all the datetime blocks of **9:00AM - 10:30AM**for all days from May 23, 2024 to May 26, 2024 inclusive.
|
32
|
+
### Example 1
|
33
|
+
As a Ruby dev, I want to generate all the datetime blocks of **9:00AM - 10:30AM** for all days from **May 23, 2024** to **May 26, 2024** inclusive.
|
34
34
|
|
35
35
|
```rb
|
36
36
|
Periodoxical.generate(
|
@@ -65,7 +65,7 @@ Periodoxical.generate(
|
|
65
65
|
]
|
66
66
|
```
|
67
67
|
|
68
|
-
|
68
|
+
### Example 2 - specify days of the week
|
69
69
|
As a Ruby dev, I want to generate all the datetime blocks of **9:00AM - 10:30AM** and **2:00PM - 2:30PM**, on **Mondays**, **Wednesdays**, and **Thursdays**, between the dates of **May 23, 2024** and **June 12, 2024**, inclusive. This can be represented visually as:
|
70
70
|
|
71
71
|
<div align="center">
|
@@ -115,9 +115,9 @@ Periodoxical.generate(
|
|
115
115
|
]
|
116
116
|
```
|
117
117
|
|
118
|
-
|
118
|
+
### Example 3 - using the `limit` key.
|
119
119
|
|
120
|
-
As a ruby dev, I want to generate the next 3 datetime blocks of **9:00AM - 10:30AM** and **2:00PM - 2:30PM** on **Sundays**, after **May 23, 2024** using the `limit` key.
|
120
|
+
As a ruby dev, I want to generate the next **3** datetime blocks of **9:00AM - 10:30AM** and **2:00PM - 2:30PM** on **Sundays**, after **May 23, 2024** using the `limit` key.
|
121
121
|
|
122
122
|
```rb
|
123
123
|
Periodoxical.generate(
|
@@ -153,7 +153,7 @@ Periodoxical.generate(
|
|
153
153
|
]
|
154
154
|
```
|
155
155
|
|
156
|
-
|
156
|
+
### Example 4 - when time blocks vary between days
|
157
157
|
|
158
158
|
As a ruby dev, I want to generate all the timeblocks between **May 23, 2024** and **June 12, 2024** where the time should be **8AM-9AM** on **Mondays**, but **10:45AM-12:00PM** and **2:00PM-4:00PM** on **Wednesdays**, and **2:30PM-4:15PM** on **Thursdays**.
|
159
159
|
|
@@ -184,9 +184,9 @@ Periodoxical.generate(
|
|
184
184
|
)
|
185
185
|
```
|
186
186
|
|
187
|
-
|
187
|
+
### Example 5 - when specifying time blocks using day-of-month and/or week-of-month and/or month.
|
188
188
|
|
189
|
-
As a Ruby dev, I want to generate the next 3 slots for 8AM - 9AM for the 5th and 10th day of every month starting from June
|
189
|
+
As a Ruby dev, I want to generate the next 3 slots for **8AM - 9AM** for the **5th** and **10th** day of every month starting from **June**
|
190
190
|
|
191
191
|
```rb
|
192
192
|
Periodoxical.generate(
|
@@ -201,21 +201,21 @@ Periodoxical.generate(
|
|
201
201
|
#=>
|
202
202
|
[
|
203
203
|
{
|
204
|
-
start: #<DateTime: 2024-06-
|
205
|
-
end: #<DateTime: 2024-06-
|
204
|
+
start: #<DateTime: 2024-06-05T08:00:00-0700>,
|
205
|
+
end: #<DateTime: 2024-06-05T09:00:00-0700>,
|
206
206
|
},
|
207
207
|
{
|
208
|
-
start: #<DateTime: 2024-06-
|
209
|
-
end: #<DateTime: 2024-06-
|
208
|
+
start: #<DateTime: 2024-06-10T08:00:00-0700>,
|
209
|
+
end: #<DateTime: 2024-06-10T09:00:00-0700>,
|
210
210
|
},
|
211
211
|
{
|
212
|
-
start: #<DateTime: 2024-07-
|
213
|
-
end: #<DateTime: 2024-07-
|
212
|
+
start: #<DateTime: 2024-07-05T08:00:00-0700>,
|
213
|
+
end: #<DateTime: 2024-07-05T09:00:00-0700>,
|
214
214
|
},
|
215
215
|
]
|
216
216
|
```
|
217
217
|
|
218
|
-
As a Ruby dev, I want to generate 4 slots for 8AM - 9AM on Mondays but only in the first two weeks in the months of April, May, June
|
218
|
+
As a Ruby dev, I want to generate **4** slots for **8AM - 9AM** on **Mondays** but only in the **first two weeks** in the months of **April, May, and June**
|
219
219
|
|
220
220
|
```
|
221
221
|
Periodoxical.generate(
|
@@ -232,26 +232,67 @@ Periodoxical.generate(
|
|
232
232
|
#=>
|
233
233
|
[
|
234
234
|
{
|
235
|
-
start_time: #<DateTime: 2024-04-
|
236
|
-
end_time: #<DateTime: 2024-04-
|
235
|
+
start_time: #<DateTime: 2024-04-01T08:00:00-0700>,
|
236
|
+
end_time: #<DateTime: 2024-04-01T09:00:00-0700>,
|
237
237
|
},
|
238
238
|
{
|
239
|
-
start_time: #<DateTime: 2024-04-
|
240
|
-
end_time: #<DateTime: 2024-04-
|
239
|
+
start_time: #<DateTime: 2024-04-08T08:00:00-0700>,
|
240
|
+
end_time: #<DateTime: 2024-04-08T09:00:00-0700>,
|
241
241
|
},
|
242
242
|
{
|
243
|
-
start_time: #<DateTime: 2024-05-
|
244
|
-
end_time: #<DateTime: 2024-05-
|
243
|
+
start_time: #<DateTime: 2024-05-06T08:00:00-0700>,
|
244
|
+
end_time: #<DateTime: 2024-05-06T09:00:00-0700>,
|
245
245
|
},
|
246
246
|
{
|
247
|
-
start_time: #<DateTime: 2024-06-
|
248
|
-
end_time: #<DateTime: 2024-06-
|
247
|
+
start_time: #<DateTime: 2024-06-03T08:00:00-0700>,
|
248
|
+
end_time: #<DateTime: 2024-06-03T09:00:00-0700>,
|
249
249
|
},
|
250
250
|
]
|
251
251
|
```
|
252
252
|
|
253
|
-
|
254
|
-
As a Ruby dev, I want to generate slots for 8AM - 9AM on
|
253
|
+
### Example 6 - Specify nth day-of-week in month (ie. first Monday of the Month, second Tuesday of the Month, last Friday of Month)
|
254
|
+
As a Ruby dev, I want to generate slots for **8AM - 9AM** on the **first and second Mondays** and **last Fridays** of every month starting in June 2024. I can do this with the `nth_day_of_week_in_month` param.
|
255
|
+
|
256
|
+
```rb
|
257
|
+
Periodoxical.generate(
|
258
|
+
time_zone: 'America/Los_Angeles',
|
259
|
+
start_date: '2024-06-01',
|
260
|
+
limit: 5,
|
261
|
+
nth_day_of_week_in_month: {
|
262
|
+
mon: [1, 2], # valid values: -1,1,2,3,4,5
|
263
|
+
fri: [-1], # Use -1 to specify "last" of the month.
|
264
|
+
},
|
265
|
+
time_blocks: [
|
266
|
+
{ start_time: '8:00AM', end_time: '9:00AM' },
|
267
|
+
],
|
268
|
+
)
|
269
|
+
# =>
|
270
|
+
[
|
271
|
+
{
|
272
|
+
start: #<DateTime: 2024-06-03T08:00:00-0700>, # First Monday of June 2024
|
273
|
+
end: #<DateTime: 2024-06-03T09:00:00-0700>,
|
274
|
+
},
|
275
|
+
{
|
276
|
+
start: #<DateTime: 2024-06-10T08:00:00-0700>, # second Monday of June 2024
|
277
|
+
end: #<DateTime: 2024-06-10T09:00:00-0700>,
|
278
|
+
},
|
279
|
+
{
|
280
|
+
start: #<DateTime: 2024-06-28 08:00:00 -0700>, # last Friday of June 2024
|
281
|
+
end: #<DateTime: 2024-06-28 09:00:00 -0700>,
|
282
|
+
},
|
283
|
+
{
|
284
|
+
start: #<DateTime: 2024-07-01 08:00:00 -0700>, # First Monday of July 2024
|
285
|
+
end: #<DateTime: 2024-07-01 09:00:00 -0700>,
|
286
|
+
},
|
287
|
+
{
|
288
|
+
start: #<DateTime: 2024-07-08 08:00:00 -0700>, # Second Monday of July 2024
|
289
|
+
end: #<DateTime: 2024-07-08 09:00:00 -0700>,
|
290
|
+
},
|
291
|
+
]
|
292
|
+
```
|
293
|
+
|
294
|
+
### Example 7 - Exclude time blocks using the `exclusion_dates` parameter
|
295
|
+
As a Ruby dev, I want to generate slots for **8AM - 9AM** on **Mondays**, except for the **Monday of June 10, 2024**.
|
255
296
|
|
256
297
|
```rb
|
257
298
|
Periodoxical.generate(
|
@@ -287,6 +328,83 @@ Periodoxical.generate(
|
|
287
328
|
]
|
288
329
|
```
|
289
330
|
|
331
|
+
### Having Some Fun
|
332
|
+
|
333
|
+
Generate all the Friday the 13ths ever since May 1980 (when the first Friday the 13th film was released).
|
334
|
+
|
335
|
+
```rb
|
336
|
+
Periodoxical.generate(
|
337
|
+
time_zone: 'America/Los_Angeles',
|
338
|
+
start_date: '1980-05-01',
|
339
|
+
days_of_week: %w(fri),
|
340
|
+
days_of_month: [13],
|
341
|
+
limit: 100,
|
342
|
+
time_blocks: [
|
343
|
+
{ start_time: '11:00PM', end_time: '12:00AM' },
|
344
|
+
],
|
345
|
+
)
|
346
|
+
# =>
|
347
|
+
[
|
348
|
+
{
|
349
|
+
start: #<DateTime: 1980-06-13T23:00:00-0700>,
|
350
|
+
end: #<DateTime: 1980-06-13T00:00:00-0700>,
|
351
|
+
},
|
352
|
+
{
|
353
|
+
start: #<DateTime: 1981-02-13T23:00:00-0800>,
|
354
|
+
end: #<DateTime: 1981-02-13T00:00:00-0800>,
|
355
|
+
},
|
356
|
+
{
|
357
|
+
start: #<DateTime: 1981-03-13T23:00:00-0800>,
|
358
|
+
end: #<DateTime: 1981-03-13T00:00:00-0800>,
|
359
|
+
},
|
360
|
+
{
|
361
|
+
start: #<DateTime: 1981-11-13T23:00:00-0800>,
|
362
|
+
end: #<DateTime: 1981-11-13T00:00:00-0800>,
|
363
|
+
}
|
364
|
+
...
|
365
|
+
]
|
366
|
+
```
|
367
|
+
|
368
|
+
Generate the next 10 Thanksgivings from now on (Thanksgivings is defined as the 4th Thursday in November).
|
369
|
+
|
370
|
+
```rb
|
371
|
+
Periodoxical.generate(
|
372
|
+
time_zone: 'America/Los_Angeles',
|
373
|
+
start_date: '2024-05-01',
|
374
|
+
months: [11],
|
375
|
+
nth_day_of_week_in_month: {
|
376
|
+
thu: [4],
|
377
|
+
},
|
378
|
+
limit: 10,
|
379
|
+
time_blocks: [
|
380
|
+
{ start_time: '5:00PM', end_time: '6:00PM' },
|
381
|
+
],
|
382
|
+
)
|
383
|
+
#=>
|
384
|
+
[
|
385
|
+
{
|
386
|
+
start: #<DateTime: 2024-11-28T17:00:00-0800>,
|
387
|
+
end: #<DateTime: 2024-11-28T18:00:00-0800>,
|
388
|
+
},
|
389
|
+
{
|
390
|
+
start: #<DateTime: 2025-11-27T17:00:00-0800>,
|
391
|
+
end: #<DateTime: 2025-11-27T18:00:00-0800>,
|
392
|
+
},
|
393
|
+
{
|
394
|
+
start: #<DateTime: 2026-11-26T17:00:00-0800>,
|
395
|
+
end: #<DateTime: 2026-11-26T18:00:00-0800>,
|
396
|
+
},
|
397
|
+
{
|
398
|
+
start: #<DateTime: 2027-11-25T17:00:00-0800>,
|
399
|
+
end: #<DateTime: 2027-11-25T18:00:00-0800>,
|
400
|
+
},
|
401
|
+
{
|
402
|
+
start: #<DateTime: 2028-11-23T17:00:00-0800>,
|
403
|
+
end: #<DateTime: 2028-11-23T18:00:00-0800>,
|
404
|
+
}
|
405
|
+
]
|
406
|
+
```
|
407
|
+
|
290
408
|
## Development
|
291
409
|
|
292
410
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/periodoxical/version.rb
CHANGED
data/lib/periodoxical.rb
CHANGED
@@ -59,6 +59,7 @@ module Periodoxical
|
|
59
59
|
exclusion_dates: nil,
|
60
60
|
time_zone: 'Etc/UTC',
|
61
61
|
days_of_week: nil,
|
62
|
+
nth_day_of_week_in_month: nil,
|
62
63
|
days_of_month: nil,
|
63
64
|
weeks_of_month: nil,
|
64
65
|
months: nil
|
@@ -66,6 +67,7 @@ module Periodoxical
|
|
66
67
|
|
67
68
|
@time_zone = TZInfo::Timezone.get(time_zone)
|
68
69
|
@days_of_week = days_of_week
|
70
|
+
@nth_day_of_week_in_month = nth_day_of_week_in_month
|
69
71
|
@days_of_month = days_of_month
|
70
72
|
@weeks_of_month = weeks_of_month
|
71
73
|
@months = months
|
@@ -105,6 +107,10 @@ module Periodoxical
|
|
105
107
|
raise "`day_of_week_time_blocks` or `time_blocks` need to be provided"
|
106
108
|
end
|
107
109
|
|
110
|
+
if @days_of_week && @day_of_week_time_blocks
|
111
|
+
raise "`days_of_week` and `day_of_week_time_blocks` are both provided, which leads to ambiguity. Please use only one of these parameters."
|
112
|
+
end
|
113
|
+
|
108
114
|
if @weeks_of_month
|
109
115
|
@weeks_of_month.each do |wom|
|
110
116
|
unless wom.is_a?(Integer) && wom.between?(1, 5)
|
@@ -122,6 +128,32 @@ module Periodoxical
|
|
122
128
|
end
|
123
129
|
end
|
124
130
|
|
131
|
+
if @nth_day_of_week_in_month
|
132
|
+
@nth_day_of_week_in_month.keys.each do |day|
|
133
|
+
unless VALID_DAYS_OF_WEEK.include?(day.to_s)
|
134
|
+
raise "#{day} is not valid day of week format. Must be: #{VALID_DAYS_OF_WEEK}"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
@nth_day_of_week_in_month.each do |k,v|
|
138
|
+
unless v.is_a?(Array)
|
139
|
+
raise "nth_day_of_week_in_month parameter is invalid. Please look at the README for examples."
|
140
|
+
end
|
141
|
+
v.each do |num|
|
142
|
+
unless [-1,1,2,3,4,5].include?(num)
|
143
|
+
raise "nth_day_of_week_in_month parameter is invalid. Please look at the README for examples. "
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
if @days_of_week && @nth_day_of_week_in_month
|
150
|
+
# If both `days_of_week` and `nth_day_of_week_in_month` are provided for the same days-of-the-week, then it is ambiguous. (ie. I want this timeslot for every Monday, but also only for the first Mondays, well which one is it?)
|
151
|
+
overlapping_days = @days_of_week & @nth_day_of_week_in_month.keys.map(&:to_s)
|
152
|
+
unless overlapping_days.empty?
|
153
|
+
raise "#{overlapping_days} is specified in both `days_of_week` and also `nth_day_of_week_in_month`, which leads to ambiguity. Pleasee look at the README for examples."
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
125
157
|
if @day_of_week_time_blocks
|
126
158
|
@day_of_week_time_blocks.keys.each do |d|
|
127
159
|
unless VALID_DAYS_OF_WEEK.include?(d.to_s)
|
@@ -254,20 +286,51 @@ module Periodoxical
|
|
254
286
|
return false unless @days_of_month.include?(@current_date.day)
|
255
287
|
end
|
256
288
|
|
289
|
+
# The following conditions depend on the day-of-week of current_date.
|
290
|
+
day_of_week = day_of_week_long_to_short(@current_date.strftime("%A"))
|
291
|
+
|
257
292
|
# If days of week are specified, but current_date does not satisfy it,
|
258
293
|
# return false
|
259
294
|
if @days_of_week
|
260
|
-
day_of_week = day_of_week_long_to_short(@current_date.strftime("%A"))
|
261
295
|
return false unless @days_of_week.include?(day_of_week)
|
262
296
|
end
|
263
297
|
|
264
298
|
if @day_of_week_time_blocks
|
265
|
-
day_of_week = day_of_week_long_to_short(@current_date.strftime("%A"))
|
266
299
|
dowtb = @day_of_week_time_blocks[day_of_week.to_sym]
|
267
300
|
return false if dowtb.nil?
|
268
301
|
return false if dowtb.empty?
|
269
302
|
end
|
270
303
|
|
304
|
+
if @nth_day_of_week_in_month
|
305
|
+
# If the day of week is specified in nth_day_of_week_in_month,
|
306
|
+
# we need to investigate it whether or not to exclude it.
|
307
|
+
if @nth_day_of_week_in_month[day_of_week.to_sym]
|
308
|
+
n_occurence_of_day_of_week_in_month = ((@current_date.day - 1) / 7) + 1
|
309
|
+
# -1 is a special case and requires extra-math
|
310
|
+
if @nth_day_of_week_in_month[day_of_week.to_sym].include?(-1)
|
311
|
+
# We basically want to convert the -1 into its 'positive-equivalent` in this month, and compare it with that.
|
312
|
+
# For example, in June 2024, the Last Friday is also the 4th Friday. So in that case, we convert the -1 into a 4.
|
313
|
+
positivized_indices = @nth_day_of_week_in_month[day_of_week.to_sym].map { |indx| positivize_index(indx, day_of_week) }
|
314
|
+
return positivized_indices.include?(n_occurence_of_day_of_week_in_month)
|
315
|
+
else
|
316
|
+
return @nth_day_of_week_in_month[day_of_week.to_sym].include?(n_occurence_of_day_of_week_in_month)
|
317
|
+
end
|
318
|
+
else
|
319
|
+
# if day-of-week was not specified in nth_day_of_week_in_month,
|
320
|
+
# it could have been specified in either `days_of_week` or `day_of_week_time_blocks and we unfortunately need to re-check those here. I cant think of a way to further DRY-it up.
|
321
|
+
return false unless @days_of_week && @day_of_week_time_blocks
|
322
|
+
if @day_of_week
|
323
|
+
return false unless @days_of_week.include?(day_of_week)
|
324
|
+
end
|
325
|
+
|
326
|
+
if @day_of_week_time_blocks
|
327
|
+
dowtb = @day_of_week_time_blocks[day_of_week.to_sym]
|
328
|
+
return false if dowtb.nil?
|
329
|
+
return false if dowtb.empty?
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
271
334
|
# Otherwise, return true
|
272
335
|
true
|
273
336
|
end
|
@@ -289,5 +352,25 @@ module Periodoxical
|
|
289
352
|
end
|
290
353
|
end
|
291
354
|
end
|
355
|
+
|
356
|
+
# What is the positive index of the last day-of-week for the given month-year?
|
357
|
+
# For example, the last Friday in June 2024 is also the nth Friday. What is this n?
|
358
|
+
# @return [Integer]
|
359
|
+
def positivize_index(indx, day_of_week)
|
360
|
+
# If index is already positive, just return it
|
361
|
+
return indx if indx > 0
|
362
|
+
|
363
|
+
# get last_day_of month
|
364
|
+
month = @current_date.month
|
365
|
+
year = @current_date.year
|
366
|
+
last_date = Date.new(year, month, -1)
|
367
|
+
|
368
|
+
# walk backwords until you get to the right day of the week
|
369
|
+
while day_of_week_long_to_short(last_date.strftime("%A")) != day_of_week
|
370
|
+
last_date = last_date - 1
|
371
|
+
end
|
372
|
+
|
373
|
+
((last_date.day - 1) / 7) + 1
|
374
|
+
end
|
292
375
|
end
|
293
376
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: periodoxical
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steven Li
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-06-
|
11
|
+
date: 2024-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: tzinfo
|