periodoxical 0.7.3 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 63ed27b7ac9376b1cd35d505dbcf13c6637e47a8bee5983aa8697a389c2423ee
4
- data.tar.gz: df71967f5be8771b0bda25c4b6352f922a82bac39a546f2f66e04247a41ea3cb
3
+ metadata.gz: 1028e4261c9759e49e7446a1ff506fcb4d3b789b4bdd1ef40dbec0bd8df18ae8
4
+ data.tar.gz: c7dece06fabe86ede77b98203bea7716d0d1e039393f1e6e693c8a232615ef01
5
5
  SHA512:
6
- metadata.gz: ce019309287e5c8d065e77ce9cdc31348c12d867ba9140a43cfb2da2a086a647dcfd0ef0e1a801c11b41f3794fb0b056d695a7f079ad87e5e77e75bae1646bc2
7
- data.tar.gz: f8ae7b9cdc4e395618665f5b625624418bdc8d343fdc4e4dccd7aa8af9672671d2d02230d344193ac016ab1ada3468f3e9089b7fcc3b41a110f974d04c5f1486
6
+ metadata.gz: '08dc98ece1a24ed33564c60f32c31fcf76defddbab51fbf2c0c3df04b296759767fc1dde8a4ed744a24b700fe812ce97bcdc06c052c382e61872c55426116258'
7
+ data.tar.gz: c8edb8ea410b88c5888edda103752c27c149e4e06421b74fb7c4a29ed0f09ae32726536ed495702156b8e6e054da0a5140e1a3487bbbac4468abb94ed7cfb79f
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- periodoxical (0.7.3)
4
+ periodoxical (0.9.3)
5
5
  tzinfo (~> 2.0, >= 2.0.0)
6
6
  week_of_month (= 1.2.6)
7
7
 
