gforces-chronik 0.3.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.
Files changed (47) hide show
  1. data/README +161 -0
  2. data/lib/chronik.rb +100 -0
  3. data/lib/chronik/chronik.rb +249 -0
  4. data/lib/chronik/grabber.rb +26 -0
  5. data/lib/chronik/handlers.rb +524 -0
  6. data/lib/chronik/ordinal.rb +40 -0
  7. data/lib/chronik/pointer.rb +27 -0
  8. data/lib/chronik/repeater.rb +129 -0
  9. data/lib/chronik/repeaters/repeater_day.rb +52 -0
  10. data/lib/chronik/repeaters/repeater_day_name.rb +51 -0
  11. data/lib/chronik/repeaters/repeater_day_portion.rb +94 -0
  12. data/lib/chronik/repeaters/repeater_fortnight.rb +70 -0
  13. data/lib/chronik/repeaters/repeater_hour.rb +57 -0
  14. data/lib/chronik/repeaters/repeater_minute.rb +57 -0
  15. data/lib/chronik/repeaters/repeater_month.rb +66 -0
  16. data/lib/chronik/repeaters/repeater_month_name.rb +98 -0
  17. data/lib/chronik/repeaters/repeater_season.rb +150 -0
  18. data/lib/chronik/repeaters/repeater_season_name.rb +45 -0
  19. data/lib/chronik/repeaters/repeater_second.rb +41 -0
  20. data/lib/chronik/repeaters/repeater_time.rb +124 -0
  21. data/lib/chronik/repeaters/repeater_week.rb +73 -0
  22. data/lib/chronik/repeaters/repeater_weekday.rb +77 -0
  23. data/lib/chronik/repeaters/repeater_weekend.rb +65 -0
  24. data/lib/chronik/repeaters/repeater_year.rb +64 -0
  25. data/lib/chronik/scalar.rb +76 -0
  26. data/lib/chronik/separator.rb +91 -0
  27. data/lib/chronik/time_zone.rb +23 -0
  28. data/lib/numerizer/numerizer.rb +100 -0
  29. data/test/suite.rb +9 -0
  30. data/test/test_Chronik.rb +50 -0
  31. data/test/test_Handler.rb +110 -0
  32. data/test/test_Numerizer.rb +38 -0
  33. data/test/test_RepeaterDayName.rb +52 -0
  34. data/test/test_RepeaterFortnight.rb +63 -0
  35. data/test/test_RepeaterHour.rb +65 -0
  36. data/test/test_RepeaterMonth.rb +47 -0
  37. data/test/test_RepeaterMonthName.rb +57 -0
  38. data/test/test_RepeaterTime.rb +72 -0
  39. data/test/test_RepeaterWeek.rb +63 -0
  40. data/test/test_RepeaterWeekday.rb +56 -0
  41. data/test/test_RepeaterWeekend.rb +75 -0
  42. data/test/test_RepeaterYear.rb +63 -0
  43. data/test/test_Span.rb +24 -0
  44. data/test/test_Time.rb +50 -0
  45. data/test/test_Token.rb +26 -0
  46. data/test/test_parsing.rb +711 -0
  47. metadata +103 -0
