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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1c90e3908492b944735cc23b5da95746309133de5b54413495d4ed49e0d2dce3
4
- data.tar.gz: 7fa63855a49e2c6feb9955007361aa6d8c9262f847e6fc0fc159c47f95ed1db5
3
+ metadata.gz: 01bdfec5ba5930f743cc1c4197e0964631c8dde5be1bb14c79082fe363bf72e1
4
+ data.tar.gz: 6aaaf40f368acbdd78632455889298e6eb6ced8c0ee80f4ed9e96387e7d05536
5
5
  SHA512:
6
- metadata.gz: 36ba01fb5173e0fae2dea0eeba563145b373860989891f2640b468ee688b0cdd5d78b7f5b44e7d52f8d137cd80940ebe8e9b75b997dc144d0d159982bd15aa87
7
- data.tar.gz: 5e678e7218811a7d5f0e779a2b766e2099742554414ee007078af87b0fc10f196900672c5fe0daa58b978ce1c4e31f9f08bfc21c0573f257f48823c211acdd74
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
- #### 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.
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
- #### Example 2
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
- #### Example 3 - using the `limit` key.
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
- #### Example 4 - when time blocks vary between days
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
- #### Example 5 - when specifying time blocks occur by day-of-month and/or and/or week-of-month and/or month.
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-05 08:00:00 -0700>,
205
- end: #<DateTime: 2024-06-05 09:00:00 -0700>,
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-10 08:00:00 -0700>,
209
- end: #<DateTime: 2024-06-10 09:00:00 -0700>,
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-05 08:00:00 -0700>,
213
- end: #<DateTime: 2024-07-05 09:00:00 -0700>,
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-01 08:00:00 -0700>,
236
- end_time: #<DateTime: 2024-04-01 09:00:00 -0700>,
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-08 08:00:00 -0700>,
240
- end_time: #<DateTime: 2024-04-08 09:00:00 -0700>,
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-06 08:00:00 -0700>,
244
- end_time: #<DateTime: 2024-05-06 09:00:00 -0700>,
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-03 08:00:00 -0700>,
248
- end_time: #<DateTime: 2024-06-03 09:00:00 -0700>,
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
- #### Example 6 - Exclude time blocks using the `exclusion_dates` parameter
254
- As a Ruby dev, I want to generate slots for 8AM - 9AM on Mondays, except for the Monday of June 10, 2024.
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.
@@ -1,3 +1,3 @@
1
1
  module Periodoxical
2
- VERSION = "0.6.2"
2
+ VERSION = "0.7.2"
3
3
  end
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.6.2
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-01 00:00:00.000000000 Z
11
+ date: 2024-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tzinfo