gitlab-chronic 0.10.3

Sign up to get free protection for your applications and to get access to all the features.
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