periodoxical 0.9.3 → 1.0.0

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: 1028e4261c9759e49e7446a1ff506fcb4d3b789b4bdd1ef40dbec0bd8df18ae8
4
- data.tar.gz: c7dece06fabe86ede77b98203bea7716d0d1e039393f1e6e693c8a232615ef01
3
+ metadata.gz: 3836bd7c49ec987409e70592d93cb8c021a009ce3587e48a6419c12121af3b1d
4
+ data.tar.gz: 510bb79570482c8019e911c8900ec15ee5016b59c1d51677d38d53f7c632523f
5
5
  SHA512:
6
- metadata.gz: '08dc98ece1a24ed33564c60f32c31fcf76defddbab51fbf2c0c3df04b296759767fc1dde8a4ed744a24b700fe812ce97bcdc06c052c382e61872c55426116258'
7
- data.tar.gz: c8edb8ea410b88c5888edda103752c27c149e4e06421b74fb7c4a29ed0f09ae32726536ed495702156b8e6e054da0a5140e1a3487bbbac4468abb94ed7cfb79f
6
+ metadata.gz: 0afbe7ebba86480f7d04766a087020734135bb0075245fbe6ba03f75590eb0c74710991b14e5fad653779ba7d6a74811d11adaeb7cfa28ec194dc9eea05adfb9
7
+ data.tar.gz: ddc843c9de7c3ae536f939f0c7bfc6c6d5ce2a37d4bde07d2941bd2ef7f7e0a41328d347fe1e9d8d1c1e1ba9bf916648424bd18795ad8642531d37529841d913
data/CODE_OF_CONDUCT.md CHANGED
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
55
55
  ## Enforcement
56
56
 
57
57
  Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
- reported by contacting the project team at steven@fountain.com. All
58
+ reported by contacting the project team at stevenJLi@gmail.com. All
59
59
  complaints will be reviewed and investigated and will result in a response that
60
60
  is deemed necessary and appropriate to the circumstances. The project team is
61
61
  obligated to maintain confidentiality with regard to the reporter of an incident.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- periodoxical (0.9.3)
4
+ periodoxical (1.0.0)
5
5
  tzinfo (~> 2.0, >= 2.0.0)
6
6
  week_of_month (= 1.2.6)
7
7
 
data/README.md CHANGED
@@ -41,8 +41,8 @@ Periodoxical.generate(
41
41
  end_time: '10:30AM'
42
42
  },
43
43
  ],
44
- start_date: '2024-05-23',
45
- end_date: '2024-05-27',
44
+ starting_from: '2024-05-23',
45
+ ending_at: '2024-05-26',
46
46
  )
47
47
  #=>
48
48
  [
@@ -65,6 +65,30 @@ Periodoxical.generate(
65
65
  ]
66
66
  ```
67
67
 
68
+ The `starting_from` and `ending_at` params can also accept datetimes in ISO 8601 format for more precision. This example generate all the datetime blocks of **9:00AM - 10:30AM** but starting from **May 23, 2024 at 9:30AM**.
69
+
70
+ ```rb
71
+ Periodoxical.generate(
72
+ time_zone: 'America/Los_Angeles',
73
+ time_blocks: [
74
+ {
75
+ start_time: '9:00AM',
76
+ end_time: '10:30AM'
77
+ },
78
+ ],
79
+ starting_from: '2024-05-23T09:30:00-07:00', # can be string in iso8601 format
80
+ ending_at: DateTime.parse('2024-05-26T17:00:00-07:00'), # or an instance of DateTime
81
+ )
82
+ #=> [
83
+ # 2024-05-23 was skipped because the 9AM timeslot was before the `starting_from` of '2024-05-23T09:30:00-07:00'
84
+ {
85
+ start_time: #<DateTime: 2024-05-24T09:00:00-0700>,
86
+ end_time: #<DateTime: 2024-05-24T10:30:00-0700>,
87
+ },
88
+ ...
89
+ ]
90
+ ```
91
+
68
92
  ### Example 2 - specify days of the week
69
93
  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
94
 
@@ -89,8 +113,8 @@ Periodoxical.generate(
89
113
  end_time: '2:30PM'
90
114
  }
91
115
  ],
92
- start_date: '2024-05-23',
93
- end_date: '2024-06-12',
116
+ starting_from: '2024-05-23',
117
+ ending_at: '2024-06-12',
94
118
  )
95
119
  # returns an array of hashes, each with :start and :end keys
96
120
  #=>
@@ -133,7 +157,7 @@ Periodoxical.generate(
133
157
  end_time: '2:30PM'
134
158
  }
135
159
  ],
136
- start_date: Date.parse('2024-05-23'), # Can also pass in `Date` object.
160
+ starting_from: Date.parse('2024-05-23'), # Can also pass in `Date` object.
137
161
  limit: 3
138
162
  )
139
163
  # =>
@@ -167,8 +191,8 @@ As a ruby dev, I want to generate all the timeblocks between **May 23, 2024** an
167
191
  ```rb
