opening_hours_converter 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,519 @@
1
+ require 'opening_hours_converter/constants'
2
+
3
+ module OpeningHoursConverter
4
+ class OpeningHoursParser
5
+ include Constants
6
+ attr_accessor :RGX_RULE_MODIFIER, :RGX_WEEK_KEY, :RGX_WEEK_VAL, :RGX_MONTH, :RGX_MONTHDAY, :RGX_TIME, :RGX_WEEKDAY, :RGX_HOLIDAY, :RGX_WD
7
+ def initialize
8
+ @RGX_RULE_MODIFIER = /^(open|closed|off)$/i
9
+ @RGX_WEEK_KEY = /^week$/
10
+ @RGX_WEEK_VAL = /^([01234]?[0-9]|5[0123])(\-([01234]?[0-9]|5[0123]))?(,([01234]?[0-9]|5[0123])(\-([01234]?[0-9]|5[0123]))?)*\:?$/
11
+ @RGX_MONTH = /^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)(\-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))?\:?$/
12
+ @RGX_MONTHDAY = /^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ([012]?[0-9]|3[01])(\-((Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) )?([012]?[0-9]|3[01]))?\:?$/
13
+ @RGX_TIME = /^((([01]?[0-9]|2[01234])\:[012345][0-9](\-([01]?[0-9]|2[01234])\:[012345][0-9])?(,([01]?[0-9]|2[01234])\:[012345][0-9](\-([01]?[0-9]|2[01234])\:[012345][0-9])?)*)|(24\/7))$/
14
+ @RGX_WEEKDAY = /^(((Mo|Tu|We|Th|Fr|Sa|Su)(\-(Mo|Tu|We|Th|Fr|Sa|Su))?)|(PH|SH|easter))(,(((Mo|Tu|We|Th|Fr|Sa|Su)(\-(Mo|Tu|We|Th|Fr|Sa|Su))?)|(PH|SH|easter)))*$/
15
+ @RGX_HOLIDAY = /^(PH|SH|easter)$/
16
+ @RGX_WD = /^(Mo|Tu|We|Th|Fr|Sa|Su)(\-(Mo|Tu|We|Th|Fr|Sa|Su))?$/
17
+ @RGX_YEAR = /^(\d{4})(\-(\d{4}))?$/
18
+ @RGX_YEAR_MONTH_DAY = /^(\d{4}) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ([012]?[0-9]|3[01])(\-((\d{4}) )?((Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) )?([012]?[0-9]|3[01]))?\:?$/
19
+ @RGX_YEAR_MONTH = /^(\d{4}) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)(\-((\d{4}) )?((Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)))?\:?$/
20
+ end
21
+
22
+ def parse(oh)
23
+ result = []
24
+ blocks = oh.split(';')
25
+
26
+ rule_modifier = nil
27
+ time_selector = nil
28
+ weekday_selector = nil
29
+ wide_range_selector = nil
30
+ month_selector = nil
31
+
32
+ times = nil
33
+ weekdays = nil
34
+ weeks = nil
35
+ months = nil
36
+ years = nil
37
+
38
+ single_month = nil
39
+ month_from = nil
40
+ month_to = nil
41
+
42
+ single_year = nil
43
+ year_from = nil
44
+ year_to = nil
45
+
46
+ single_year = nil
47
+ year_from = nil
48
+ year_to = nil
49
+
50
+ date_ranges = nil
51
+ date_range = nil
52
+ dr_obj = nil
53
+ res_dr_id = nil
54
+
55
+ blocks.each do |block|
56
+ block.strip!
57
+ next if block.length == 0
58
+
59
+ tokens = tokenize(block)
60
+ current_token = tokens.length - 1
61
+
62
+ # get state
63
+ if current_token >= 0 && is_rule_modifier?(tokens[current_token])
64
+ rule_modifier = tokens[current_token].downcase
65
+ current_token -= 1
66
+ end
67
+
68
+ times = []
69
+ if current_token >= 0 && is_time?(tokens[current_token])
70
+ time_selector = tokens[current_token]
71
+ times = get_times(time_selector)
72
+ current_token -= 1
73
+ end
74
+
75
+ # get weekdays selector
76
+ weekdays = []
77
+ if time_selector == "24/7"
78
+ weekdays << {from: 0, to: 6}
79
+ elsif current_token >= 0 && is_weekday?(tokens[current_token])
80
+ weekday_selector = tokens[current_token]
81
+ weekdays = get_weekdays(weekday_selector)
82
+ current_token -= 1
83
+ end
84
+
85
+ # years = []
86
+ # if current_token >= 0 && is_year?(tokens[current_token])
87
+ # year_selector = tokens[current_token]
88
+ # year_selector = year_selector.split(',')
89
+ # year_selector.each do |y|
90
+ # single_year = y.gsub(/\:$/, '').split('-')
91
+ # year_from = single_year[0]
92
+ # if single_year.length > 1
93
+ # year_to = single_year[1]
94
+ # else
95
+ # year_to = year_from
96
+ # end
97
+
98
+ # years << {from: year_from, to: year_to}
99
+ # end
100
+ # current_token -= 1
101
+ # end
102
+
103
+ months = []
104
+ years = []
105
+ if current_token >= 0
106
+
107
+ wide_range_selector = tokens[0]
108
+ for i in 1..current_token
109
+ wide_range_selector += " #{tokens[i]}"
110
+ end
111
+ if wide_range_selector.length > 0
112
+ wide_range_selector = wide_range_selector.strip
113
+ wide_range_selector = wide_range_selector.split(',')
114
+ wide_range_selector.each do |wrs|
115
+ if !(@RGX_YEAR_MONTH_DAY =~ wrs).nil?
116
+ years << get_year_month_day(wrs)
117
+ elsif !(@RGX_YEAR_MONTH =~ wrs).nil?
118
+ years << get_year_month(wrs)
119
+ elsif !(@RGX_MONTHDAY =~ wrs).nil?
120
+ months << get_month_day(wrs)
121
+ elsif !(@RGX_MONTH =~ wrs).nil?
122
+ months << get_month(wrs)
123
+ elsif !(@RGX_YEAR =~ wrs).nil?
124
+
125
+ years << {from: year_from, to: year_to}
126
+ else
127
+ raise ArgumentError, "Unsupported selector #{wrs}"
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ if current_token == tokens.length - 1
134
+ raise ArgumentError, "Unreadable string"
135
+ end
136
+ puts "months : #{months}"
137
+ puts "weekdays : #{weekdays}"
138
+ puts "times : #{times}"
139
+ puts "years : #{years}"
140
+ puts "rule_modifier : #{rule_modifier}"
141
+
142
+ date_ranges = []
143
+
144
+ if months.length > 0
145
+ months.each do |month|
146
+ if !month[:from_day].nil?
147
+ if !month[:to_day].nil?
148
+ date_range = OpeningHoursConverter::WideInterval.new.day(month[:from_day][:day], month[:from_day][:month], nil, month[:to_day][:day], month[:to_day][:month], nil)
149
+ else
150
+ date_range = OpeningHoursConverter::WideInterval.new.day(month[:from_day][:day], month[:from_day][:month])
151
+ end
152
+ date_ranges << date_range
153
+ else
154
+ if !month[:to].nil?
155
+ date_range = OpeningHoursConverter::WideInterval.new.month(month[:from], nil, month[:to])
156
+ else
157
+ date_range = OpeningHoursConverter::WideInterval.new.month(month[:from])
158
+ end
159
+ date_ranges << date_range
160
+ end
161
+ end
162
+ elsif years.length > 0
163
+ years.each do |year|
164
+ # binding.pry
165
+ if !year[:to].nil?
166
+ if !year[:from_day].nil?
167
+ date_range = OpeningHoursConverter::WideInterval.new.day(year[:from_day][:day], year[:from_day][:month], year[:from_day][:year],
168
+ year[:to_day][:day], year[:to_day][:month], year[:to_day][:year])
169
+ elsif !year[:from_month].nil?
170
+ date_range = OpeningHoursConverter::WideInterval.new.month(year[:from_month][:month], year[:from_month][:year],
171
+ year[:to_month][:month], year[:to_month][:year])
172
+ else
173
+ date_range = OpeningHoursConverter::WideInterval.new.year(year[:from], year[:to])
174
+ end
175
+ else
176
+ if !year[:from_month].nil?
177
+ date_range = OpeningHoursConverter::WideInterval.new.month(year[:from_month][:month], year[:from_month][:year])
178
+ elsif !year[:from_day].nil?
179
+ date_range = OpeningHoursConverter::WideInterval.new.day(year[:from_day][:day], year[:from_day][:month], year[:from_day][:year])
180
+ else
181
+ date_range = OpeningHoursConverter::WideInterval.new.year(year[:from])
182
+ end
183
+ end
184
+ date_ranges << date_range
185
+ end
186
+ else
187
+ date_ranges << OpeningHoursConverter::WideInterval.new.always
188
+ end
189
+
190
+ if weekdays.length == 0
191
+ weekdays << {from: 0, to: OSM_DAYS.length - 1}
192
+ end
193
+
194
+ if times.length == 0
195
+ times << {from: 0, to: 24*60}
196
+ end
197
+
198
+ # pasur
199
+ date_ranges.each do |dr|
200
+ found_date_range = false
201
+ res_dr_id = 0
202
+
203
+ while res_dr_id < result.length && !found_date_range
204
+ if result[res_dr_id].wide_interval.equals(dr)
205
+ found_date_range = true
206
+ else
207
+ res_dr_id += 1
208
+ end
209
+ end
210
+
211
+ if found_date_range
212
+ dr_obj = result[res_dr_id]
213
+ else
214
+ dr_obj = OpeningHoursConverter::DateRange.new(dr)
215
+
216
+ general = -1
217
+ for res_dr_id in 0...result.length
218
+ if result[res_dr_id].is_general_for?(OpeningHoursConverter::DateRange.new(dr))
219
+ general = res_dr_id
220
+ end
221
+ end
222
+ if general >= 0
223
+ dr_obj.typical.copy_intervals(result[general].typical.intervals)
224
+ end
225
+ result << dr_obj
226
+ end
227
+
228
+ for wd_id in 0...weekdays.length
229
+ if weekdays[wd_id][:from] <= weekdays[wd_id][:to]
230
+ for wd_rm in weekdays[wd_id][:from]..weekdays[wd_id][:to]
231
+ if dr_obj.defines_typical_week?
232
+ dr_obj.typical.remove_intervals_during_day(wd_rm)
233
+ else
234
+ dr_obj.typical.clear_intervals
235
+ end
236
+ end
237
+ else
238
+ for wd_rm in weekdays[wd_id][:from]..6
239
+ if dr_obj.defines_typical_week?
240
+ dr_obj.typical.remove_intervals_during_day(wd_rm)
241
+ else
242
+ dr_obj.typical.clear_intervals
243
+ end
244
+ end
245
+ for wd_rm in 0..weekdays[wd_id][:to]
246
+ if dr_obj.defines_typical_week?
247
+ dr_obj.typical.remove_intervals_during_day(wd_rm)
248
+ else
249
+ dr_obj.typical.clear_intervals
250
+ end
251
+ end
252
+ end
253
+
254
+ for t_id in 0...times.length
255
+ if rule_modifier == "closed" || rule_modifier == "off"
256
+ remove_interval(dr_obj.typical, weekdays[wd_id], times[t_id])
257
+ else
258
+ add_interval(dr_obj.typical, weekdays[wd_id], times[t_id])
259
+ end
260
+ end
261
+ end
262
+ end
263
+ end
264
+ return result
265
+ end
266
+
267
+ def get_year(wrs)
268
+ single_year = wrs.gsub(/\:$/, '').split('-')
269
+ year_from = single_year[0].to_i
270
+ if year_from < 1
271
+ raise ArgumentError, "Invalid year : #{single_year[0]}"
272
+ end
273
+
274
+ if single_year.length > 1
275
+ year_to = single_year[1].to_i
276
+ if year_to < 1
277
+ raise ArgumentError, "Invalid year : #{single_year[1]}"
278
+ end
279
+ else
280
+ year_to = nil
281
+ end
282
+ { from: year_from, to: year_to }
283
+ end
284
+
285
+ def get_month(wrs)
286
+ single_month = wrs.gsub(/\:$/, '').split('-')
287
+ month_from = OSM_MONTHS.find_index(single_month[0]) + 1
288
+ if month_from < 1
289
+ raise ArgumentError, "Invalid month : #{single_month[0]}"
290
+ end
291
+
292
+ if single_month.length > 1
293
+ month_to = OSM_MONTHS.find_index(single_month[1]) + 1
294
+ if month_to < 1
295
+ raise ArgumentError, "Invalid month : #{single_month[1]}"
296
+ end
297
+ else
298
+ month_to = month_from
299
+ end
300
+ { from: month_from, to: month_to }
301
+ end
302
+
303
+ def get_month_day(wrs)
304
+ single_month = wrs.gsub(/\:$/, '').split('-')
305
+
306
+ month_from = single_month[0].split(' ')
307
+ month_from = { day: month_from[1].to_i, month: OSM_MONTHS.find_index(month_from[0]) + 1 }
308
+ if month_from[:month] < 1
309
+ raise ArgumentError, "Invalid month : #{month_from.inspect}"
310
+ end
311
+
312
+ if single_month.length > 1
313
+ month_to = single_month[1].split(' ')
314
+ month_to = { day: month_to[1].to_i, month: OSM_MONTHS.find_index(month_to[0]) + 1 }
315
+ if month_to[:month] < 1
316
+ raise ArgumentError, "Invalid month : #{month_to.inspect}"
317
+ end
318
+ else
319
+ month_to = nil
320
+ end
321
+ { from_day: month_from, to_day: month_to }
322
+ end
323
+
324
+ def get_year_month(wrs)
325
+ single_year_month = wrs.gsub(/\:$/, '').split('-')
326
+ year_month_from = single_year_month[0].split(' ')
327
+ year_month_from = { month: OSM_MONTHS.find_index(year_month_from[1]) + 1, year: year_month_from[0].to_i }
328
+ if year_month_from.length < 1
329
+ raise ArgumentError, "Invalid year_month : #{year_month_from.inspect}"
330
+ end
331
+ if single_year_month.length > 1
332
+ year_month_to = single_year_month[1].split(' ')
333
+ if year_month_to.length == 2
334
+ year_month_to = { month: OSM_MONTHS.find_index(year_month_to[1]) + 1, year: year_month_to[0].to_i }
335
+ elsif year_month_to.length == 1
336
+ year_month_to = { month: OSM_MONTHS.find_index(year_month_to[1]) + 1, year: year_month_from[:year] }
337
+ end
338
+ if year_month_to.length < 1
339
+ raise ArgumentError, "Invalid year_month : #{year_month_to.inspect}"
340
+ end
341
+ else
342
+ year_month_to = nil
343
+ end
344
+ { from_month: year_month_from, to_month: year_month_to }
345
+ end
346
+
347
+ def get_year_month_day(wrs)
348
+ single_year_month_day = wrs.gsub(/\:$/, '').split('-')
349
+ year_month_day_from = single_year_month_day[0].split(' ')
350
+ year_month_day_from = { day: year_month_day_from[2].to_i,
351
+ month: OSM_MONTHS.find_index(year_month_day_from[1]) + 1,
352
+ year: year_month_day_from[0].to_i }
353
+ if year_month_day_from.length < 1
354
+ raise ArgumentError, "Invalid year_month_day : #{year_month_day_from.inspect}"
355
+ end
356
+ if single_year_month_day.length > 1
357
+ year_month_day_to = single_year_month_day[1].split(' ')
358
+ if year_month_day_to.length == 3
359
+ year_month_day_to = { day: year_month_day_to[2].to_i,
360
+ month: OSM_MONTHS.find_index(year_month_day_to[1]) + 1,
361
+ year: year_month_day_to[0].to_i }
362
+ elsif year_month_day_to.length == 2
363
+ year_month_day_to = { day: year_month_day_to[1].to_i,
364
+ month: OSM_MONTHS.find_index(year_month_day_to[0]) + 1,
365
+ year: year_month_day_from[:year] }
366
+ elsif year_month_day_to.length == 1
367
+ year_month_day_to = { day: year_month_day_to[0].to_i,
368
+ month: year_month_day_from[:month],
369
+ year: year_month_day_from[:year] }
370
+ end
371
+ if year_month_day_to.length < 1
372
+ raise ArgumentError, "Invalid year_month_day : #{year_month_day_to.inspect}"
373
+ end
374
+ else
375
+ year_month_day_to = nil
376
+ end
377
+
378
+ { from_day: year_month_day_from, to_day: year_month_day_to }
379
+ end
380
+
381
+ def get_times(time_selector)
382
+ times = []
383
+ from = nil
384
+ to = nil
385
+ if time_selector == "24/7"
386
+ times << {from: 0, to: 24*60}
387
+ else
388
+ time_selector = time_selector.split(',')
389
+ time_selector.each do |ts|
390
+ single_time = ts.split('-')
391
+ from = as_minutes(single_time[0])
392
+ if single_time.length > 1
393
+ to = as_minutes(single_time[1])
394
+ else
395
+ to = from
396
+ end
397
+ times << {from: from, to: to}
398
+ end
399
+ end
400
+ times
401
+ end
402
+
403
+ def get_weekdays(weekday_selector)
404
+ weekdays = []
405
+ wd_from = nil
406
+ wd_to = nil
407
+
408
+ weekday_selector = weekday_selector.split(',')
409
+ weekday_selector.each do |wd|
410
+ if !(@RGX_HOLIDAY =~ wd).nil?
411
+ elsif !(@RGX_WD =~ wd).nil?
412
+ single_weekday = wd.split('-')
413
+ wd_from = OSM_DAYS.find_index(single_weekday[0])
414
+ if single_weekday.length > 1
415
+ wd_to = OSM_DAYS.find_index(single_weekday[1])
416
+ else
417
+ wd_to = wd_from
418
+ end
419
+
420
+ weekdays << {from: wd_from, to: wd_to}
421
+ else
422
+ raise ArgumentError, "Invalid weekday interval : #{wd}"
423
+ end
424
+ end
425
+
426
+ weekdays
427
+ end
428
+
429
+ def remove_interval(typical, weekdays, times)
430
+ if weekdays[:from] <= weekdays[:to]
431
+ for wd in weekdays[:from]..weekdays[:to]
432
+ typical.remove_intervals_during_day(wd)
433
+ end
434
+ else
435
+ for wd in weekdays[:from]..6
436
+ typical.remove_intervals_during_day(wd)
437
+ end
438
+ for wd in 0..weekdays[:from]
439
+ typical.remove_intervals_during_day(wd)
440
+ end
441
+ end
442
+ end
443
+
444
+ def remove_interval_wd(typical, times, wd)
445
+ if times[:to] >= times[:from]
446
+ typical.remove_interval(OpeningHoursConverter::Interval.new(wd, times[:from], wd, times[:to]))
447
+ else
448
+ if wd < 6
449
+ typical.remove_interval(OpeningHoursConverter::Interval.new(wd, times[:from], wd+1, times[:to]))
450
+ else
451
+ typical.remove_interval(OpeningHoursConverter::Interval.new(wd, times[:from], wd+1, 24*60))
452
+ typical.remove_interval(OpeningHoursConverter::Interval.new(0, 0, 0, times[:to]))
453
+ end
454
+ end
455
+ end
456
+
457
+ def add_interval(typical, weekdays, times)
458
+ if typical.instance_of?(OpeningHoursConverter::Day)
459
+ if weekdays[:from] != 0 || (weekdays[:to] !=0 && times[:from] <= times[:to])
460
+ weekdays = weekdays.dup
461
+ weekdays[:from] = 0
462
+ if times[:from] <= times[:to]
463
+ weekdays[:to] = 0
464
+ else
465
+ weekdays[:to] = 1
466
+ end
467
+ end
468
+ end
469
+
470
+ if weekdays[:from] <= weekdays[:to]
471
+ for wd in weekdays[:from]..weekdays[:to]
472
+ add_interval_wd(typical, times, wd)
473
+ end
474
+ else
475
+ for wd in weekdays[:from]..6
476
+ add_interval_wd(typical, times, wd)
477
+ end
478
+ for wd in 0..weekdays[:to]
479
+ add_interval_wd(typical, times, wd)
480
+ end
481
+ end
482
+ end
483
+
484
+ def add_interval_wd(typical, times, wd)
485
+ if times[:to] >= times[:from]
486
+ typical.add_interval(OpeningHoursConverter::Interval.new(wd, times[:from], wd, times[:to]))
487
+ else
488
+ if wd < 6
489
+ typical.add_interval(OpeningHoursConverter::Interval.new(wd, times[:from], wd+1, times[:to]))
490
+ else
491
+ typical.add_interval(OpeningHoursConverter::Interval.new(wd, times[:from], wd+1, 24*60))
492
+ typical.add_interval(OpeningHoursConverter::Interval.new(0, 0, 0, times[:to]))
493
+ end
494
+ end
495
+ end
496
+
497
+ def tokenize(block)
498
+ block.split(' ')
499
+ end
500
+
501
+ def as_minutes(time)
502
+ values = time.split(':')
503
+ values[0].to_i * 60 + values[1].to_i
504
+ end
505
+
506
+ def is_rule_modifier?(token)
507
+ !(@RGX_RULE_MODIFIER =~ token).nil?
508
+ end
509
+ def is_time?(token)
510
+ !(@RGX_TIME =~ token).nil?
511
+ end
512
+ def is_weekday?(token)
513
+ !(@RGX_WEEKDAY =~ token).nil?
514
+ end
515
+ def is_year?(token)
516
+ !(@RGX_YEAR =~ token).nil?
517
+ end
518
+ end
519
+ end
@@ -0,0 +1,99 @@
1
+ module OpeningHoursConverter
2
+ class OpeningHoursRule
3
+ attr_accessor :date, :time
4
+
5
+ def initialize
6
+ @date = []
7
+ @time = []
8
+ end
9
+
10
+ def get
11
+ 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
19
+ end
20
+
21
+ if @date.length > 0
22
+ wd = @date[0].get_weekdays
23
+ if wd.length > 0
24
+ result += " #{wd}"
25
+ end
26
+ end
27
+
28
+ if @time.length > 0
29
+ result += " "
30
+ @time.each_with_index do |t, i|
31
+ if (i > 0)
32
+ result += ","
33
+ end
34
+ result += t.get
35
+ end
36
+ else
37
+ result += " off"
38
+ end
39
+ if result.strip == "00:00-24:00"
40
+ result = "24/7"
41
+ end
42
+
43
+ result.strip
44
+ end
45
+
46
+ def same_time?(o)
47
+ if o.nil? || o.time.length != @time.length
48
+ return false
49
+ else
50
+ @time.each_with_index do |t, i|
51
+ return false if !t.equals(o.time[i])
52
+ end
53
+ return true
54
+ end
55
+ end
56
+
57
+ def is_off?
58
+ @time.length == 0 || (@time.length == 1 && time[0].start.nil?)
59
+ end
60
+
61
+ def has_overwritten_weekday?
62
+ @date.length > 0 && @date[0].weekdays_over.length > 0
63
+ end
64
+
65
+ def add_weekday(weekday)
66
+ @date.each do |d|
67
+ d.add_weekday(weekday)
68
+ end
69
+ end
70
+
71
+ def add_overwritten_weekday(weekday)
72
+ @date.each do |d|
73
+ d.add_overwritten_weekday(weekday)
74
+ end
75
+ end
76
+
77
+ def add_date(date)
78
+ if date.nil? || !date.instance_of?(OpeningHoursConverter::OpeningHoursDate)
79
+ raise ArgumentError
80
+ end
81
+
82
+ if @date.length == 0 || @date.first.wide_type != "always" && @date.first.same_kind_as?(date)
83
+ @date << date
84
+ else
85
+ if @date.length != 1 || @date.first.wide_type != "always" || !@date.first.same_weekdays?(date.weekdays)
86
+ raise ArgumentError, "This date #{@date.inspect} can't be added to this rule #{self.inspect}"
87
+ end
88
+ end
89
+ end
90
+
91
+ def add_time(time)
92
+ if (@time.length == 0 || @time[0].get != "off") && !@time.include?(time)
93
+ @time << time
94
+ else
95
+ raise ArgumentError, "This time can't be added to this rule"
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,28 @@
1
+ module OpeningHoursConverter
2
+ class OpeningHoursTime
3
+ attr_accessor :start, :end
4
+
5
+ def initialize(minute_start=nil, minute_end=nil)
6
+ @start = minute_start
7
+ unless minute_start == minute_end
8
+ @end = minute_end
9
+ end
10
+ end
11
+
12
+ def get
13
+ return "off" if (@start.nil? && @end.nil?)
14
+ "#{time_string(@start)}#{@end.nil? ? "" : "-#{time_string(@end)}"}"
15
+ end
16
+
17
+ def equals(t)
18
+ @start == t.start && @end == t.end
19
+ end
20
+
21
+ def time_string(minutes)
22
+ fminutes = minutes.to_f
23
+ h = (fminutes/60).floor.to_i
24
+ m = (fminutes%60).to_i
25
+ "#{h < 10 ? "0" : ""}#{h}:#{m < 10 ? "0" : ""}#{m}"
26
+ end
27
+ end
28
+ end