gitlab-chronic 0.10.3

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 (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.gitlab-ci.yml +14 -0
  4. data/.travis.yml +10 -0
  5. data/Gemfile +3 -0
  6. data/HISTORY.md +252 -0
  7. data/LICENSE +21 -0
  8. data/README.md +188 -0
  9. data/Rakefile +68 -0
  10. data/chronic.gemspec +25 -0
  11. data/lib/chronic.rb +155 -0
  12. data/lib/chronic/date.rb +81 -0
  13. data/lib/chronic/definition.rb +128 -0
  14. data/lib/chronic/dictionary.rb +36 -0
  15. data/lib/chronic/handler.rb +97 -0
  16. data/lib/chronic/handlers.rb +672 -0
  17. data/lib/chronic/mini_date.rb +38 -0
  18. data/lib/chronic/parser.rb +222 -0
  19. data/lib/chronic/repeaters/repeater_day.rb +54 -0
  20. data/lib/chronic/repeaters/repeater_day_name.rb +53 -0
  21. data/lib/chronic/repeaters/repeater_day_portion.rb +109 -0
  22. data/lib/chronic/repeaters/repeater_fortnight.rb +72 -0
  23. data/lib/chronic/repeaters/repeater_hour.rb +59 -0
  24. data/lib/chronic/repeaters/repeater_minute.rb +59 -0
  25. data/lib/chronic/repeaters/repeater_month.rb +80 -0
  26. data/lib/chronic/repeaters/repeater_month_name.rb +95 -0
  27. data/lib/chronic/repeaters/repeater_quarter.rb +59 -0
  28. data/lib/chronic/repeaters/repeater_quarter_name.rb +40 -0
  29. data/lib/chronic/repeaters/repeater_season.rb +111 -0
  30. data/lib/chronic/repeaters/repeater_season_name.rb +43 -0
  31. data/lib/chronic/repeaters/repeater_second.rb +43 -0
  32. data/lib/chronic/repeaters/repeater_time.rb +159 -0
  33. data/lib/chronic/repeaters/repeater_week.rb +76 -0
  34. data/lib/chronic/repeaters/repeater_weekday.rb +86 -0
  35. data/lib/chronic/repeaters/repeater_weekend.rb +67 -0
  36. data/lib/chronic/repeaters/repeater_year.rb +78 -0
  37. data/lib/chronic/season.rb +26 -0
  38. data/lib/chronic/span.rb +31 -0
  39. data/lib/chronic/tag.rb +89 -0
  40. data/lib/chronic/tags/grabber.rb +29 -0
  41. data/lib/chronic/tags/ordinal.rb +52 -0
  42. data/lib/chronic/tags/pointer.rb +28 -0
  43. data/lib/chronic/tags/repeater.rb +160 -0
  44. data/lib/chronic/tags/scalar.rb +89 -0
  45. data/lib/chronic/tags/separator.rb +123 -0
  46. data/lib/chronic/tags/sign.rb +35 -0
  47. data/lib/chronic/tags/time_zone.rb +32 -0
  48. data/lib/chronic/time.rb +40 -0
  49. data/lib/chronic/token.rb +61 -0
  50. data/lib/chronic/tokenizer.rb +38 -0
  51. data/lib/chronic/version.rb +3 -0
  52. data/test/helper.rb +12 -0
  53. data/test/test_chronic.rb +203 -0
  54. data/test/test_daylight_savings.rb +122 -0
  55. data/test/test_handler.rb +128 -0
  56. data/test/test_mini_date.rb +32 -0
  57. data/test/test_parsing.rb +1537 -0
  58. data/test/test_repeater_day_name.rb +51 -0
  59. data/test/test_repeater_day_portion.rb +254 -0
  60. data/test/test_repeater_fortnight.rb +62 -0
  61. data/test/test_repeater_hour.rb +68 -0
  62. data/test/test_repeater_minute.rb +34 -0
  63. data/test/test_repeater_month.rb +50 -0
  64. data/test/test_repeater_month_name.rb +56 -0
  65. data/test/test_repeater_quarter.rb +70 -0
  66. data/test/test_repeater_quarter_name.rb +198 -0
  67. data/test/test_repeater_season.rb +40 -0
  68. data/test/test_repeater_time.rb +88 -0
  69. data/test/test_repeater_week.rb +115 -0
  70. data/test/test_repeater_weekday.rb +55 -0
  71. data/test/test_repeater_weekend.rb +74 -0
  72. data/test/test_repeater_year.rb +69 -0
  73. data/test/test_span.rb +23 -0
  74. data/test/test_token.rb +31 -0
  75. metadata +215 -0
@@ -0,0 +1,72 @@
1
+ module Chronic
2
+ class RepeaterFortnight < Repeater #:nodoc:
3
+ FORTNIGHT_SECONDS = 1_209_600 # (14 * 24 * 60 * 60)
4
+
5
+ def initialize(type, width = nil, options = {})
6
+ super
7
+ @current_fortnight_start = nil
8
+ end
9
+
10
+ def next(pointer)
11
+ super
12
+
13
+ unless @current_fortnight_start
14
+ case pointer
15
+ when :future
16
+ sunday_repeater = RepeaterDayName.new(:sunday)
17
+ sunday_repeater.start = @now
18
+ next_sunday_span = sunday_repeater.next(:future)
19
+ @current_fortnight_start = next_sunday_span.begin
20
+ when :past
21
+ sunday_repeater = RepeaterDayName.new(:sunday)
22
+ sunday_repeater.start = (@now + RepeaterDay::DAY_SECONDS)
23
+ 2.times { sunday_repeater.next(:past) }
24
+ last_sunday_span = sunday_repeater.next(:past)
25
+ @current_fortnight_start = last_sunday_span.begin
26
+ end
27
+ else
28
+ direction = pointer == :future ? 1 : -1
29
+ @current_fortnight_start += direction * FORTNIGHT_SECONDS
30
+ end
31
+
32
+ Span.new(@current_fortnight_start, @current_fortnight_start + FORTNIGHT_SECONDS)
33
+ end
34
+
35
+ def this(pointer = :future)
36
+ super
37
+
38
+ pointer = :future if pointer == :none
39
+
40
+ case pointer
41
+ when :future
42
+ this_fortnight_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour) + RepeaterHour::HOUR_SECONDS
43
+ sunday_repeater = RepeaterDayName.new(:sunday)
44
+ sunday_repeater.start = @now
45
+ sunday_repeater.this(:future)
46
+ this_sunday_span = sunday_repeater.this(:future)
47
+ this_fortnight_end = this_sunday_span.begin
48
+ Span.new(this_fortnight_start, this_fortnight_end)
49
+ when :past
50
+ this_fortnight_end = Chronic.construct(@now.year, @now.month, @now.day, @now.hour)
51
+ sunday_repeater = RepeaterDayName.new(:sunday)
52
+ sunday_repeater.start = @now
53
+ last_sunday_span = sunday_repeater.next(:past)
54
+ this_fortnight_start = last_sunday_span.begin
55
+ Span.new(this_fortnight_start, this_fortnight_end)
56
+ end
57
+ end
58
+
59
+ def offset(span, amount, pointer)
60
+ direction = pointer == :future ? 1 : -1
61
+ span + direction * amount * FORTNIGHT_SECONDS
62
+ end
63
+
64
+ def width
65
+ FORTNIGHT_SECONDS
66
+ end
67
+
68
+ def to_s
69
+ super << '-fortnight'
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,59 @@
1
+ module Chronic
2
+ class RepeaterHour < Repeater #:nodoc:
3
+ HOUR_SECONDS = 3600 # 60 * 60
4
+
5
+ def initialize(type, width = nil, options = {})
6
+ super
7
+ @current_hour_start = nil
8
+ end
9
+
10
+ def next(pointer)
11
+ super
12
+
13
+ unless @current_hour_start
14
+ case pointer
15
+ when :future
16
+ @current_hour_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour + 1)
17
+ when :past
18
+ @current_hour_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour - 1)
19
+ end
20
+ else
21
+ direction = pointer == :future ? 1 : -1
22
+ @current_hour_start += direction * HOUR_SECONDS
23
+ end
24
+
25
+ Span.new(@current_hour_start, @current_hour_start + HOUR_SECONDS)
26
+ end
27
+
28
+ def this(pointer = :future)
29
+ super
30
+
31
+ case pointer
32
+ when :future
33
+ hour_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min + 1)
34
+ hour_end = Chronic.construct(@now.year, @now.month, @now.day, @now.hour + 1)
35
+ when :past
36
+ hour_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour)
37
+ hour_end = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
38
+ when :none
39
+ hour_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour)
40
+ hour_end = hour_start + HOUR_SECONDS
41
+ end
42
+
43
+ Span.new(hour_start, hour_end)
44
+ end
45
+
46
+ def offset(span, amount, pointer)
47
+ direction = pointer == :future ? 1 : -1
48
+ span + direction * amount * HOUR_SECONDS
49
+ end
50
+
51
+ def width
52
+ HOUR_SECONDS
53
+ end
54
+
55
+ def to_s
56
+ super << '-hour'
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,59 @@
1
+ module Chronic
2
+ class RepeaterMinute < Repeater #:nodoc:
3
+ MINUTE_SECONDS = 60
4
+
5
+ def initialize(type, width = nil, options = {})
6
+ super
7
+ @current_minute_start = nil
8
+ end
9
+
10
+ def next(pointer = :future)
11
+ super
12
+
13
+ unless @current_minute_start
14
+ case pointer
15
+ when :future
16
+ @current_minute_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min + 1)
17
+ when :past
18
+ @current_minute_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min - 1)
19
+ end
20
+ else
21
+ direction = pointer == :future ? 1 : -1
22
+ @current_minute_start += direction * MINUTE_SECONDS
23
+ end
24
+
25
+ Span.new(@current_minute_start, @current_minute_start + MINUTE_SECONDS)
26
+ end
27
+
28
+ def this(pointer = :future)
29
+ super
30
+
31
+ case pointer
32
+ when :future
33
+ minute_begin = @now
34
+ minute_end = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
35
+ when :past
36
+ minute_begin = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
37
+ minute_end = @now
38
+ when :none
39
+ minute_begin = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
40
+ minute_end = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min) + MINUTE_SECONDS
41
+ end
42
+
43
+ Span.new(minute_begin, minute_end)
44
+ end
45
+
46
+ def offset(span, amount, pointer)
47
+ direction = pointer == :future ? 1 : -1
48
+ span + direction * amount * MINUTE_SECONDS
49
+ end
50
+
51
+ def width
52
+ MINUTE_SECONDS
53
+ end
54
+
55
+ def to_s
56
+ super << '-minute'
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,80 @@
1
+ module Chronic
2
+ class RepeaterMonth < Repeater #:nodoc:
3
+ MONTH_SECONDS = 2_592_000 # 30 * 24 * 60 * 60
4
+ YEAR_MONTHS = 12
5
+ MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
6
+ MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
7
+
8
+ def initialize(type, width = nil, options = {})
9
+ super
10
+ @current_month_start = nil
11
+ end
12
+
13
+ def next(pointer)
14
+ super
15
+
16
+ unless @current_month_start
17
+ @current_month_start = offset_by(Chronic.construct(@now.year, @now.month), 1, pointer)
18
+ else
19
+ @current_month_start = offset_by(Chronic.construct(@current_month_start.year, @current_month_start.month), 1, pointer)
20
+ end
21
+
22
+ Span.new(@current_month_start, Chronic.construct(@current_month_start.year, @current_month_start.month + 1))
23
+ end
24
+
25
+ def this(pointer = :future)
26
+ super
27
+
28
+ case pointer
29
+ when :future
30
+ month_start = Chronic.construct(@now.year, @now.month, @now.day + 1)
31
+ month_end = self.offset_by(Chronic.construct(@now.year, @now.month), 1, :future)
32
+ when :past
33
+ month_start = Chronic.construct(@now.year, @now.month)
34
+ month_end = Chronic.construct(@now.year, @now.month, @now.day)
35
+ when :none
36
+ month_start = Chronic.construct(@now.year, @now.month)
37
+ month_end = self.offset_by(Chronic.construct(@now.year, @now.month), 1, :future)
38
+ end
39
+
40
+ Span.new(month_start, month_end)
41
+ end
42
+
43
+ def offset(span, amount, pointer)
44
+ Span.new(offset_by(span.begin, amount, pointer), offset_by(span.end, amount, pointer))
45
+ end
46
+
47
+ def offset_by(time, amount, pointer)
48
+ direction = pointer == :future ? 1 : -1
49
+
50
+ amount_years = direction * amount / YEAR_MONTHS
51
+ amount_months = direction * amount % YEAR_MONTHS
52
+
53
+ new_year = time.year + amount_years
54
+ new_month = time.month + amount_months
55
+ if new_month > YEAR_MONTHS
56
+ new_year += 1
57
+ new_month -= YEAR_MONTHS
58
+ end
59
+
60
+ days = month_days(new_year, new_month)
61
+ new_day = time.day > days ? days : time.day
62
+
63
+ Chronic.construct(new_year, new_month, new_day, time.hour, time.min, time.sec)
64
+ end
65
+
66
+ def width
67
+ MONTH_SECONDS
68
+ end
69
+
70
+ def to_s
71
+ super << '-month'
72
+ end
73
+
74
+ private
75
+
76
+ def month_days(year, month)
77
+ ::Date.leap?(year) ? MONTH_DAYS_LEAP[month - 1] : MONTH_DAYS[month - 1]
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,95 @@
1
+ module Chronic
2
+ class RepeaterMonthName < Repeater #:nodoc:
3
+ MONTH_SECONDS = 2_592_000 # 30 * 24 * 60 * 60
4
+ MONTHS = {
5
+ :january => 1,
6
+ :february => 2,
7
+ :march => 3,
8
+ :april => 4,
9
+ :may => 5,
10
+ :june => 6,
11
+ :july => 7,
12
+ :august => 8,
13
+ :september => 9,
14
+ :october => 10,
15
+ :november => 11,
16
+ :december => 12
17
+ }
18
+
19
+ def initialize(type, width = nil, options = {})
20
+ super
21
+ @current_month_begin = nil
22
+ end
23
+
24
+ def next(pointer)
25
+ super
26
+
27
+ unless @current_month_begin
28
+ case pointer
29
+ when :future
30
+ if @now.month < index
31
+ @current_month_begin = Chronic.construct(@now.year, index)
32
+ elsif @now.month > index
33
+ @current_month_begin = Chronic.construct(@now.year + 1, index)
34
+ end
35
+ when :none
36
+ if @now.month <= index
37
+ @current_month_begin = Chronic.construct(@now.year, index)
38
+ elsif @now.month > index
39
+ @current_month_begin = Chronic.construct(@now.year + 1, index)
40
+ end
41
+ when :past
42
+ if @now.month >= index
43
+ @current_month_begin = Chronic.construct(@now.year, index)
44
+ elsif @now.month < index
45
+ @current_month_begin = Chronic.construct(@now.year - 1, index)
46
+ end
47
+ end
48
+ @current_month_begin || raise('Current month should be set by now')
49
+ else
50
+ case pointer
51
+ when :future
52
+ @current_month_begin = Chronic.construct(@current_month_begin.year + 1, @current_month_begin.month)
53
+ when :past
54
+ @current_month_begin = Chronic.construct(@current_month_begin.year - 1, @current_month_begin.month)
55
+ end
56
+ end
57
+
58
+ cur_month_year = @current_month_begin.year
59
+ cur_month_month = @current_month_begin.month
60
+
61
+ if cur_month_month == 12
62
+ next_month_year = cur_month_year + 1
63
+ next_month_month = 1
64
+ else
65
+ next_month_year = cur_month_year
66
+ next_month_month = cur_month_month + 1
67
+ end
68
+
69
+ Span.new(@current_month_begin, Chronic.construct(next_month_year, next_month_month))
70
+ end
71
+
72
+ def this(pointer = :future)
73
+ super
74
+
75
+ case pointer
76
+ when :past
77
+ self.next(pointer)
78
+ when :future, :none
79
+ self.next(:none)
80
+ end
81
+ end
82
+
83
+ def width
84
+ MONTH_SECONDS
85
+ end
86
+
87
+ def index
88
+ @index ||= MONTHS[@type]
89
+ end
90
+
91
+ def to_s
92
+ super << '-monthname-' << @type.to_s
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,59 @@
1
+ module Chronic
2
+ class RepeaterQuarter < Repeater #:nodoc:
3
+ MONTHS_PER_QUARTER = 3
4
+ QUARTER_SECONDS = 7_776_000 # 3 * 30 * 24 * 60 * 60
5
+
6
+ def next(pointer)
7
+ @current_span ||= quarter(@now)
8
+ offset_quarter_amount = pointer == :future ? 1 : -1
9
+ @current_span = offset_quarter(@current_span.begin, offset_quarter_amount)
10
+ end
11
+
12
+ def this(*)
13
+ @current_span = quarter(@now)
14
+ end
15
+
16
+ def offset(span, amount, pointer)
17
+ direction = pointer == :future ? 1 : -1
18
+ offset_quarter(span.begin, amount * direction)
19
+ end
20
+
21
+ def width
22
+ @current_span ? @current_span.width : QUARTER_SECONDS
23
+ end
24
+
25
+ def to_s
26
+ super << '-quarter'
27
+ end
28
+
29
+ protected
30
+
31
+ def quarter_index(month)
32
+ (month - 1) / MONTHS_PER_QUARTER
33
+ end
34
+
35
+ def quarter(time)
36
+ year, month = time.year, time.month
37
+
38
+ quarter_index = quarter_index(month)
39
+ quarter_month_start = (quarter_index * MONTHS_PER_QUARTER) + 1
40
+ quarter_month_end = quarter_month_start + MONTHS_PER_QUARTER
41
+
42
+ quarter_start = Chronic.construct(year, quarter_month_start)
43
+ quarter_end = Chronic.construct(year, quarter_month_end)
44
+
45
+ Span.new(quarter_start, quarter_end)
46
+ end
47
+
48
+ def offset_quarter(time, amount)
49
+ new_month = time.month - 1
50
+ new_month = new_month + MONTHS_PER_QUARTER * amount
51
+ new_year = time.year + new_month / 12
52
+ new_month = new_month % 12 + 1
53
+
54
+ offset_time_basis = Chronic.construct(new_year, new_month)
55
+
56
+ quarter(offset_time_basis)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,40 @@
1
+ module Chronic
2
+ class RepeaterQuarterName < RepeaterQuarter #:nodoc:
3
+ QUARTERS = {
4
+ :q1 => 0,
5
+ :q2 => 1,
6
+ :q3 => 2,
7
+ :q4 => 3
8
+ }
9
+
10
+ def next(pointer)
11
+ unless @current_span
12
+ @current_span = this(pointer)
13
+ else
14
+ year_offset = pointer == :future ? 1 : -1
15
+ new_year = @current_span.begin.year + year_offset
16
+ time_basis = Chronic.construct(new_year, @current_span.begin.month)
17
+ @current_span = quarter(time_basis)
18
+ end
19
+
20
+ @current_span
21
+ end
22
+
23
+ def this(pointer = :future)
24
+ current_quarter_index = quarter_index(@now.month)
25
+ target_quarter_index = QUARTERS[type]
26
+
27
+ year_basis_offset = case pointer
28
+ when :past then current_quarter_index > target_quarter_index ? 0 : -1
29
+ when :future then current_quarter_index < target_quarter_index ? 0 : 1
30
+ else 0
31
+ end
32
+
33
+ year_basis = @now.year + year_basis_offset
34
+ month_basis = (MONTHS_PER_QUARTER * target_quarter_index) + 1
35
+ time_basis = Chronic.construct(year_basis, month_basis)
36
+
37
+ @current_span = quarter(time_basis)
38
+ end
39
+ end
40
+ end