168
192
  Periodoxical.generate(
169
193
  time_zone: 'America/Los_Angeles',
170
- start_date: Date.parse('2024-05-23'), # can also pass in Date objects
171
- end_date: Date.parse('2024-06-12'), # can also pass in Date objects,
194
+ starting_from: Date.parse('2024-05-23'), # can also pass in Date objects
195
+ ending_at: Date.parse('2024-06-12'), # can also pass in Date objects,
172
196
  day_of_week_time_blocks: {
173
197
  mon: [
174
198
  { start_time: '8:00AM', end_time: '9:00AM' },
@@ -191,7 +215,7 @@ As a Ruby dev, I want to generate the next 3 timeblocks for **8AM - 9AM** for th
191
215
  ```rb
192
216
  Periodoxical.generate(
193
217
  time_zone: 'America/Los_Angeles',
194
- start_date: '2024-06-1',
218
+ starting_from: '2024-06-01',
195
219
  limit: 3,
196
220
  days_of_month: [5, 10],
197
221
  time_blocks: [
@@ -220,7 +244,7 @@ As a Ruby dev, I want to generate **4** timeblocks for **8AM - 9AM** on **Monday
220
244
  ```
221
245
  Periodoxical.generate(
222
246
  time_zone: 'America/Los_Angeles',
223
- start_date: '2024-04-1',
247
+ starting_from: '2024-04-01',
224
248
  limit: 4,
225
249
  weeks_of_month: [1 2],
226
250
  months: [4, 5, 6],
@@ -256,7 +280,7 @@ As a Ruby dev, I want to generate timeblocks for **8AM - 9AM** on the **first an
256
280
  ```rb
257
281
  Periodoxical.generate(
258
282
  time_zone: 'America/Los_Angeles',
259
- start_date: '2024-06-01',
283
+ starting_from: '2024-06-01',
260
284
  limit: 5,
261
285
  nth_day_of_week_in_month: {
262
286
  mon: [1, 2], # valid values: -1,1,2,3,4,5
@@ -297,7 +321,7 @@ As a Ruby dev, I want to generate timeblocks for **8AM - 9AM** on **Mondays**, e
297
321
  ```rb
298
322
  Periodoxical.generate(
299
323
  time_zone: 'America/Los_Angeles',
300
- start_date: '2024-06-3',
324
+ starting_from: '2024-06-03',
301
325
  limit: 4,
302
326
  exclusion_dates: %w(2024-06-10),
303
327
  day_of_week_time_blocks: {
@@ -333,7 +357,7 @@ As a Ruby dev, I want to generate timeblocks for **8AM - 9AM**, and **10AM - 11A
333
357
  ```rb
334
358
  Periodoxical.generate(
335
359
  time_zone: 'America/Los_Angeles',
336
- start_date: '2024-06-3',
360
+ starting_from: '2024-06-03',
337
361
  limit: 4,
338
362
  days_of_week: %(mon),
339
363
  time_blocks: [
@@ -393,11 +417,11 @@ This can be visualized as:
393
417
  ```rb
394
418
  Periodoxical.generate(
395
419
  time_zone: 'America/Los_Angeles',
396
- start_date: '2024-12-30',
420
+ starting_from: '2024-12-30',
397
421
  days_of_week: {
398
422
  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
423
+ tue: { every_other_nth: 2 }, # every other Tuesday starting at first Tuesday from `starting_from` date
424
+ wed: { every_other_nth: 3 }, # every 3rd Wednesday starting at first Wednesday from `starting_from` date
401
425
  },
402
426
  limit: 10,
403
427
  time_blocks: [
@@ -456,7 +480,7 @@ Generate all the Friday the 13ths ever since May 1980 (when the first Friday the
456
480
  ```rb
457
481
  Periodoxical.generate(
458
482
  time_zone: 'America/Los_Angeles',
459
- start_date: '1980-05-01',
483
+ starting_from: '1980-05-01',
460
484
  days_of_week: %w(fri),
461
485
  days_of_month: [13],
462
486
  limit: 100,
@@ -491,7 +515,7 @@ Generate the next 10 Thanksgivings from now on (Thanksgivings is defined as the
491
515
  ```rb
492
516
  Periodoxical.generate(
493
517
  time_zone: 'America/Los_Angeles',
494
- start_date: '2024-05-01',
518
+ starting_from: '2024-05-01',
495
519
  months: [11],
496
520
  nth_day_of_week_in_month: {
497
521
  thu: [4],
@@ -96,8 +96,8 @@ module Periodoxical
96
96
  end
97
97
  end
98
98
 
99
- unless( @limit || @end_date)
100
- raise "Either `limit` or `end_date` must be provided"
99
+ unless( @limit || @ending_at)
100
+ raise "Either `limit` or `ending_at` must be provided"
101
101
  end
102
102
 
103
103
  if @exclusion_times
@@ -1,3 +1,3 @@
1
1
  module Periodoxical
2
- VERSION = "0.9.3"
2
+ VERSION = "1.0.0"
3
3
  end
data/lib/periodoxical.rb CHANGED
@@ -17,8 +17,8 @@ module Periodoxical
17
17
  # @param [String] time_zone
18
18
  # Ex: 'America/Los_Angeles', 'America/Chicago',
19
19
  # TZInfo::DataTimezone#name from the tzinfo gem (https://github.com/tzinfo/tzinfo)
20
- # @param [Date, String] start_date
21
- # @param [Date, String] end_date
20
+ # @param [Date, String] starting_from
21
+ # @param [Date, String] ending_at
22
22
  # @param [Array<Hash>] time_blocks
23
23
  # Ex: [
24
24
  # {
@@ -40,7 +40,7 @@ module Periodoxical
40
40
  # @param [Array<Integer>, nil] months
41
41
  # Months as integers, where 1 = Jan, 12 = Dec
42
42
  # @param [Integer] limit
43
- # How many date times to generate. To be used when `end_date` is nil.
43
+ # How many date times to generate. To be used when `ending_at` is nil.
44
44
  # @param [Aray<String>] exclusion_dates
45
45
  # Dates to be excluded when generating the time blocks
46
46
  # Ex: ['2024-06-10', '2024-06-14']
@@ -65,8 +65,8 @@ module Periodoxical
65
65
  # fri: { start_time: '7:00PM', end_time: '9:00PM' },
66
66
  # }
67
67
  def initialize(
68
- start_date:,
69
- end_date: nil,
68
+ starting_from:,
69
+ ending_at: nil,
70
70
  time_blocks: nil,
71
71
  day_of_week_time_blocks: nil,
72
72
  limit: nil,
@@ -92,8 +92,8 @@ module Periodoxical
92
92
  @months = months
93
93
  @time_blocks = time_blocks
94
94
  @day_of_week_time_blocks = day_of_week_time_blocks
95
- @start_date = start_date.is_a?(String) ? Date.parse(start_date) : start_date
96
- @end_date = end_date.is_a?(String) ? Date.parse(end_date) : end_date
95
+ @starting_from = date_object_from(starting_from)
96
+ @ending_at = date_object_from(ending_at)
97
97
  @limit = limit
98
98
  @exclusion_dates = if exclusion_dates && !exclusion_dates.empty?
99
99
  exclusion_dates.map { |ed| Date.parse(ed) }
@@ -174,7 +174,11 @@ module Periodoxical
174
174
  # Variables which manage flow of looping through time and generating slots
175
175
  def initialize_looping_variables!
176
176
  @output = []
177
- @current_date = @start_date
177
+ if @starting_from.is_a?(DateTime)
178
+ @current_date = @starting_from.to_date
179
+ else
180
+ @current_date = @starting_from
181
+ end
178
182
  @current_day_of_week = day_of_week_long_to_short(@current_date.strftime("%A"))
179
183
  @current_count = 0
180
184
  @keep_generating = true
@@ -205,6 +209,7 @@ module Periodoxical
205
209
  def append_to_output_and_check_limit(time_block)
206
210
  # Check if this particular time is conflicts with any times from `exclusion_times`.
207
211
  return if overlaps_with_an_excluded_time?(time_block)
212
+ return if before_starting_from_or_after_ending_at?(time_block)
208
213
 
209
214
  @output << {
210
215
  start: time_str_to_object(@current_date, time_block[:start_time]),
@@ -224,7 +229,7 @@ module Periodoxical
224
229
 
225
230
  @current_day_of_week = day_of_week_long_to_short(@current_date.strftime("%A"))
226
231
 
227
- if @end_date && (@current_date > @end_date)
232
+ if @ending_at && (@current_date > @ending_at)
228
233
  @keep_generating = false
229
234
  end
230
235
 
@@ -232,7 +237,7 @@ module Periodoxical
232
237
  # there is bug, or poorly specified rules. If @current_date goes into
233
238
  # 1000 years in the future, but still no dates have been generated yet, this is
234
239
  # most likely an infinite loop situation, and needs to be killed.
235
- if @limit && ((@current_date - @start_date).to_i > 365000) && @output.empty?
240
+ if @limit && ((@current_date - @starting_from).to_i > 365000) && @output.empty?
236
241
  raise "No end condition detected, causing infinite loop. Please check rules/conditions or raise github issue for potential bug fixed"
237
242
  end
238
243
  end
@@ -324,8 +329,8 @@ module Periodoxical
324
329
  # end_time: '10:30AM'
325
330
  # },
326
331
  # ],
327
- # start_date: '2024-05-23',
328
- # end_date: '2024-05-27',
332
+ # starting_from: '2024-05-23',
333
+ # ending_at: '2024-05-27',
329
334
  # )
330
335
  # where if we don't specify any date-of-week/month constraints, we return all consecutive dates.
331
336
  # In the future, if we don't support this case, we can use `false` as the return value.
@@ -373,6 +378,29 @@ module Periodoxical
373
378
  @days_of_week_running_tally[@current_day_of_week.to_sym] = @days_of_week_running_tally[@current_day_of_week.to_sym] + 1
374
379
  end
375
380
 
381
+ # @return [Boolean]
382
+ # Used only when `starting_from` and `ending_at` are instances of DateTime
383
+ # instead of Date, requiring more precision, calculation.
384
+ def before_starting_from_or_after_ending_at?(time_block)
385
+ return false unless @starting_from.is_a?(DateTime) || @ending_at.is_a?(DateTime)
386
+
387
+ if @starting_from.is_a?(DateTime)
388
+ start_time = time_str_to_object(@current_date, time_block[:start_time])
389
+
390
+ # If the candidate time block is starting earlier than @starting_from, we want to skip it
391
+ return true if start_time < @starting_from
392
+ end
393
+
394
+ if @ending_at.is_a?(DateTime)
395
+ end_time = time_str_to_object(@current_date, time_block[:end_time])
396
+
397
+ # If the candidate time block is ending after @ending_at, we want to skip it
398
+ return true if end_time > @ending_at
399
+ end
400
+
401
+ false
402
+ end
403
+
376
404
  # @return [Boolean]
377
405
  # Whether or not the given `time_block` in the @current_date and
378
406
  # @time_zone overlaps with the times in `exclusion_times`.
@@ -410,5 +438,25 @@ module Periodoxical
410
438
 
411
439
  false
412
440
  end
441
+
442
+ def date_object_from(dt)
443
+ return unless dt
444
+ return dt if dt.is_a?(Date) || dt.is_a?(DateTime)
445
+
446
+ if dt.is_a?(String)
447
+ return Date.parse(dt) if /\A\d{4}-(0?[1-9]|1[0-2])-(0?[1-9]|[12]\d|3[01])\z/ =~ dt
448
+
449
+ if /\A\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?(Z|[+-][01]\d:[0-5]\d)?\z/ =~ dt
450
+ # convert to DateTime object
451
+ dt = DateTime.parse(dt)
452
+ # convert to given time_zone
453
+ return dt.to_time.localtime(@time_zone.utc_offset).to_datetime
454
+ end
455
+
456
+ raise "Could not parse date/datetime string #{dt}. Please README for examples."
457
+ else
458
+ raise "Invalid argument: #{dt}"
459
+ end
460
+ end
413
461
  end
414
462
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: periodoxical
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.3
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steven Li