opening_hours_converter 0.0.6 → 1.0.0

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.
@@ -1,5 +1,8 @@
1
+ require 'opening_hours_converter/constants'
2
+
1
3
  module OpeningHoursConverter
2
4
  class OpeningHoursRule
5
+ include Constants
3
6
  attr_accessor :date, :time
4
7
 
5
8
  def initialize
@@ -8,14 +11,10 @@ module OpeningHoursConverter
8
11
  end
9
12
 
10
13
  def get
14
+ get_wide_selector if @date.length > 1
11
15
  result = ""
12
- if @date.length > 1 || @date[0]&.wide != ""
13
- @date.each_with_index do |d, i|
14
- if (i > 0)
15
- result += ","
16
- end
17
- result += d.wide
18
- end
16
+ if @date.length > 0
17
+ result += get_wide_selector
19
18
  end
20
19
 
21
20
  if @date.length > 0
@@ -36,13 +35,493 @@ module OpeningHoursConverter
36
35
  else
37
36
  result += " off"
38
37
  end
39
- if result.strip == "00:00-24:00"
40
- result = "24/7"
38
+
39
+ rgx_day = /(Mo|Tu|We|Th|Fr|Sa|Su)/
40
+
41
+ if result.strip == "00:00-24:00" || (!(result =~ /00:00-24:00/).nil? && (result =~ rgx_day).nil?)
42
+ result.gsub!("00:00-24:00", "24/7")
41
43
  end
42
44
 
45
+
46
+ result = clean(result)
43
47
  result.strip
44
48
  end
45
49
 
