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,89 @@
1
+ module Chronic
2
+ class Scalar < Tag
3
+ DAY_PORTIONS = %w( am pm morning afternoon evening night )
4
+
5
+ # Scan an Array of Token objects and apply any necessary Scalar
6
+ # tags to each token.
7
+ #
8
+ # tokens - An Array of tokens to scan.
9
+ # options - The Hash of options specified in Chronic::parse.
10
+ #
11
+ # Returns an Array of tokens.
12
+ def self.scan(tokens, options)
13
+ tokens.each_index do |i|
14
+ token = tokens[i]
15
+ post_token = tokens[i + 1]
16
+ if token.word =~ /^\d+$/
17
+ width = token.word.length
18
+ scalar = token.word.to_i
19
+ token.tag(Scalar.new(scalar, width))
20
+ token.tag(ScalarWide.new(token.word, width)) if width == 4
21
+ token.tag(ScalarSubsecond.new(scalar, width)) if Chronic::Time::could_be_subsecond?(scalar, width)
22
+ token.tag(ScalarSecond.new(scalar, width)) if Chronic::Time::could_be_second?(scalar, width)
23
+ token.tag(ScalarMinute.new(scalar, width)) if Chronic::Time::could_be_minute?(scalar, width)
24
+ token.tag(ScalarHour.new(scalar, width)) if Chronic::Time::could_be_hour?(scalar, width, options[:hours24] == false)
25
+ unless post_token and DAY_PORTIONS.include?(post_token.word)
26
+ token.tag(ScalarDay.new(scalar, width)) if Chronic::Date::could_be_day?(scalar, width)
27
+ token.tag(ScalarMonth.new(scalar, width)) if Chronic::Date::could_be_month?(scalar, width)
28
+ if Chronic::Date::could_be_year?(scalar, width)
29
+ year = Chronic::Date::make_year(scalar, options[:ambiguous_year_future_bias])
30
+ token.tag(ScalarYear.new(year.to_i, width))
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ def to_s
38
+ 'scalar'
39
+ end
40
+ end
41
+
42
+ class ScalarWide < Scalar #:nodoc:
43
+ def to_s
44
+ super << '-wide-' << @type.to_s
45
+ end
46
+ end
47
+
48
+ class ScalarSubsecond < Scalar #:nodoc:
49
+ def to_s
50
+ super << '-subsecond-' << @type.to_s
51
+ end
52
+ end
53
+
54
+ class ScalarSecond < Scalar #:nodoc:
55
+ def to_s
56
+ super << '-second-' << @type.to_s
57
+ end
58
+ end
59
+
60
+ class ScalarMinute < Scalar #:nodoc:
61
+ def to_s
62
+ super << '-minute-' << @type.to_s
63
+ end
64
+ end
65
+
66
+ class ScalarHour < Scalar #:nodoc:
67
+ def to_s
68
+ super << '-hour-' << @type.to_s
69
+ end
70
+ end
71
+
72
+ class ScalarDay < Scalar #:nodoc:
73
+ def to_s
74
+ super << '-day-' << @type.to_s
75
+ end
76
+ end
77
+
78
+ class ScalarMonth < Scalar #:nodoc:
79
+ def to_s
80
+ super << '-month-' << @type.to_s
81
+ end
82
+ end
83
+
84
+ class ScalarYear < Scalar #:nodoc:
85
+ def to_s
86
+ super << '-year-' << @type.to_s
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,123 @@
1
+ module Chronic
2
+ class Separator < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary Separator
5
+ # tags to each token.
6
+ #
7
+ # tokens - An Array of tokens to scan.
8
+ # options - The Hash of options specified in Chronic::parse.
9
+ #
10
+ # Returns an Array of tokens.
11
+ def self.scan(tokens, options)
12
+ tokens.each do |token|
13
+ token.tag scan_for(token, SeparatorComma, { ','.to_sym => :comma })
14
+ token.tag scan_for(token, SeparatorDot, { '.'.to_sym => :dot })
15
+ token.tag scan_for(token, SeparatorColon, { ':'.to_sym => :colon })
16
+ token.tag scan_for(token, SeparatorSpace, { ' '.to_sym => :space })
17
+ token.tag scan_for(token, SeparatorSlash, { '/'.to_sym => :slash })
18
+ token.tag scan_for(token, SeparatorDash, { :- => :dash })
19
+ token.tag scan_for(token, SeparatorAt, { /^(at|@)$/i => :at })
20
+ token.tag scan_for(token, SeparatorIn, { 'in' => :in })
21
+ token.tag scan_for(token, SeparatorOn, { 'on' => :on })
22
+ token.tag scan_for(token, SeparatorAnd, { 'and' => :and })
23
+ token.tag scan_for(token, SeparatorT, { :T => :T })
24
+ token.tag scan_for(token, SeparatorW, { :W => :W })
25
+ token.tag scan_for_quote(token)
26
+ end
27
+ end
28
+
29
+ # token - The Token object we want to scan.
30
+ #
31
+ # Returns a new SeparatorQuote object.
32
+ def self.scan_for_quote(token)
33
+ scan_for token, SeparatorQuote,
34
+ {
35
+ "'".to_sym => :single_quote,
36
+ '"'.to_sym => :double_quote
37
+ }
38
+ end
39
+
40
+ def to_s
41
+ 'separator'
42
+ end
43
+ end
44
+
45
+ class SeparatorComma < Separator #:nodoc:
46
+ def to_s
47
+ super << '-comma'
48
+ end
49
+ end
50
+
51
+ class SeparatorDot < Separator #:nodoc:
52
+ def to_s
53
+ super << '-dot'
54
+ end
55
+ end
56
+
57
+ class SeparatorColon < Separator #:nodoc:
58
+ def to_s
59
+ super << '-colon'
60
+ end
61
+ end
62
+
63
+ class SeparatorSpace < Separator #:nodoc:
64
+ def to_s
65
+ super << '-space'
66
+ end
67
+ end
68
+
69
+ class SeparatorSlash < Separator #:nodoc:
70
+ def to_s
71
+ super << '-slash'
72
+ end
73
+ end
74
+
75
+ class SeparatorDash < Separator #:nodoc:
76
+ def to_s
77
+ super << '-dash'
78
+ end
79
+ end
80
+
81
+ class SeparatorQuote < Separator #:nodoc:
82
+ def to_s
83
+ super << '-quote-' << @type.to_s
84
+ end
85
+ end
86
+
87
+ class SeparatorAt < Separator #:nodoc:
88
+ def to_s
89
+ super << '-at'
90
+ end
91
+ end
92
+
93
+ class SeparatorIn < Separator #:nodoc:
94
+ def to_s
95
+ super << '-in'
96
+ end
97
+ end
98
+
99
+ class SeparatorOn < Separator #:nodoc:
100
+ def to_s
101
+ super << '-on'
102
+ end
103
+ end
104
+
105
+ class SeparatorAnd < Separator #:nodoc:
106
+ def to_s
107
+ super << '-and'
108
+ end
109
+ end
110
+
111
+ class SeparatorT < Separator #:nodoc:
112
+ def to_s
113
+ super << '-T'
114
+ end
115
+ end
116
+
117
+ class SeparatorW < Separator #:nodoc:
118
+ def to_s
119
+ super << '-W'
120
+ end
121
+ end
122
+
123
+ end
@@ -0,0 +1,35 @@
1
+ module Chronic
2
+ class Sign < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary Sign
5
+ # tags to each token.
6
+ #
7
+ # tokens - An Array of tokens to scan.
8
+ # options - The Hash of options specified in Chronic::parse.
9
+ #
10
+ # Returns an Array of tokens.
11
+ def self.scan(tokens, options)
12
+ tokens.each do |token|
13
+ token.tag scan_for(token, SignPlus, { :+ => :plus })
14
+ token.tag scan_for(token, SignMinus, { :- => :minus })
15
+ end
16
+ end
17
+
18
+ def to_s
19
+ 'sign'
20
+ end
21
+ end
22
+
23
+ class SignPlus < Sign #:nodoc:
24
+ def to_s
25
+ super << '-plus'
26
+ end
27
+ end
28
+
29
+ class SignMinus < Sign #:nodoc:
30
+ def to_s
31
+ super << '-minus'
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,32 @@
1
+ module Chronic
2
+ class TimeZone < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary TimeZone
5
+ # tags to each token.
6
+ #
7
+ # tokens - An Array of tokens to scan.
8
+ # options - The Hash of options specified in Chronic::parse.
9
+ #
10
+ # Returns an Array of tokens.
11
+ def self.scan(tokens, options)
12
+ tokens.each do |token|
13
+ token.tag scan_for_all(token)
14
+ end
15
+ end
16
+
17
+ # token - The Token object we want to scan.
18
+ #
19
+ # Returns a new Pointer object.
20
+ def self.scan_for_all(token)
21
+ scan_for token, self,
22
+ {
23
+ /[PMCE][DS]T|UTC/i => :tz,
24
+ /(tzminus)?\d{2}:?\d{2}/ => :tz
25
+ }
26
+ end
27
+
28
+ def to_s
29
+ 'timezone'
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,40 @@
1
+ module Chronic
2
+ class Time
3
+ HOUR_SECONDS = 3600 # 60 * 60
4
+ MINUTE_SECONDS = 60
5
+ SECOND_SECONDS = 1 # haha, awesome
6
+ SUBSECOND_SECONDS = 0.001
7
+
8
+ # Checks if given number could be hour
9
+ def self.could_be_hour?(hour, width = nil, hours12 = false)
10
+ hour >= 0 && hour <= (hours12 ? 12 : 24) && (width.nil? || width > 0)
11
+ end
12
+
13
+ # Checks if given number could be minute
14
+ def self.could_be_minute?(minute, width = nil)
15
+ minute >= 0 && minute <= 60 && (width.nil? || width <= 2)
16
+ end
17
+
18
+ # Checks if given number could be second
19
+ def self.could_be_second?(second, width = nil)
20
+ second >= 0 && second <= 60 && (width.nil? || width <= 2)
21
+ end
22
+
23
+ # Checks if given number could be subsecond
24
+ def self.could_be_subsecond?(subsecond, width = nil)
25
+ subsecond >= 0 && subsecond <= 999999 && (width.nil? || width > 0)
26
+ end
27
+
28
+ # normalize offset in seconds to offset as string +mm:ss or -mm:ss
29
+ def self.normalize_offset(offset)
30
+ return offset if offset.is_a?(String)
31
+ offset = Chronic.time_class.now.to_time.utc_offset unless offset # get current system's UTC offset if offset is nil
32
+ sign = '+'
33
+ sign = '-' if offset < 0
34
+ hours = (offset.abs / 3600).to_i.to_s.rjust(2,'0')
35
+ minutes = (offset.abs % 3600).to_s.rjust(2,'0')
36
+ sign + hours + minutes
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,61 @@
1
+ module Chronic
2
+ class Token
3
+
4
+ attr_accessor :word
5
+ attr_accessor :tags
6
+
7
+ attr_reader :text
8
+ attr_reader :position
9
+
10
+ def initialize(word, text = nil, position = 0)
11
+ @word = word
12
+ @tags = []
13
+ @text = text
14
+ @position = position
15
+ end
16
+
17
+ def ==(token)
18
+ token.word == @word.downcase
19
+ end
20
+
21
+ # Tag this token with the specified tag.
22
+ #
23
+ # new_tag - The new Tag object.
24
+ #
25
+ # Returns nothing.
26
+ def tag(new_tag)
27
+ @tags << new_tag if new_tag
28
+ end
29
+
30
+ # Remove all tags of the given class.
31
+ #
32
+ # tag_class - The tag Class to remove.
33
+ #
34
+ # Returns nothing.
35
+ def untag(tag_class)
36
+ @tags.delete_if { |m| m.kind_of? tag_class }
37
+ end
38
+
39
+ # Returns true if this token has any tags.
40
+ def tagged?
41
+ @tags.size > 0
42
+ end
43
+
44
+ # tag_class - The tag Class to search for.
45
+ #
46
+ # Returns The first Tag that matches the given class.
47
+ def get_tag(tag_class)
48
+ @tags.find { |m| m.kind_of? tag_class }
49
+ end
50
+
51
+ # Print this Token in a pretty way
52
+ def to_s
53
+ @word + '(' + @tags.join(', ') + ') '
54
+ end
55
+
56
+ def inspect
57
+ to_s
58
+ end
59
+ end
60
+
61
+ end
@@ -0,0 +1,38 @@
1
+ module Chronic
2
+ module Tokenizer
3
+ def self.char_type(char)
4
+ case char
5
+ when '.'
6
+ :period
7
+ when /[[:alpha:]]/
8
+ :letter
9
+ when /[[:digit:]]/
10
+ :digit
11
+ when /[[:space:]]/
12
+ :space
13
+ when /[[:punct:]]/
14
+ :punct
15
+ else
16
+ :other
17
+ end
18
+ end
19
+
20
+ # Proccess text to tokens
21
+ def self.tokenize(text)
22
+ tokens = []
23
+ index = 0
24
+ previos_index = 0
25
+ text.each_char do |char|
26
+ type = char_type(char)
27
+ if type == :space
28
+ tokens << Token.new(text[previos_index...index], text, previos_index)
29
+ previos_index = index+1
30
+ end
31
+ index += 1
32
+ end
33
+ tokens << Token.new(text[previos_index...index], text, previos_index)
34
+ tokens
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module Chronic
2
+ VERSION = '0.10.3'
3
+ end
@@ -0,0 +1,12 @@
1
+ unless defined? Chronic
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'chronic'
4
+ end
5
+
6
+ require 'minitest/autorun'
7
+
8
+ class TestCase < MiniTest::Test
9
+ def self.test(name, &block)
10
+ define_method("test_#{name.gsub(/\W/, '_')}", &block) if block
11
+ end
12
+ end
@@ -0,0 +1,203 @@
1
+ require 'helper'
2
+
3
+ class TestChronic < TestCase
4
+
5
+ def setup
6
+ # Wed Aug 16 14:00:00 UTC 2006
7
+ @now = Time.local(2006, 8, 16, 14, 0, 0, 0)
8
+ end
9
+
10
+ def test_pre_normalize
11
+ assert_equal Chronic::Parser.new.pre_normalize('12:55 pm'), Chronic::Parser.new.pre_normalize('12.55 pm')
12
+ end
13
+
14
+ def test_pre_normalize_numerized_string
15
+ string = 'two and a half years'
16
+ assert_equal Numerizer.numerize(string), Chronic::Parser.new.pre_normalize(string)
17
+ end
18
+
19
+ def test_post_normalize_am_pm_aliases
20
+ # affect wanted patterns
21
+
22
+ tokens = [Chronic::Token.new("5:00"), Chronic::Token.new("morning")]
23
+ tokens[0].tag(Chronic::RepeaterTime.new("5:00"))
24
+ tokens[1].tag(Chronic::RepeaterDayPortion.new(:morning))
25
+
26
+ assert_equal :morning, tokens[1].tags[0].type
27
+
28
+ tokens = Chronic::Handlers.dealias_and_disambiguate_times(tokens, {})
29
+
30
+ assert_equal :am, tokens[1].tags[0].type
31
+ assert_equal 2, tokens.size
32
+
33
+ # don't affect unwanted patterns
34
+
35
+ tokens = [Chronic::Token.new("friday"), Chronic::Token.new("morning")]
36
+ tokens[0].tag(Chronic::RepeaterDayName.new(:friday))
37
+ tokens[1].tag(Chronic::RepeaterDayPortion.new(:morning))
38
+
39
+ assert_equal :morning, tokens[1].tags[0].type
40
+
41
+ tokens = Chronic::Handlers.dealias_and_disambiguate_times(tokens, {})
42
+
43
+ assert_equal :morning, tokens[1].tags[0].type
44
+ assert_equal 2, tokens.size
45
+ end
46
+
47
+ def test_guess
48
+ span = Chronic::Span.new(Time.local(2006, 8, 16, 0), Time.local(2006, 8, 17, 0))
49
+ assert_equal Time.local(2006, 8, 16, 12), Chronic::Parser.new.guess(span)
50
+
51
+ span = Chronic::Span.new(Time.local(2006, 8, 16, 0), Time.local(2006, 8, 17, 0, 0, 1))
52
+ assert_equal Time.local(2006, 8, 16, 12), Chronic::Parser.new.guess(span)
53
+
54
+ span = Chronic::Span.new(Time.local(2006, 11), Time.local(2006, 12))
55
+ assert_equal Time.local(2006, 11, 16), Chronic::Parser.new.guess(span)
56
+ end
57
+
58
+ def test_endian_definitions
59
+ # middle, little
60
+ endians = [
61
+ Chronic::Handler.new([:scalar_month, [:separator_slash, :separator_dash], :scalar_day, [:separator_slash, :separator_dash], :scalar_year, :separator_at?, 'time?'], :handle_sm_sd_sy),
62
+ Chronic::Handler.new([:scalar_month, [:separator_slash, :separator_dash], :scalar_day, :separator_at?, 'time?'], :handle_sm_sd),
63
+ Chronic::Handler.new([:scalar_day, [:separator_slash, :separator_dash], :scalar_month, :separator_at?, 'time?'], :handle_sd_sm),
64
+ Chronic::Handler.new([:scalar_day, [:separator_slash, :separator_dash], :scalar_month, [:separator_slash, :separator_dash], :scalar_year, :separator_at?, 'time?'], :handle_sd_sm_sy),
65
+ Chronic::Handler.new([:scalar_day, :repeater_month_name, :scalar_year, :separator_at?, 'time?'], :handle_sd_rmn_sy)
66
+ ]
67
+
68
+ assert_equal endians, Chronic::SpanDictionary.new.definitions[:endian]
69
+
70
+ defs = Chronic::SpanDictionary.new(:endian_precedence => :little).definitions
71
+ assert_equal endians.reverse, defs[:endian]
72
+
73
+ defs = Chronic::SpanDictionary.new(:endian_precedence => [:little, :middle]).definitions
74
+ assert_equal endians.reverse, defs[:endian]
75
+
76
+ assert_raises(ArgumentError) do
77
+ Chronic::SpanDictionary.new(:endian_precedence => :invalid).definitions
78
+ end
79
+ end
80
+
81
+ def test_debug
82
+ require 'stringio'
83
+ $stdout = StringIO.new
84
+ Chronic.debug = true
85
+
86
+ Chronic.parse 'now'
87
+ assert $stdout.string.include?('this(grabber-this)')
88
+ ensure
89
+ $stdout = STDOUT
90
+ Chronic.debug = false
91
+ end
92
+
93
+ # Chronic.construct
94
+
95
+ def test_normal
96
+ assert_equal Time.local(2006, 1, 2, 0, 0, 0), Chronic.construct(2006, 1, 2, 0, 0, 0)
97
+ assert_equal Time.local(2006, 1, 2, 3, 0, 0), Chronic.construct(2006, 1, 2, 3, 0, 0)
98
+ assert_equal Time.local(2006, 1, 2, 3, 4, 0), Chronic.construct(2006, 1, 2, 3, 4, 0)
99
+ assert_equal Time.local(2006, 1, 2, 3, 4, 5), Chronic.construct(2006, 1, 2, 3, 4, 5)
100
+ end
101
+
102
+ def test_second_overflow
103
+ assert_equal Time.local(2006, 1, 1, 0, 1, 30), Chronic.construct(2006, 1, 1, 0, 0, 90)
104
+ assert_equal Time.local(2006, 1, 1, 0, 5, 0), Chronic.construct(2006, 1, 1, 0, 0, 300)
105
+ end
106
+
107
+ def test_minute_overflow
108
+ assert_equal Time.local(2006, 1, 1, 1, 30), Chronic.construct(2006, 1, 1, 0, 90)
109
+ assert_equal Time.local(2006, 1, 1, 5), Chronic.construct(2006, 1, 1, 0, 300)
110
+ end
111
+
112
+ def test_hour_overflow
113
+ assert_equal Time.local(2006, 1, 2, 12), Chronic.construct(2006, 1, 1, 36)
114
+ assert_equal Time.local(2006, 1, 7), Chronic.construct(2006, 1, 1, 144)
115
+ end
116
+
117
+ def test_day_overflow
118
+ assert_equal Time.local(2006, 2, 1), Chronic.construct(2006, 1, 32)
119
+ assert_equal Time.local(2006, 3, 5), Chronic.construct(2006, 2, 33)
120
+ assert_equal Time.local(2004, 3, 4), Chronic.construct(2004, 2, 33)
121
+ assert_equal Time.local(2000, 3, 4), Chronic.construct(2000, 2, 33)
122
+
123
+ assert_raises(RuntimeError) do
124
+ Chronic.construct(2006, 1, 57)
125
+ end
126
+ end
127
+
128
+ def test_month_overflow
129
+ assert_equal Time.local(2006, 1), Chronic.construct(2005, 13)
130
+ assert_equal Time.local(2005, 12), Chronic.construct(2000, 72)
131
+ end
132
+
133
+ def test_time
134
+ org = Chronic.time_class
135
+ begin
136
+ Chronic.time_class = ::Time
137
+ assert_equal ::Time.new(2013, 8, 27, 20, 30, 40, '+05:30'), Chronic.construct(2013, 8, 27, 20, 30, 40, '+05:30')
138
+ assert_equal ::Time.new(2013, 8, 27, 20, 30, 40, '-08:00'), Chronic.construct(2013, 8, 27, 20, 30, 40, -28800)
139
+ ensure
140
+ Chronic.time_class = org
141
+ end
142
+ end
143
+
144
+ def test_date
145
+ org = Chronic.time_class
146
+ begin
147
+ Chronic.time_class = ::Date
148
+ assert_equal Date.new(2013, 8, 27), Chronic.construct(2013, 8, 27)
149
+ ensure
150
+ Chronic.time_class = org
151
+ end
152
+ end
153
+
154
+ def test_datetime
155
+ org = Chronic.time_class
156
+ begin
157
+ Chronic.time_class = ::DateTime
158
+ assert_equal DateTime.new(2013, 8, 27, 20, 30, 40, '+05:30'), Chronic.construct(2013, 8, 27, 20, 30, 40, '+05:30')
159
+ assert_equal DateTime.new(2013, 8, 27, 20, 30, 40, '-08:00'), Chronic.construct(2013, 8, 27, 20, 30, 40, -28800)
160
+ ensure
161
+ Chronic.time_class = org
162
+ end
163
+ end
164
+
165
+ def test_valid_options
166
+ options = {
167
+ :context => :future,
168
+ :now => nil,
169
+ :hours24 => nil,
170
+ :week_start => :sunday,
171
+ :guess => true,
172
+ :ambiguous_time_range => 6,
173
+ :endian_precedence => [:middle, :little],
174
+ :ambiguous_year_future_bias => 50
175
+ }
176
+ refute_nil Chronic.parse('now', options)
177
+ end
178
+
179
+ def test_invalid_options
180
+ assert_raises(ArgumentError) { Chronic.parse('now', foo: 'boo') }
181
+ assert_raises(ArgumentError) { Chronic.parse('now', time_class: Time) }
182
+ end
183
+
184
+ def test_activesupport
185
+ =begin
186
+ # ActiveSupport needs MiniTest '~> 4.2' which conflicts with '~> 5.0'
187
+ require 'active_support/time'
188
+ org = Chronic.time_class
189
+ org_zone = ::Time.zone
190
+ begin
191
+ ::Time.zone = "Tokyo"
192
+ Chronic.time_class = ::Time.zone
193
+ assert_equal Time.new(2013, 8, 27, 20, 30, 40, '+09:00'), Chronic.construct(2013, 8, 27, 20, 30, 40)
194
+ ::Time.zone = "Indiana (East)"
195
+ Chronic.time_class = ::Time.zone
196
+ assert_equal Time.new(2013, 8, 27, 20, 30, 40, -14400), Chronic.construct(2013, 8, 27, 20, 30, 40)
197
+ ensure
198
+ Chronic.time_class = org
199
+ ::Time.zone = org_zone
200
+ end
201
+ =end
202
+ end
203
+ end