data/README.md CHANGED
@@ -186,7 +186,7 @@ Periodoxical.generate(
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 timeblocks for **8AM - 9AM** for the **5th** and **10th** day of every month starting from **June**
190
190
 
191
191
  ```rb
192
192
  Periodoxical.generate(
@@ -215,7 +215,7 @@ Periodoxical.generate(
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, and June**
218
+ As a Ruby dev, I want to generate **4** timeblocks 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(
@@ -251,7 +251,7 @@ Periodoxical.generate(
251
251
  ```
252
252
 
253
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.
254
+ As a Ruby dev, I want to generate timeblocks 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
255
 
256
256
  ```rb
257
257
  Periodoxical.generate(
@@ -291,8 +291,8 @@ Periodoxical.generate(
291
291
  ]
292
292
  ```
293
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**.
294
+ ### Example 7 - Exclude time blocks using the `exclusion_dates` and `exclusion_times` parameters
295
+ As a Ruby dev, I want to generate timeblocks for **8AM - 9AM** on **Mondays**, except for the **Monday of June 10, 2024**.
296
296
 
297
297
  ```rb
298
298
  Periodoxical.generate(
@@ -328,6 +328,127 @@ Periodoxical.generate(
328
328
  ]
329
329
  ```
330
330
 
331
+ As a Ruby dev, I want to generate timeblocks for **8AM - 9AM**, and **10AM - 11AM** on **Mondays**, except for those that conflict (meaning overlap) with the time block of **10:30AM - 11:30AM** on the **Monday of June 10, 2024**. I can skip the conflicting time blocks by using the `exclusion_times` parameter.
332
+
333
+ ```rb
334
+ Periodoxical.generate(
335
+ time_zone: 'America/Los_Angeles',
336
+ start_date: '2024-06-3',
337
+ limit: 4,
338
+ days_of_week: %(mon),
339
+ time_blocks: [
340
+ { start_time: '8:00AM', end_time: '9:00AM' },
341
+ { start_time: '10:00AM', end_time: '11:00AM' },
342
+ ],
343
+ exclusion_times: [
344
+ {
345
+ start: '2024-06-10T10:30:00-07:00',
346
+ end: '2024-06-10T11:30:00-07:00',
347
+ }
348
+ ],
349
+ )
350
+ # =>
351
+ [
352
+ {
353
+ start: #<DateTime 2024-06-03T08:00:00-0700>,
354
+ end: #<DateTime 2024-06-03T09:00:00-0700>,
355
+ },
356
+ {
357
+ start: #<DateTime 2024-06-03T10:00:00-0700>,
358
+ end: #<DateTime 2024-06-03T11:00:00-0700>,
359
+ },
360
+ {
361
+ start: #<DateTime 2024-06-10T08:00:00-0700>,
362
+ end: #<DateTime 2024-06-10T09:00:00-0700>,
363
+ },
364
+ # The June 10 10AM - 11AM was skipped because it overlapped with the June 10 10:30AM - 11:30AM exclusion time.
365
+ {
366
+ start: #<DateTime 2024-06-17T08:00:00-0700>,
367
+ end: #<DateTime 2024-06-17T09:00:00-0700>,
368
+ },
369
+ {
370
+ start: #<DateTime 2024-06-17T10:00:00-0700>,
371
+ end: #<DateTime 2024-06-17T11:00:00-0700>,
372
+ },
373
+ {
374
+ start: #<DateTime 2024-06-24T08:00:00-0700>,
375
+ end: #<DateTime 2024-06-24T09:00:00-0700>,
376
+ },
377
+ ]
378
+ ```
379
+
380
+ ### Example 8 - Every-other-nth day-of-week rules (ie. every other Tuesday, every 3rd Wednesday, every 10th Friday)
381
+
382
+ As a Ruby dev, I want to generate timeblocks for **9AM- 10AM** on **every Monday**, but **every other Tuesday**, and **every other 3rd Wednesday**. I can do this using the `days_of_week` parameter, but also using the `every` and `every_other_nth` keys to specify the every-other-nth-rules.
383
+
384
+ This can be visualized as:
385
+
386
+ <div align="center">
387
+ <img width="600" alt="alt_google_cal_image" src="https://github.com/StevenJL/periodoxical/assets/2191808/d663da17-a94a-4715-886a-8223b129dd60">
388
+ <p><i>(image courtesy of calendar.google.com)</i></p>
389
+ </div>
390
+
391
+ <br>
392
+
393
+ ```rb
394
+ Periodoxical.generate(
395
+ time_zone: 'America/Los_Angeles',
396
+ start_date: '2024-12-30',
397
+ days_of_week: {
398
+ mon: { every: true }, # every Monday (no skipping)
399
+ tue: { every_other_nth: 2 }, # every other Tuesday starting at first Tuesday from start date
400
+ wed: { every_other_nth: 3 }, # every 3rd Wednesday starting at first Wednesday from start date
401
+ },
402
+ limit: 10,
403
+ time_blocks: [
404
+ { start_time: '9:00AM', end_time: '10:00AM' },
405
+ ],
406
+ )
407
+ #=>
408
+ [
409
+ {
410
+ start: #<DateTime: 2024-12-30T09:00:00-0800>,
411
+ end: #<DateTime: 2024-12-30T10:00:00-0800>,
412
+ },
413
+ {
414
+ start: #<DateTime: 2024-12-31T09:00:00-0800>,
415
+ end: #<DateTime: 2024-12-31T10:00:00-0800>,
416
+ },
417
+ {
418
+ start: #<DateTime: 2025-01-01T09:00:00-0800>,
419
+ end: #<DateTime: 2025-01-01T10:00:00-0800>,
420
+ },
421
+ {
422
+ start: #<DateTime: 2025-01-06T09:00:00-0800>,
423
+ end: #<DateTime: 2025-01-06T10:00:00-0800>,
424
+ },
425
+ {
426
+ start: #<DateTime: 2025-01-13T09:00:00-0800>,
427
+ end: #<DateTime: 2025-01-13T10:00:00-0800>,
428
+ },
429
+ {
430
+ start: #<DateTime: 2025-01-14T09:00:00-0800>,
431
+ end: #<DateTime: 2025-01-14T10:00:00-0800>,
432
+ },
433
+ {
434
+ start: #<DateTime: 2025-01-20T09:00:00-0800>,
435
+ end: #<DateTime: 2025-01-20T10:00:00-0800>,
436
+ },
437
+ {
438
+ start: #<DateTime: 2025-01-22T09:00:00-0800>,
439
+ end: #<DateTime: 2025-01-22T10:00:00-0800>,
440
+ },
441
+ {
442
+ start: #<DateTime: 2025-01-27T09:00:00-0800>,
443
+ end: #<DateTime: 2025-01-27T10:00:00-0800>,
444
+ },
445
+ {
446
+ start: #<DateTime: 2025-01-28T09:00:00-0800>,
447
+ end: #<DateTime: 2025-01-28T10:00:00-0800>,
448
+ }
449
+ ]
450
+ ```
451
+
331
452
  ### Having Some Fun
332
453
 
333
454
  Generate all the Friday the 13ths ever since May 1980 (when the first Friday the 13th film was released).
@@ -401,7 +522,8 @@ Periodoxical.generate(
401
522
  {
402
523
  start: #<DateTime: 2028-11-23T17:00:00-0800>,
403
524
  end: #<DateTime: 2028-11-23T18:00:00-0800>,
404
- }
525
+ },
526
+ ...
405
527
  ]
