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,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