50
+ def get_wide_selector
51
+
52
+ years = build_day_array
53
+
54
+ year_start = -1
55
+ month_start = -1
56
+ day_start = -1
57
+
58
+ result = {}
59
+
60
+ if !years["always"].nil?
61
+
62
+ always = years.delete("always")
63
+
64
+ always.each_with_index do |month_array, month|
65
+ month_array.each_with_index do |day_bool, day|
66
+ if day_bool && month_start < 0
67
+ month_start = month
68
+ day_start = day
69
+ elsif day_bool && month_start >= 0 && month == 11 && day == 30
70
+ result["always"] ||= []
71
+ result["always"] << {start: { day: day_start, month: month_start }, end: { day: 30, month: 11 }}
72
+ month_start = -1
73
+ day_start = -1
74
+ elsif !day_bool && month_start >= 0
75
+ result["always"] ||= []
76
+ end_res = day == 0 ?
77
+ month == 0 ?
78
+ { day: 30, month: 11 } : { day: MONTH_END_DAY[month-1]-1, month: month-1 } :
79
+ { day: day-1, month: month }
80
+ result["always"] << { start: { day: day_start, month: month_start }, end: end_res }
81
+ month_start = -1
82
+ day_start = -1
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ # years {
89
+ # 2017: [
90
+ # {start: {}, end: {}}
91
+ # ],
92
+ # multi_year: [
93
+ # {start: {}, {}}
94
+ # ]
95
+ # }
96
+
97
+ years.each do |year, months|
98
+ months.each_with_index do |month_array, month|
99
+ month_array.each_with_index do |day_bool, day|
100
+ if day_bool && year_start < 0
101
+ year_start = year
102
+ month_start = month
103
+ day_start = day
104
+ elsif day_bool && year_start >= 0 && month == 11 && day == 30 && years[year+1].nil?
105
+ if year_start == year
106
+ result[year] ||= []
107
+ result[year] << { start: { day: day_start, month: month_start }, end: { day: 30, month: 11 } }
108
+ else
109
+ result["multi_year"] ||= {}
110
+ result["multi_year"]["#{year_start}-#{year}"] ||= []
111
+ result["multi_year"]["#{year_start}-#{year}"] << { start: { day: day_start, month: month_start, year: year_start },
112
+ end: { day: 30, month: 11, year: year } }
113
+ end
114
+ year_start = -1
115
+ month_start = -1
116
+ day_start = -1
117
+ elsif !day_bool && year_start >= 0
118
+ end_res = {}
119
+
120
+ if day == 0
121
+ if month == 0
122
+ end_res = { day: 30, month: 11 }
123
+ else
124
+ end_res = { day: MONTH_END_DAY[month-1]-1, month: month-1 }
125
+ end
126
+ else
127
+ end_res = { day: day-1, month: month }
128
+ end
129
+
130
+ if year_start == year
131
+ result[year] ||= []
132
+ result[year] << { start: { day: day_start, month: month_start }, end: end_res }
133
+ else
134
+ result["multi_year"] ||= {}
135
+ result["multi_year"]["#{year_start}-#{year}"] ||= []
136
+ end_res[:year] = year
137
+ result["multi_year"]["#{year_start}-#{year}"] << { start: { day: day_start, month: month_start, year: year_start },
138
+ end: end_res }
139
+ end
140
+ # end_res = day == 0 ?
141
+ # month == 0 ?
142
+ # { day: 30, month: 11, year: year } : { day: MONTH_END_DAY[month-1]-1, month: month-1, year: year } :
143
+ # { day: day-1, month: month, year: year }
144
+ # result << {start: {day: day_start, month: month_start, year: year_start}, end: end_res}
145
+ year_start = -1
146
+ month_start = -1
147
+ day_start = -1
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+
154
+ result_to_string(result)
155
+
156
+ end
157
+
158
+ def result_to_string(result)
159
+ str_result = ""
160
+ result.each do |selector, intervals|
161
+ if selector == "always"
162
+ intervals.each do |interval|
163
+ if str_result.length > 0
164
+ str_result += ","
165
+ end
166
+ if is_full_year?(interval)
167
+ elsif is_full_month?(interval)
168
+ str_result += "#{OSM_MONTHS[interval[:start][:month]]}"
169
+ elsif starts_month?(interval) && ends_month?(interval)
170
+ str_result += "#{OSM_MONTHS[interval[:start][:month]]}-#{OSM_MONTHS[interval[:end][:month]]}"
171
+ elsif is_same_month?(interval)
172
+ str_result += "#{OSM_MONTHS[interval[:start][:month]]} #{interval[:start][:day]+1 < 10 ? "0#{interval[:start][:day]+1}" : interval[:start][:day]+1}-#{interval[:end][:day]+1 < 10 ? "0#{interval[:end][:day]+1}" : interval[:end][:day]+1}"
173
+ else
174
+ str_result += "#{OSM_MONTHS[interval[:start][:month]]} #{interval[:start][:day]+1 < 10 ? "0#{interval[:start][:day]+1}" : interval[:start][:day]+1}-#{OSM_MONTHS[interval[:end][:month]]} #{interval[:end][:day]+1 < 10 ? "0#{interval[:end][:day]+1}" : interval[:end][:day]+1}"
175
+ end
176
+ end
177
+ elsif selector == "multi_year"
178
+ intervals.each do |years, intervals|
179
+ intervals.each do |interval|
180
+ if str_result.length > 0
181
+ str_result += ","
182
+ end
183
+ if starts_year?(interval) && ends_year?(interval)
184
+ str_result += "#{interval[:start][:year]}-#{interval[:end][:year]}"
185
+ elsif starts_month?(interval) && ends_month?(interval)
186
+ str_result += "#{interval[:start][:year]} #{OSM_MONTHS[interval[:start][:month]]}-#{interval[:end][:year]} #{OSM_MONTHS[interval[:end][:month]]}"
187
+ else
188
+ str_result += "#{interval[:start][:year]} #{OSM_MONTHS[interval[:start][:month]]} #{interval[:start][:day]+1 < 10 ? "0#{interval[:start][:day]+1}" : interval[:start][:day]+1}-#{interval[:end][:year]} #{OSM_MONTHS[interval[:end][:month]]} #{interval[:end][:day]+1 < 10 ? "0#{interval[:end][:day]+1}" : interval[:end][:day]+1}"
189
+ end
190
+ end
191
+ end
192
+ else
193
+ str_result += "#{selector} "
194
+ intervals.each do |interval|
195
+ if is_full_year?(interval)
196
+ elsif is_full_month?(interval)
197
+ str_result += "#{str_result.length > 5 ? "," : ""}#{OSM_MONTHS[interval[:start][:month]]}"
198
+ elsif starts_month?(interval) && ends_month?(interval)
199
+ str_result += "#{str_result.length > 5 ? "," : ""}#{OSM_MONTHS[interval[:start][:month]]}-#{OSM_MONTHS[interval[:end][:month]]}"
200
+ elsif is_same_month?(interval)
201
+ str_result += "#{str_result.length > 5 ? "," : ""}#{OSM_MONTHS[interval[:start][:month]]} #{interval[:start][:day]+1 < 10 ? "0#{interval[:start][:day]+1}" : interval[:start][:day]+1}-#{interval[:end][:day]+1 < 10 ? "0#{interval[:end][:day]+1}" : interval[:end][:day]+1}"
202
+ else
203
+ str_result += "#{str_result.length > 5 ? "," : ""}#{OSM_MONTHS[interval[:start][:month]]} #{interval[:start][:day]+1 < 10 ? "0#{interval[:start][:day]+1}" : interval[:start][:day]+1}-#{OSM_MONTHS[interval[:end][:month]]} #{interval[:end][:day]+1 < 10 ? "0#{interval[:end][:day]+1}" : interval[:end][:day]+1}"
204
+ end
205
+ end
206
+ end
207
+ end
208
+
209
+ str_result.strip
210
+ end
211
+
212
+ def is_full_year?(r)
213
+ starts_year?(r) && ends_year?(r)
214
+ end
215
+
216
+ def is_full_month?(r)
217
+ is_same_month?(r) && starts_month?(r) && ends_month?(r)
218
+ end
219
+
220
+ def is_same_year?(r)
221
+ r[:start][:year] == r[:end][:year]
222
+ end
223
+
224
+ def is_same_month?(r)
225
+ r[:start][:month] == r[:end][:month]
226
+ end
227
+
228
+ def starts_month?(r)
229
+ r[:start][:day] == 0
230
+ end
231
+
232
+ def ends_month?(r)
233
+ r[:end][:day] == MONTH_END_DAY[r[:end][:month]] - 1
234
+ end
235
+
236
+ def starts_year?(r)
237
+ r[:start][:month] == 0 && starts_month?(r)
238
+ end
239
+
240
+ def ends_year?(r)
241
+ r[:end][:month] == 11 && ends_month?(r)
242
+ end
243
+
244
+ def build_day_array
245
+ years = {}
246
+ @date.each do |date|
247
+ if !date.wide.start.nil? && !date.wide.start[:year].nil?
248
+ if date.wide.end.nil? || date.wide.end[:year].nil? || date.wide.start[:year] == date.wide.end[:year]
249
+ if !years[date.wide.start[:year]].nil?
250
+ years = process_single_year(date, years)
251
+ else
252
+ years[date.wide.start[:year]] = Array.new(OSM_MONTHS.length) { |i| Array.new(MONTH_END_DAY[i]) { false } }
253
+ years = process_single_year(date, years)
254
+ end
255
+ else
256
+ for year in date.wide.start[:year]..date.wide.end[:year]
257
+ years[year] ||= Array.new(OSM_MONTHS.length) { |i| Array.new(MONTH_END_DAY[i]) { false } }
258
+ end
259
+ process_multiple_years(date, years)
260
+ end
261
+ else
262
+ years["always"] ||= Array.new(OSM_MONTHS.length) { |i| Array.new(MONTH_END_DAY[i]) { false } }
263
+ years = process_always(date, years)
264
+ end
265
+ end
266
+ years
267
+ end
268
+
269
+ def process_always(date, years, get_iterator=false)
270
+ if !get_iterator
271
+ if date.wide.start.nil?
272
+ years["always"] = Array.new(OSM_MONTHS.length) { |i| Array.new(MONTH_END_DAY[i]) { true } }
273
+ elsif !date.wide.start[:day].nil?
274
+ if date.wide.end.nil? || (date.wide.end[:month].nil? && date.wide.end[:day].nil?) ||
275
+ (date.wide.start[:month] == date.wide.end[:month] && date.wide.start[:day] == date.wide.end[:day])
276
+ years["always"][date.wide.start[:month]-1][date.wide.start[:day]-1] = true
277
+ elsif date.wide.start[:month] == date.wide.end[:month]
278
+ for day in date.wide.start[:day]-1..date.wide.end[:day]-1
279
+ years["always"][date.wide.start[:month]-1][day] = true
280
+ end
281
+ elsif date.wide.start[:month] != date.wide.end[:month]
282
+ for month in date.wide.start[:month]-1..date.wide.end[:month]-1
283
+ if month == date.wide.start[:month]-1
284
+ for day in date.wide.start[:day]-1...MONTH_END_DAY[month]
285
+ years["always"][month][day] = true
286
+ end
287
+ elsif month == date.wide.end[:month]-1
288
+ for day in 0..date.wide.end[:day]-1
289
+ years["always"][month][day] = true
290
+ end
291
+ else
292
+ for day in 0...MONTH_END_DAY[month]
293
+ years["always"][month][day] = true
294
+ end
295
+ end
296
+ end
297
+ end
298
+ elsif !date.wide.start[:month].nil?
299
+ if date.wide.end.nil? || date.wide.end[:month].nil? || date.wide.start[:month] == date.wide.end[:month]
300
+ years["always"][date.wide.start[:month]-1].each_with_index do |month, i|
301
+ years["always"][date.wide.start[:month]-1][i] = true
302
+ end
303
+ else
304
+ for month in date.wide.start[:month]-1..date.wide.end[:month]-1
305
+ years["always"][month].each_with_index do |day, i|
306
+ years["always"][month][i] = true
307
+ end
308
+ end
309
+ end
310
+ end
311
+ else
312
+ for year in YEAR_MIN..YEAR_MAX
313
+ if years[year].nil?
314
+ years[year] = Array.new(OSM_MONTHS.length) { |i| Array.new(MONTH_END_DAY[i]) { false } }
315
+ end
316
+ if !date.wide.start[:day].nil?
317
+ if date.wide.end.nil? || (date.wide.end[:month].nil? && date.wide.end[:day].nil?) ||
318
+ (date.wide.start[:month] == date.wide.end[:month] && date.wide.start[:day] == date.wide.end[:day])
319
+ years[year][date.wide.start[:month]-1][date.wide.start[:day]-1] = true
320
+ elsif date.wide.start[:month] == date.wide.end[:month]
321
+ for day in date.wide.start[:day]-1..date.wide.end[:day]-1
322
+ years[year][date.wide.start[:month]-1][day] = true
323
+ end
324
+ elsif date.wide.start[:month] != date.wide.end[:month]
325
+ for month in date.wide.start[:month]-1..date.wide.end[:month]-1
326
+ if month == date.wide.start[:month]-1
327
+ for day in date.wide.start[:day]-1...MONTH_END_DAY[month]
328
+ years[year][month][day] = true
329
+ end
330
+ elsif month == date.wide.end[:month]-1
331
+ for day in 0..date.wide.end[:day]-1
332
+ years[year][month][day] = true
333
+ end
334
+ else
335
+ for day in 0...MONTH_END_DAY[month]
336
+ years[year][month][day] = true
337
+ end
338
+ end
339
+ end
340
+ end
341
+ elsif !date.wide.start[:month].nil?
342
+ if date.wide.end.nil? || date.wide.end[:month].nil? || date.wide.start[:month] == date.wide.end[:month]
343
+ years[year][date.wide.start[:month]-1].each_with_index do |month, i|
344
+ years[year][date.wide.start[:month]-1][i] = true
345
+ end
346
+ else
347
+ for month in date.wide.start[:month]-1..date.wide.end[:month]-1
348
+ years[year][month].each_with_index do |day, i|
349
+ years[year][month][i] = true
350
+ end
351
+ end
352
+ end
353
+ end
354
+ end
355
+ end
356
+ return years
357
+ end
358
+
359
+ def process_multiple_years(date, years)
360
+ if date.wide.type == "year"
361
+ for year in date.wide.start[:year]..date.wide.end[:year]
362
+ years[year].each_with_index do |month,i|
363
+ month.each_with_index do |day,j|
364
+ years[year][i][j] = true
365
+ end
366
+ end
367
+ end
368
+ elsif date.wide.type == "month"
369
+ for year in date.wide.start[:year]..date.wide.end[:year]
370
+ if year == date.wide.start[:year]
371
+ for month in date.wide.start[:month]-1..11
372
+ years[year][month].each_with_index do |day, i|
373
+ years[year][month][i] = true
374
+ end
375
+ end
376
+ elsif year == date.wide.end[:year]
377
+ for month in 0..date.wide.end[:month]-1
378
+ years[year][month].each_with_index do |day, i|
379
+ years[year][month][i] = true
380
+ end
381
+ end
382
+ else
383
+ for month in 0..11
384
+ years[year][month].each_with_index do |day, i|
385
+ years[year][month][i] = true
386
+ end
387
+ end
388
+ end
389
+ end
390
+ elsif date.wide.type == "day"
391
+ for year in date.wide.start[:year]..date.wide.end[:year]
392
+ if year == date.wide.start[:year]
393
+ for month in date.wide.start[:month]-1..11
394
+ if month == date.wide.start[:month]-1
395
+ for day in date.wide.start[:day]-1...MONTH_END_DAY[month]
396
+ years[year][month][day] = true
397
+ end
398
+ else
399
+ for day in 0...MONTH_END_DAY[month]
400
+ years[year][month][day] = true
401
+ end
402
+ end
403
+ end
404
+ elsif year == date.wide.end[:year]
405
+ for month in 0..date.wide.end[:month]-1
406
+ if month == date.wide.end[:month]-1
407
+ for day in 0..date.wide.end[:day]-1
408
+ years[year][month][day] = true
409
+ end
410
+ else
411
+ for day in 0...MONTH_END_DAY[month]
412
+ years[year][month][day] = true
413
+ end
414
+ end
415
+ end
416
+ else
417
+ for month in 0..11
418
+ for day in 0...MONTH_END_DAY[month]
419
+ years[year][month][day] = true
420
+ end
421
+ end
422
+ end
423
+ end
424
+ end
425
+ return years
426
+ end
427
+
428
+ def process_single_year(date, years, always=false)
429
+ if date.wide.type == "year"
430
+ years[date.wide.start[:year]].each_with_index do |month,i|
431
+ month.each_with_index do |day,j|
432
+ years[date.wide.start[:year]][i][j] = true
433
+ end
434
+ end
435
+ elsif date.wide.type == "month"
436
+ if date.wide.end.nil? || date.wide.end[:month].nil? || date.wide.start[:month] == date.wide.end[:month]
437
+ years[date.wide.start[:year]][date.wide.start[:month]-1].each_with_index do |month, i|
438
+ years[date.wide.start[:year]][date.wide.start[:month]-1][i] = true
439
+ end
440
+ else
441
+ for month in date.wide.start[:month]-1..date.wide.end[:month]-1
442
+ years[date.wide.start[:year]][month].each_with_index do |day, i|
443
+ years[date.wide.start[:year]][month][i] = true
444
+ end
445
+ end
446
+ end
447
+ elsif date.wide.type == "day"
448
+ if date.wide.start[:month] == date.wide.end[:month] || date.wide.end[:month].nil?
449
+ if date.wide.start[:day] == date.wide.end[:day]
450
+ years[date.wide.start[:year]][date.wide.start[:month]-1][date.wide.start[:day]-1] = true
451
+ else
452
+ for day in date.wide.start[:day]-1..date.wide.end[:day]-1
453
+ years[date.wide.start[:year]][date.wide.start[:month]-1][day] = true
454
+ end
455
+ end
456
+ else
457
+ for month in date.wide.start[:month]-1..date.wide.end[:month]-1
458
+ if month == date.wide.start[:month]-1
459
+ for day in date.wide.start[:day]-1...MONTH_END_DAY[month]
460
+ years[date.wide.start[:year]][month][day] = true
461
+ end
462
+ elsif month == date.wide.end[:month]-1
463
+ for day in 0..date.wide.end[:day]-1
464
+ years[date.wide.start[:year]][month][day] = true
465
+ end
466
+ else
467
+ for day in 0...MONTH_END_DAY[month]
468
+ years[date.wide.start[:year]][month][day] = true
469
+ end
470
+ end
471
+ end
472
+ end
473
+ end
474
+ return years
475
+ end
476
+
477
+ def clean(result)
478
+ result = remove_duplicate_year(result)
479
+ result = remove_bad_spaces(result)
480
+ end
481
+
482
+ def remove_bad_spaces(result)
483
+ return result if (result =~ /\,\s/).nil?
484
+ return result.split(', ').join(',')
485
+ end
486
+
487
+ def remove_duplicate_year(result)
488
+ return result if (result =~ /(\d{4})[^;]+(\1)/).nil? && (result =~ /(\d{4}\-\d{4})[^;]+(\1)/).nil?
489
+ result_parts = result.split(';')
490
+ sanitized_parts = []
491
+ result_parts.each do |rp|
492
+
493
+ # string with year range repeating separated by a comma
494
+ if !(rp =~ /(\d{4}\-\d{4})\,(\1)/).nil?
495
+ first_occurence = (rp =~ /(\d{4}\-\d{4})/)
496
+ years = result[first_occurence...first_occurence + 9]
497
+
498
+ sanitized_parts << years + rp[first_occurence + 10, rp.length].split(year).join('')
499
+
500
+ # string with year repeating separrated by a comma
501
+ elsif !(rp =~ /(\d{4})\,(\1)/).nil?
502
+ first_occurence = (rp =~ /(\d{4})/)
503
+ year = result[first_occurence...first_occurence + 4]
504
+
505
+ sanitized_parts << year + rp[first_occurence + 5, rp.length].split(year).join('')
506
+
507
+ # string with year range repeating
508
+ elsif !(rp =~ /(\d{4}\-\d{4})[^;]+(\1)/).nil?
509
+ first_occurence = (rp =~ /(\d{4}\-\d{4})/)
510
+ years = result[first_occurence...first_occurence + 9]
511
+
512
+ sanitized_parts << years + rp[first_occurence + 9, rp.length].split(year).join('')
513
+
514
+ # string with year repeating
515
+ elsif !(rp =~ /(\d{4})[^;]+(\1)/).nil?
516
+ first_occurence = (rp =~ /(\d{4})/)
517
+ year = result[first_occurence...first_occurence + 4]
518
+
519
+ sanitized_parts << year + rp[first_occurence + 4, rp.length].split(year).join('')
520
+ end
521
+ end
522
+ sanitized_parts.join('')
523
+ end
524
+
46
525
  def same_time?(o)