@@ -0,0 +1,41 @@
1
+ class Chronik::RepeaterSecond < Chronik::Repeater #:nodoc:
2
+ SECOND_SECONDS = 1 # haha, awesome
3
+
4
+ def initialize(type)
5
+ super
6
+ @second_start = nil
7
+ end
8
+
9
+ def next(pointer = :future)
10
+ super
11
+
12
+ direction = pointer == :future ? 1 : -1
13
+
14
+ if !@second_start
15
+ @second_start = @now + (direction * SECOND_SECONDS)
16
+ else
17
+ @second_start += SECOND_SECONDS * direction
18
+ end
19
+
20
+ Chronik::Span.new(@second_start, @second_start + SECOND_SECONDS)
21
+ end
22
+
23
+ def this(pointer = :future)
24
+ super
25
+
26
+ Chronik::Span.new(@now, @now + 1)
27
+ end
28
+
29
+ def offset(span, amount, pointer)
30
+ direction = pointer == :future ? 1 : -1
31
+ span + direction * amount * SECOND_SECONDS
32
+ end
33
+
34
+ def width
35
+ SECOND_SECONDS
36
+ end
37
+
38
+ def to_s
39
+ super << '-second'
40
+ end
41
+ end
@@ -0,0 +1,124 @@
1
+ class Chronik::RepeaterTime < Chronik::Repeater #:nodoc:
2
+ class Tick #:nodoc:
3
+ attr_accessor :time
4
+
5
+ def initialize(time, ambiguous = false)
6
+ @time = time
7
+ @ambiguous = ambiguous
8
+ end
9
+
10
+ def ambiguous?
11
+ @ambiguous
12
+ end
13
+
14
+ def *(other)
15
+ Tick.new(@time * other, @ambiguous)
16
+ end
17
+
18
+ def to_f
19
+ @time.to_f
20
+ end
21
+
22
+ def to_s
23
+ @time.to_s + (@ambiguous ? '?' : '')
24
+ end
25
+ end
26
+
27
+ def initialize(time, options = {})
28
+ @current_time = nil
29
+ t = time.gsub(/\:/, '')
30
+
31
+ @type =
32
+ case t.size
33
+ when 1..2
34
+ hours = t.to_i
35
+ hours == 12 ? Tick.new(0 * 60 * 60, true) : Tick.new(hours * 60 * 60, true)
36
+ when 3
37
+ Tick.new((t[0..0].to_i * 60 * 60) + (t[1..2].to_i * 60), true)
38
+ when 4
39
+ ambiguous = time =~ /:/ && t[0..0].to_i != 0 && t[0..1].to_i <= 12
40
+ hours = t[0..1].to_i
41
+ hours == 12 ? Tick.new(0 * 60 * 60 + t[2..3].to_i * 60, ambiguous) : Tick.new(hours * 60 * 60 + t[2..3].to_i * 60, ambiguous)
42
+ when 5
43
+ Tick.new(t[0..0].to_i * 60 * 60 + t[1..2].to_i * 60 + t[3..4].to_i, true)
44
+ when 6
45
+ ambiguous = time =~ /:/ && t[0..0].to_i != 0 && t[0..1].to_i <= 12
46
+ hours = t[0..1].to_i
47
+ hours == 12 ? Tick.new(0 * 60 * 60 + t[2..3].to_i * 60 + t[4..5].to_i, ambiguous) : Tick.new(hours * 60 * 60 + t[2..3].to_i * 60 + t[4..5].to_i, ambiguous)
48
+ else
49
+ raise("Time cannot exceed six digits")
50
+ end
51
+ end
52
+
53
+ # Return the next past or future Span for the time that this Repeater represents
54
+ # pointer - Symbol representing which temporal direction to fetch the next day
55
+ # must be either :past or :future
56
+ def next(pointer)
57
+ super
58
+
59
+ half_day = 60 * 60 * 12
60
+ full_day = 60 * 60 * 24
61
+
62
+ first = false
63
+
64
+ unless @current_time
65
+ first = true
66
+ midnight = Chronik.time_class.local(@now.year, @now.month, @now.day)
67
+
68
+ yesterday_midnight = midnight - full_day
69
+ tomorrow_midnight = midnight + full_day
70
+
71
+ offset_fix = midnight.gmt_offset - tomorrow_midnight.gmt_offset
72
+ tomorrow_midnight += offset_fix
73
+
74
+ catch :done do
75
+ if pointer == :future
76
+ if @type.ambiguous?
77
+ [midnight + @type + offset_fix, midnight + half_day + @type + offset_fix, tomorrow_midnight + @type].each do |t|
78
+ (@current_time = t; throw :done) if t >= @now
79
+ end
80
+ else
81
+ [midnight + @type + offset_fix, tomorrow_midnight + @type].each do |t|
82
+ (@current_time = t; throw :done) if t >= @now
83
+ end
84
+ end
85
+ else # pointer == :past
86
+ if @type.ambiguous?
87
+ [midnight + half_day + @type + offset_fix, midnight + @type + offset_fix, yesterday_midnight + @type + half_day].each do |t|
88
+ (@current_time = t; throw :done) if t <= @now
89
+ end
90
+ else
91
+ [midnight + @type + offset_fix, yesterday_midnight + @type].each do |t|
92
+ (@current_time = t; throw :done) if t <= @now
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ @current_time || raise("Current time cannot be nil at this point")
99
+ end
100
+
101
+ unless first
102
+ increment = @type.ambiguous? ? half_day : full_day
103
+ @current_time += pointer == :future ? increment : -increment
104
+ end
105
+
106
+ Chronik::Span.new(@current_time, @current_time + width)
107
+ end
108
+
109
+ def this(context = :future)
110
+ super
111
+
112
+ context = :future if context == :none
113
+
114
+ self.next(context)
115
+ end
116
+
117
+ def width
118
+ 1
119
+ end
120
+
121
+ def to_s
122
+ super << '-time-' << @type.to_s
123
+ end
124
+ end
@@ -0,0 +1,73 @@
1
+ class Chronik::RepeaterWeek < Chronik::Repeater #:nodoc:
2
+ WEEK_SECONDS = 604800 # (7 * 24 * 60 * 60)
3
+
4
+ def initialize(type)
5
+ super
6
+ @current_week_start = nil
7
+ end
8
+
9
+ def next(pointer)
10
+ super
11
+
12
+ if !@current_week_start
13
+ case pointer
14
+ when :future
15
+ sunday_repeater = Chronik::RepeaterDayName.new(:sunday)
16
+ sunday_repeater.start = @now
17
+ next_sunday_span = sunday_repeater.next(:future)
18
+ @current_week_start = next_sunday_span.begin
19
+ when :past
20
+ sunday_repeater = Chronik::RepeaterDayName.new(:sunday)
21
+ sunday_repeater.start = (@now + Chronik::RepeaterDay::DAY_SECONDS)
22
+ sunday_repeater.next(:past)
23
+ last_sunday_span = sunday_repeater.next(:past)
24
+ @current_week_start = last_sunday_span.begin
25
+ end
26
+ else
27
+ direction = pointer == :future ? 1 : -1
28
+ @current_week_start += direction * WEEK_SECONDS
29
+ end
30
+
31
+ Chronik::Span.new(@current_week_start, @current_week_start + WEEK_SECONDS)
32
+ end
33
+
34
+ def this(pointer = :future)
35
+ super
36
+
37
+ case pointer
38
+ when :future
39
+ this_week_start = Chronik.time_class.local(@now.year, @now.month, @now.day, @now.hour) + Chronik::RepeaterHour::HOUR_SECONDS
40
+ sunday_repeater = Chronik::RepeaterDayName.new(:sunday)
41
+ sunday_repeater.start = @now
42
+ this_sunday_span = sunday_repeater.this(:future)
43
+ this_week_end = this_sunday_span.begin
44
+ Chronik::Span.new(this_week_start, this_week_end)
45
+ when :past
46
+ this_week_end = Chronik.time_class.local(@now.year, @now.month, @now.day, @now.hour)
47
+ sunday_repeater = Chronik::RepeaterDayName.new(:sunday)
48
+ sunday_repeater.start = @now
49
+ last_sunday_span = sunday_repeater.next(:past)
50
+ this_week_start = last_sunday_span.begin
51
+ Chronik::Span.new(this_week_start, this_week_end)
52
+ when :none
53
+ sunday_repeater = Chronik::RepeaterDayName.new(:sunday)
54
+ sunday_repeater.start = @now
55
+ last_sunday_span = sunday_repeater.next(:past)
56
+ this_week_start = last_sunday_span.begin
57
+ Chronik::Span.new(this_week_start, this_week_start + WEEK_SECONDS)
58
+ end
59
+ end
60
+
61
+ def offset(span, amount, pointer)
62
+ direction = pointer == :future ? 1 : -1
63
+ span + direction * amount * WEEK_SECONDS
64
+ end
65
+
66
+ def width
67
+ WEEK_SECONDS
68
+ end
69
+
70
+ def to_s
71
+ super << '-week'
72
+ end
73
+ end
@@ -0,0 +1,77 @@
1
+ class Chronik::RepeaterWeekday < Chronik::Repeater #:nodoc:
2
+ WEEK_WEEKDAYS = 5
3
+ DAY_SECONDS = 86400 # (24 * 60 * 60)
4
+
5
+ def initialize(type)
6
+ super
7
+ @current_weekday_start = nil
8
+ end
9
+
10
+ def next(pointer)
11
+ super
12
+
13
+ direction = pointer == :future ? 1 : -1
14
+
15
+ if !@current_weekday_start
16
+ @current_weekday_start = Time.construct(@now.year, @now.month, @now.day)
17
+ @current_weekday_start += direction * DAY_SECONDS
18
+
19
+ until is_weekday?(@current_weekday_start.wday)
20
+ @current_weekday_start += direction * DAY_SECONDS
21
+ end
22
+ else
23
+ loop do
24
+ @current_weekday_start += direction * DAY_SECONDS
25
+ break if is_weekday?(@current_weekday_start.wday)
26
+ end
27
+ end
28
+
29
+ Chronik::Span.new(@current_weekday_start, @current_weekday_start + DAY_SECONDS)
30
+ end
31
+
32
+ def this(pointer = :future)
33
+ super
34
+
35
+ case pointer
36
+ when :past
37
+ self.next(:past)
38
+ when :future, :none
39
+ self.next(:future)
40
+ end
41
+ end
42
+
43
+ def offset(span, amount, pointer)
44
+ direction = pointer == :future ? 1 : -1
45
+
46
+ num_weekdays_passed = 0; offset = 0
47
+ until num_weekdays_passed == amount
48
+ offset += direction * DAY_SECONDS
49
+ num_weekdays_passed += 1 if is_weekday?((span.begin+offset).wday)
50
+ end
51
+
52
+ span + offset
53
+ end
54
+
55
+ def width
56
+ DAY_SECONDS
57
+ end
58
+
59
+ def to_s
60
+ super << '-weekday'
61
+ end
62
+
63
+ private
64
+
65
+ def is_weekend?(day)
66
+ day == symbol_to_number(:saturday) || day == symbol_to_number(:sunday)
67
+ end
68
+
69
+ def is_weekday?(day)
70
+ !is_weekend?(day)
71
+ end
72
+
73
+ def symbol_to_number(sym)
74
+ lookup = {:sunday => 0, :monday => 1, :tuesday => 2, :wednesday => 3, :thursday => 4, :friday => 5, :saturday => 6}
75
+ lookup[sym] || raise("Invalid symbol specified")
76
+ end
77
+ end
@@ -0,0 +1,65 @@
1
+ class Chronik::RepeaterWeekend < Chronik::Repeater #:nodoc:
2
+ WEEKEND_SECONDS = 172_800 # (2 * 24 * 60 * 60)
3
+
4
+ def initialize(type)
5
+ super
6
+ @current_week_start = nil
7
+ end
8
+
9
+ def next(pointer)
10
+ super
11
+
12
+ if !@current_week_start
13
+ case pointer
14
+ when :future
15
+ saturday_repeater = Chronik::RepeaterDayName.new(:saturday)
16
+ saturday_repeater.start = @now
17
+ next_saturday_span = saturday_repeater.next(:future)
18
+ @current_week_start = next_saturday_span.begin
19
+ when :past
20
+ saturday_repeater = Chronik::RepeaterDayName.new(:saturday)
21
+ saturday_repeater.start = (@now + Chronik::RepeaterDay::DAY_SECONDS)
22
+ last_saturday_span = saturday_repeater.next(:past)
23
+ @current_week_start = last_saturday_span.begin
24
+ end
25
+ else
26
+ direction = pointer == :future ? 1 : -1
27
+ @current_week_start += direction * Chronik::RepeaterWeek::WEEK_SECONDS
28
+ end
29
+
30
+ Chronik::Span.new(@current_week_start, @current_week_start + WEEKEND_SECONDS)
31
+ end
32
+
33
+ def this(pointer = :future)
34
+ super
35
+
36
+ case pointer
37
+ when :future, :none
38
+ saturday_repeater = Chronik::RepeaterDayName.new(:saturday)
39
+ saturday_repeater.start = @now
40
+ this_saturday_span = saturday_repeater.this(:future)
41
+ Chronik::Span.new(this_saturday_span.begin, this_saturday_span.begin + WEEKEND_SECONDS)
42
+ when :past
43
+ saturday_repeater = Chronik::RepeaterDayName.new(:saturday)
44
+ saturday_repeater.start = @now
45
+ last_saturday_span = saturday_repeater.this(:past)
46
+ Chronik::Span.new(last_saturday_span.begin, last_saturday_span.begin + WEEKEND_SECONDS)
47
+ end
48
+ end
49
+
50
+ def offset(span, amount, pointer)
51
+ direction = pointer == :future ? 1 : -1
52
+ weekend = Chronik::RepeaterWeekend.new(:weekend)
53
+ weekend.start = span.begin
54
+ start = weekend.next(pointer).begin + (amount - 1) * direction * Chronik::RepeaterWeek::WEEK_SECONDS
55
+ Chronik::Span.new(start, start + (span.end - span.begin))
56
+ end
57
+
58
+ def width
59
+ WEEKEND_SECONDS
60
+ end
61
+
62
+ def to_s
63
+ super << '-weekend'
64
+ end
65
+ end
@@ -0,0 +1,64 @@
1
+ class Chronik::RepeaterYear < Chronik::Repeater #:nodoc:
2
+ YEAR_SECONDS = 31536000 # 365 * 24 * 60 * 60
3
+
4
+ def initialize(type)
5
+ super
6
+ @current_year_start = nil
7
+ end
8
+
9
+ def next(pointer)
10
+ super
11
+
12
+ if !@current_year_start
13
+ case pointer
14
+ when :future
15
+ @current_year_start = Time.construct(@now.year + 1)
16
+ when :past
17
+ @current_year_start = Time.construct(@now.year - 1)
18
+ end
19
+ else
20
+ diff = pointer == :future ? 1 : -1
21
+ @current_year_start = Time.construct(@current_year_start.year + diff)
22
+ end
23
+
24
+ Chronik::Span.new(@current_year_start, Time.construct(@current_year_start.year + 1))
25
+ end
26
+
27
+ def this(pointer = :future)
28
+ super
29
+
30
+ case pointer
31
+ when :future
32
+ this_year_start = Time.construct(@now.year, @now.month, @now.day) + Chronik::RepeaterDay::DAY_SECONDS
33
+ this_year_end = Time.construct(@now.year + 1, 1, 1)
34
+ when :past
35
+ this_year_start = Time.construct(@now.year, 1, 1)
36
+ this_year_end = Time.construct(@now.year, @now.month, @now.day)
37
+ when :none
38
+ this_year_start = Time.construct(@now.year, 1, 1)
39
+ this_year_end = Time.construct(@now.year + 1, 1, 1)
40
+ end
41
+
42
+ Chronik::Span.new(this_year_start, this_year_end)
43
+ end
44
+
45
+ def offset(span, amount, pointer)
46
+ direction = pointer == :future ? 1 : -1
47
+
48
+ sb = span.begin
49
+ new_begin = Time.construct(sb.year + (amount * direction), sb.month, sb.day, sb.hour, sb.min, sb.sec)
50
+
51
+ se = span.end
52
+ new_end = Time.construct(se.year + (amount * direction), se.month, se.day, se.hour, se.min, se.sec)
53
+
54
+ Chronik::Span.new(new_begin, new_end)
55
+ end
56
+
57
+ def width
58
+ (365 * 24 * 60 * 60)
59
+ end
60
+
61
+ def to_s
62
+ super << '-year'
63
+ end
64
+ end