406
528
  ```
407
529
 
@@ -0,0 +1,112 @@
1
+ module Periodoxical
2
+ module Validation
3
+ VALID_DAYS_OF_WEEK = %w[mon tue wed thu fri sat sun].freeze
4
+ def validate!
5
+ unless @day_of_week_time_blocks || @time_blocks
6
+ raise "`day_of_week_time_blocks` or `time_blocks` need to be provided"
7
+ end
8
+
9
+ if (@days_of_week || @days_of_week_with_alternations) && @day_of_week_time_blocks
10
+ raise "`days_of_week` and `day_of_week_time_blocks` are both provided, which leads to ambiguity. Please use only one of these parameters."
11
+ end
12
+
13
+ if @weeks_of_month
14
+ @weeks_of_month.each do |wom|
15
+ unless wom.is_a?(Integer) && wom.between?(1, 5)
16
+ raise "weeks_of_month must be an array of integers between 1 and 5"
17
+ end
18
+ end
19
+ end
20
+
21
+ # days of week are valid
22
+ if @days_of_week
23
+ @days_of_week.each do |day|
24
+ unless VALID_DAYS_OF_WEEK.include?(day.to_s)
25
+ raise "#{day} is not valid day of week format. Must be: #{VALID_DAYS_OF_WEEK}"
26
+ end
27
+ end
28
+ end
29
+
30
+ if @days_of_week_with_alternations
31
+ @days_of_week_with_alternations.each do |dow, every_other|
32
+ unless VALID_DAYS_OF_WEEK.include?(dow.to_s)
33
+ raise "#{dow} is not valid day of week format. Must be: #{VALID_DAYS_OF_WEEK}"
34
+ end
35
+ unless every_other.is_a?(Hash)
36
+ raise "days_of_week parameter is not used correctly. Please look at examples in README."
37
+ end
38
+ unless every_other[:every] || every_other[:every_other_nth]
39
+ raise "days_of_week parameter is not used correctly. Please look at examples in README."
40
+ end
41
+ if every_other[:every_other_nth]
42
+ unless every_other[:every_other_nth].is_a?(Integer)
43
+ raise "days_of_week parameter is not used correctly. Please look at examples in README."
44
+ end
45
+
46
+ unless every_other[:every_other_nth] > 1
47
+ raise "days_of_week parameter is not used correctly. Please look at examples in README."
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ if @nth_day_of_week_in_month
54
+ @nth_day_of_week_in_month.keys.each do |day|
55
+ unless VALID_DAYS_OF_WEEK.include?(day.to_s)
56
+ raise "#{day} is not valid day of week format. Must be: #{VALID_DAYS_OF_WEEK}"
57
+ end
58
+ end
59
+ @nth_day_of_week_in_month.each do |k,v|
60
+ unless v.is_a?(Array)
61
+ raise "nth_day_of_week_in_month parameter is invalid. Please look at the README for examples."
62
+ end
63
+ v.each do |num|
64
+ unless [-1,1,2,3,4,5].include?(num)
65
+ raise "nth_day_of_week_in_month parameter is invalid. Please look at the README for examples. "
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ if @nth_day_of_week_in_month && (@days_of_week || @days_of_week_with_alternations || @day_of_week_time_blocks)
72
+ raise "nth_day_of_week_in_month parameter cannot be used in combination with `days_of_week` or `day_of_week_time_blocks`. Please look at the README for examples."
73
+ end
74
+
75
+ if @day_of_week_time_blocks
76
+ @day_of_week_time_blocks.keys.each do |d|
77
+ unless VALID_DAYS_OF_WEEK.include?(d.to_s)
78
+ raise "#{d} is not a valid day of week format. Must be #{VALID_DAYS_OF_WEEK}"
79
+ end
80
+ end
81
+ end
82
+
83
+ if @days_of_month
84
+ @days_of_month.each do |dom|
85
+ unless dom.is_a?(Integer) && dom.between?(1,31)
86
+ raise 'days_of_months must be array of integers between 1 and 31'
87
+ end
88
+ end
89
+ end
90
+
91
+ if @months
92
+ @months.each do |mon|
93
+ unless mon.is_a?(Integer) && mon.between?(1, 12)
94
+ raise 'months must be array of integers between 1 and 12'
95
+ end
96
+ end
97
+ end
98
+
99
+ unless( @limit || @end_date)
100
+ raise "Either `limit` or `end_date` must be provided"
101
+ end
102
+
103
+ if @exclusion_times
104
+ @exclusion_times.each do |tb|
105
+ unless tb[:start] < tb[:end]
106
+ raise "Exclusion times must have `start` before `end`. #{tb[:start]} not before #{tb[:end]}"
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -1,3 +1,3 @@
1
1
  module Periodoxical
2
- VERSION = "0.7.3"
2
+ VERSION = "0.9.3"
3
3
  end
data/lib/periodoxical.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "periodoxical/version"
2
+ require "periodoxical/validation"
2
3
  require "date"
3
4
  require "time"
4
5
  require "tzinfo"
@@ -12,7 +13,7 @@ module Periodoxical
12
13
  end
13
14
 
14
15
  class Core
15
- VALID_DAYS_OF_WEEK = %w[mon tue wed thu fri sat sun].freeze
16
+ include Periodoxical::Validation
16
17
  # @param [String] time_zone
17
18
  # Ex: 'America/Los_Angeles', 'America/Chicago',
18
19
  # TZInfo::DataTimezone#name from the tzinfo gem (https://github.com/tzinfo/tzinfo)
@@ -43,6 +44,19 @@ module Periodoxical
43
44
  # @param [Aray<String>] exclusion_dates
44
45
  # Dates to be excluded when generating the time blocks
45
46
  # Ex: ['2024-06-10', '2024-06-14']
47
+ # @param [Aray<Hash>] exclusion_times
48
+ # Timeblocks to be excluded when generating the time blocks if there is conflict (ie. overlap)
49
+ # Ex: [
50
+ # {
51
+ # start: '2024-06-10T10:30:00-07:00',
52
+ # end: '2024-06-10T11:30:00-07:00'
53
+ # },
54
+ # {
55
+ # start: '2024-06-10T14:30:00-07:00',
56
+ # end: '2024-06-10T15:30:00-07:00'
57
+ # },
58
+ # ]
59
+ #
46
60
  # @param [Hash<Array<Hash>>] day_of_week_time_blocks
47
61
  # To be used when hours are different between days of the week
48
62
  # Ex: {
@@ -57,6 +71,7 @@ module Periodoxical
57
71
  day_of_week_time_blocks: nil,
58
72
  limit: nil,
59
73
  exclusion_dates: nil,
74
+ exclusion_times: nil,
60
75
  time_zone: 'Etc/UTC',
61
76
  days_of_week: nil,
62
77
  nth_day_of_week_in_month: nil,
@@ -66,7 +81,11 @@ module Periodoxical
66
81
  )
67
82
 
68
83
  @time_zone = TZInfo::Timezone.get(time_zone)
69
- @days_of_week = days_of_week
84
+ if days_of_week.is_a?(Array)
85
+ @days_of_week = days_of_week
86
+ elsif days_of_week.is_a?(Hash)
87
+ @days_of_week_with_alternations = days_of_week
88
+ end
70
89
  @nth_day_of_week_in_month = nth_day_of_week_in_month
71
90
  @days_of_month = days_of_month
72
91
  @weeks_of_month = weeks_of_month
@@ -79,6 +98,11 @@ module Periodoxical
79
98
  @exclusion_dates = if exclusion_dates && !exclusion_dates.empty?
80
99
  exclusion_dates.map { |ed| Date.parse(ed) }
81
100
  end
101
+ @exclusion_times = if exclusion_times
102
+ exclusion_times.map do |et|
103
+ { start: DateTime.parse(et[:start]), end: DateTime.parse(et[:end]) }
104
+ end
105
+ end
82
106
  validate!
83
107
  end
84
108
 
@@ -106,83 +130,6 @@ module Periodoxical
106
130
 
107
131
  private
108
132
 
109
- def validate!
110
- unless @day_of_week_time_blocks || @time_blocks
111
- raise "`day_of_week_time_blocks` or `time_blocks` need to be provided"
112
- end
113
-
114
- if @days_of_week && @day_of_week_time_blocks
115
- raise "`days_of_week` and `day_of_week_time_blocks` are both provided, which leads to ambiguity. Please use only one of these parameters."
116
- end
117
-
118
- if @weeks_of_month
119
- @weeks_of_month.each do |wom|
120
- unless wom.is_a?(Integer) && wom.between?(1, 5)
121
- raise "weeks_of_month must be an array of integers between 1 and 5"
122
- end
123
- end
124
- end
125
-
126
- # days of week are valid
127
- if @days_of_week
128
- @days_of_week.each do |day|
129
- unless VALID_DAYS_OF_WEEK.include?(day.to_s)
130
- raise "#{day} is not valid day of week format. Must be: #{VALID_DAYS_OF_WEEK}"
131
- end
132
- end
133
- end
134
-
135
- if @nth_day_of_week_in_month
136
- @nth_day_of_week_in_month.keys.each do |day|
137
- unless VALID_DAYS_OF_WEEK.include?(day.to_s)
138
- raise "#{day} is not valid day of week format. Must be: #{VALID_DAYS_OF_WEEK}"
139
- end
140
- end
141
- @nth_day_of_week_in_month.each do |k,v|
142
- unless v.is_a?(Array)
143
- raise "nth_day_of_week_in_month parameter is invalid. Please look at the README for examples."
144
- end
145
- v.each do |num|
146
- unless [-1,1,2,3,4,5].include?(num)
147
- raise "nth_day_of_week_in_month parameter is invalid. Please look at the README for examples. "
148
- end
149
- end
150
- end
151
- end
152
-
153
- if @nth_day_of_week_in_month && (@days_of_week || @day_of_week_time_blocks)
154
- raise "nth_day_of_week_in_month parameter cannot be used in combination with `days_of_week` or `day_of_week_time_blocks`. Please look at the README for examples."
155
- end
156
-
157
- if @day_of_week_time_blocks
158
- @day_of_week_time_blocks.keys.each do |d|
159
- unless VALID_DAYS_OF_WEEK.include?(d.to_s)
160
- raise "#{d} is not a valid day of week format. Must be #{VALID_DAYS_OF_WEEK}"
161
- end
162
- end
163
- end
164
-
165
- if @days_of_month
166
- @days_of_month.each do |dom|
167
- unless dom.is_a?(Integer) && dom.between?(1,31)
168
- raise 'days_of_months must be array of integers between 1 and 31'
169
- end
170
- end
171
- end
172
-
173
- if @months
174
- @months.each do |mon|
175
- unless mon.is_a?(Integer) && mon.between?(1, 12)
176
- raise 'months must be array of integers between 1 and 12'
177
- end
178
- end
179
- end
180
-
181
- unless( @limit || @end_date)
182
- raise "Either `limit` or `end_date` must be provided"
183
- end
184
- end
185
-
186
133
  def day_of_week_long_to_short(dow)
187
134
  {
188
135
  "Monday" => "mon",
@@ -231,6 +178,21 @@ module Periodoxical
231
178
  @current_day_of_week = day_of_week_long_to_short(@current_date.strftime("%A"))
232
179
  @current_count = 0
233
180
  @keep_generating = true
181
+ # When there are alternations in days of week
182
+ # (ie. every other Monday, every 3rd Thursday, etc).
183
+ # We keep running tally of day_of_week counts and use modulo-math to pick out
184
+ # every n-th one.
185
+ if @days_of_week_with_alternations
186
+ @days_of_week_running_tally = {
187
+ mon: 0,
188
+ tue: 0,
189
+ wed: 0,
190
+ thu: 0,
191
+ fri: 0,
192
+ sat: 0,
193
+ sun: 0,
194
+ }
195
+ end
234
196
  end
235
197
 
236
198
  # @param [Hash] time_block
@@ -241,6 +203,9 @@ module Periodoxical
241
203
  # }
242
204
  # Generates time block but also checks if we should stop generating
243
205
  def append_to_output_and_check_limit(time_block)
206
+ # Check if this particular time is conflicts with any times from `exclusion_times`.
207
+ return if overlaps_with_an_excluded_time?(time_block)
208
+
244
209
  @output << {
245
210
  start: time_str_to_object(@current_date, time_block[:start_time]),
246
211
  end: time_str_to_object(@current_date, time_block[:end_time])
@@ -256,6 +221,7 @@ module Periodoxical
256
221
 
257
222
  def advance_current_date_and_check_if_reached_end_date
258
223
  @current_date = @current_date + 1
224
+
259
225
  @current_day_of_week = day_of_week_long_to_short(@current_date.strftime("%A"))
260
226
 
261
227
  if @end_date && (@current_date > @end_date)
@@ -302,6 +268,28 @@ module Periodoxical
302
268
  return false unless @days_of_week.include?(@current_day_of_week)
303
269
  end
304
270
 
271
+ if @days_of_week_with_alternations
272
+ # current_date is not specified in days_of_week, so skip it
273
+ return false if @days_of_week_with_alternations[@current_day_of_week.to_sym].nil?
274
+
275
+ alternating_spec = @days_of_week_with_alternations[@current_day_of_week.to_sym]
276
+
277
+ # In the { every: true } case, we don't check the alternations logic, we just add it.
278
+ unless alternating_spec[:every]
279
+ # We are now specifying every other nth occurrence (ie. every 2nd Tuesday, every 3rd Wednesday)
280
+ alternating_frequency = alternating_spec[:every_other_nth]
281
+
282
+ unless (@days_of_week_running_tally[@current_day_of_week.to_sym] % alternating_frequency) == 0
283
+ # If day-of-week alternations are present, we need to keep track of day-of-weeks
284
+ # we have encountered and added or would have added so far.
285
+ update_days_of_week_running_tally!
286
+
287
+ return false
288
+ end
289
+ update_days_of_week_running_tally!
290
+ end
291
+ end
292
+
305
293
  if @day_of_week_time_blocks
306
294
  dowtb = @day_of_week_time_blocks[@current_day_of_week.to_sym]
307
295
  return false if dowtb.nil?
@@ -327,7 +315,20 @@ module Periodoxical
327
315
  end
328
316
  end
329
317
 
330
- # Otherwise, return true
318
+ # The default return true is really only needed to support this use-case:
319
+ # Periodoxical.generate(
320
+ # time_zone: 'America/Los_Angeles',
321
+ # time_blocks: [
322
+ # {
323
+ # start_time: '9:00AM',
324
+ # end_time: '10:30AM'
325
+ # },
326
+ # ],
327
+ # start_date: '2024-05-23',
328
+ # end_date: '2024-05-27',
329
+ # )
330
+ # where if we don't specify any date-of-week/month constraints, we return all consecutive dates.
331
+ # In the future, if we don't support this case, we can use `false` as the return value.
331
332
  true
332
333
  end
333
334
 
@@ -367,5 +368,47 @@ module Periodoxical
367
368
 
368
369
  ((last_date.day - 1) / 7) + 1
369
370
  end
371
+
372
+ def update_days_of_week_running_tally!
373
+ @days_of_week_running_tally[@current_day_of_week.to_sym] = @days_of_week_running_tally[@current_day_of_week.to_sym] + 1
374
+ end
375
+
376
+ # @return [Boolean]
377
+ # Whether or not the given `time_block` in the @current_date and
378
+ # @time_zone overlaps with the times in `exclusion_times`.
379
+ def overlaps_with_an_excluded_time?(time_block)
380
+ return false unless @exclusion_times
381
+
382
+ @exclusion_times.each do |exclusion_timeblock|
383
+ return true if overlap?(
384
+ exclusion_timeblock,
385
+ {
386
+ start: time_str_to_object(@current_date, time_block[:start_time]),
387
+ end: time_str_to_object(@current_date, time_block[:end_time]),
388
+ }
389
+ )
390
+ end
391
+
392
+ false
393
+ end
394
+
395
+ # @param [Hash] time_block_1, time_block_2
396
+ # Ex: {
397
+ # start: #<DateTime>,
398
+ # end: #<DateTime>,
399
+ # }
400
+ def overlap?(time_block_1, time_block_2)
401
+ tb_1_start = time_block_1[:start]
402
+ tb_1_end = time_block_1[:end]
403
+ tb_2_start = time_block_2[:start]
404
+ tb_2_end = time_block_2[:end]
405
+
406
+ # Basicall overlap is when one starts before the other has ended
407
+ return true if tb_1_end > tb_2_start && tb_1_end < tb_2_end
408
+ # By symmetry
409
+ return true if tb_2_end > tb_1_start && tb_2_end < tb_1_end
410
+
411
+ false
412
+ end
370
413
  end
371
414
  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.7.3
4
+ version: 0.9.3
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-02 00:00:00.000000000 Z
11
+ date: 2024-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tzinfo
@@ -135,6 +135,7 @@ files:
135
135
  - bin/console
136
136
  - bin/setup
137
137
  - lib/periodoxical.rb
138
+ - lib/periodoxical/validation.rb
138
139
  - lib/periodoxical/version.rb
139
140
  - periodoxical.gemspec
140
141
  homepage: https://github.com/StevenJL/periodoxical