47
526
  if o.nil? || o.time.length != @time.length
48
527
  return false
@@ -54,6 +533,22 @@ module OpeningHoursConverter
54
533
  end
55
534
  end
56
535
 
536
+ def same_date?(o)
537
+ if o.nil? || o.date.length != @date.length
538
+ return false
539
+ else
540
+ @date.each_with_index do |d, i|
541
+ return false if !d.wide.equals(o.date[i].wide)
542
+ end
543
+ return true
544
+ end
545
+ end
546
+
547
+ def equals(o)
548
+ return false unless o.instance_of?(OpeningHoursConverter::OpeningHoursRule)
549
+ (same_time?(o) && same_date?(o))
550
+ end
551
+
57
552
  def is_off?
58
553
  @time.length == 0 || (@time.length == 1 && time[0].start.nil?)
59
554
  end
@@ -79,7 +574,7 @@ module OpeningHoursConverter
79
574
  raise ArgumentError
80
575
  end
81
576
 
82
- if @date.length == 0 || @date.first.wide_type != "always" && @date.first.same_kind_as?(date)
577
+ if @date.length == 0 || date.same_weekdays?(@date.first.weekdays)
83
578
  @date << date
84
579
  else
85
580
  if @date.length != 1 || @date.first.wide_type != "always" || !@date.first.same_weekdays?(date.weekdays)