chronic 0.6.3 → 0.10.2

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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -3
  3. data/.travis.yml +8 -0
  4. data/HISTORY.md +76 -0
  5. data/README.md +47 -42
  6. data/Rakefile +30 -12
  7. data/chronic.gemspec +10 -4
  8. data/lib/chronic/date.rb +82 -0
  9. data/lib/chronic/grabber.rb +9 -7
  10. data/lib/chronic/handler.rb +47 -40
  11. data/lib/chronic/handlers.rb +240 -28
  12. data/lib/chronic/numerizer.rb +11 -2
  13. data/lib/chronic/ordinal.rb +28 -23
  14. data/lib/chronic/parser.rb +268 -0
  15. data/lib/chronic/pointer.rb +9 -7
  16. data/lib/chronic/repeater.rb +58 -48
  17. data/lib/chronic/repeaters/repeater_day.rb +4 -3
  18. data/lib/chronic/repeaters/repeater_day_name.rb +5 -4
  19. data/lib/chronic/repeaters/repeater_day_portion.rb +33 -18
  20. data/lib/chronic/repeaters/repeater_fortnight.rb +4 -3
  21. data/lib/chronic/repeaters/repeater_hour.rb +4 -3
  22. data/lib/chronic/repeaters/repeater_minute.rb +4 -3
  23. data/lib/chronic/repeaters/repeater_month.rb +5 -4
  24. data/lib/chronic/repeaters/repeater_month_name.rb +4 -3
  25. data/lib/chronic/repeaters/repeater_season.rb +5 -3
  26. data/lib/chronic/repeaters/repeater_second.rb +4 -3
  27. data/lib/chronic/repeaters/repeater_time.rb +35 -25
  28. data/lib/chronic/repeaters/repeater_week.rb +4 -3
  29. data/lib/chronic/repeaters/repeater_weekday.rb +4 -3
  30. data/lib/chronic/repeaters/repeater_weekend.rb +4 -3
  31. data/lib/chronic/repeaters/repeater_year.rb +6 -5
  32. data/lib/chronic/scalar.rb +40 -68
  33. data/lib/chronic/season.rb +1 -12
  34. data/lib/chronic/separator.rb +142 -23
  35. data/lib/chronic/sign.rb +49 -0
  36. data/lib/chronic/span.rb +2 -2
  37. data/lib/chronic/tag.rb +10 -15
  38. data/lib/chronic/time.rb +40 -0
  39. data/lib/chronic/time_zone.rb +9 -7
  40. data/lib/chronic/token.rb +16 -10
  41. data/lib/chronic.rb +109 -70
  42. data/test/helper.rb +8 -2
  43. data/test/{test_Chronic.rb → test_chronic.rb} +69 -34
  44. data/test/{test_DaylightSavings.rb → test_daylight_savings.rb} +1 -1
  45. data/test/{test_Handler.rb → test_handler.rb} +38 -14
  46. data/test/{test_MiniDate.rb → test_mini_date.rb} +9 -9
  47. data/test/{test_Numerizer.rb → test_numerizer.rb} +16 -2
  48. data/test/test_parsing.rb +392 -22
  49. data/test/{test_RepeaterDayName.rb → test_repeater_day_name.rb} +1 -1
  50. data/test/test_repeater_day_portion.rb +254 -0
  51. data/test/{test_RepeaterFortnight.rb → test_repeater_fortnight.rb} +1 -1
  52. data/test/{test_RepeaterHour.rb → test_repeater_hour.rb} +1 -1
  53. data/test/{test_RepeaterMinute.rb → test_repeater_minute.rb} +1 -1
  54. data/test/{test_RepeaterMonth.rb → test_repeater_month.rb} +1 -1
  55. data/test/{test_RepeaterMonthName.rb → test_repeater_month_name.rb} +1 -1
  56. data/test/{test_RepeaterSeason.rb → test_repeater_season.rb} +1 -1
  57. data/test/{test_RepeaterTime.rb → test_repeater_time.rb} +19 -1
  58. data/test/{test_RepeaterWeek.rb → test_repeater_week.rb} +1 -1
  59. data/test/{test_RepeaterWeekday.rb → test_repeater_weekday.rb} +1 -1
  60. data/test/{test_RepeaterWeekend.rb → test_repeater_weekend.rb} +1 -1
  61. data/test/{test_RepeaterYear.rb → test_repeater_year.rb} +1 -1
  62. data/test/{test_Span.rb → test_span.rb} +2 -2
  63. data/test/{test_Token.rb → test_token.rb} +1 -1
  64. metadata +129 -87
  65. data/.gemtest +0 -0
  66. data/.yardopts +0 -3
  67. data/lib/chronic/chronic.rb +0 -323
@@ -1,56 +1,126 @@
1
1
  module Chronic
2
2
  class Separator < Tag
3
3
 
4
- # Scan an Array of {Token}s and apply any necessary Separator tags to
5
- # each token
4
+ # Scan an Array of Token objects and apply any necessary Separator
5
+ # tags to each token.
6
6
  #
7
- # @param [Array<Token>] tokens Array of tokens to scan
8
- # @param [Hash] options Options specified in {Chronic.parse}
9
- # @return [Array] list of tokens
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.
10
11
  def self.scan(tokens, options)
11
12
  tokens.each do |token|
12
13
  if t = scan_for_commas(token) then token.tag(t); next end
13
- if t = scan_for_slash_or_dash(token) then token.tag(t); next end
14
+ if t = scan_for_dots(token) then token.tag(t); next end
15
+ if t = scan_for_colon(token) then token.tag(t); next end
16
+ if t = scan_for_space(token) then token.tag(t); next end
17
+ if t = scan_for_slash(token) then token.tag(t); next end
18
+ if t = scan_for_dash(token) then token.tag(t); next end
19
+ if t = scan_for_quote(token) then token.tag(t); next end
14
20
  if t = scan_for_at(token) then token.tag(t); next end
15
21
  if t = scan_for_in(token) then token.tag(t); next end
16
22
  if t = scan_for_on(token) then token.tag(t); next end
23
+ if t = scan_for_and(token) then token.tag(t); next end
24
+ if t = scan_for_t(token) then token.tag(t); next end
25
+ if t = scan_for_w(token) then token.tag(t); next end
17
26
  end
18
27
  end
19
28
 
20
- # @param [Token] token
21
- # @return [SeparatorComma, nil]
29
+ # token - The Token object we want to scan.
30
+ #
31
+ # Returns a new SeparatorComma object.
22
32
  def self.scan_for_commas(token)
23
33
  scan_for token, SeparatorComma, { /^,$/ => :comma }
24
34
  end
25
35
 
26
- # @param [Token] token
27
- # @return [SeparatorSlashOrDash, nil]
28
- def self.scan_for_slash_or_dash(token)
29
- scan_for token, SeparatorSlashOrDash,
36
+ # token - The Token object we want to scan.
37
+ #
38
+ # Returns a new SeparatorDot object.
39
+ def self.scan_for_dots(token)
40
+ scan_for token, SeparatorDot, { /^\.$/ => :dot }
41
+ end
42
+
43
+ # token - The Token object we want to scan.
44
+ #
45
+ # Returns a new SeparatorColon object.
46
+ def self.scan_for_colon(token)
47
+ scan_for token, SeparatorColon, { /^:$/ => :colon }
48
+ end
49
+
50
+ # token - The Token object we want to scan.
51
+ #
52
+ # Returns a new SeparatorSpace object.
53
+ def self.scan_for_space(token)
54
+ scan_for token, SeparatorSpace, { /^ $/ => :space }
55
+ end
56
+
57
+ # token - The Token object we want to scan.
58
+ #
59
+ # Returns a new SeparatorSlash object.
60
+ def self.scan_for_slash(token)
61
+ scan_for token, SeparatorSlash, { /^\/$/ => :slash }
62
+ end
63
+
64
+ # token - The Token object we want to scan.
65
+ #
66
+ # Returns a new SeparatorDash object.
67
+ def self.scan_for_dash(token)
68
+ scan_for token, SeparatorDash, { /^-$/ => :dash }
69
+ end
70
+
71
+ # token - The Token object we want to scan.
72
+ #
73
+ # Returns a new SeparatorQuote object.
74
+ def self.scan_for_quote(token)
75
+ scan_for token, SeparatorQuote,
30
76
  {
31
- /^-$/ => :dash,
32
- /^\/$/ => :slash
77
+ /^'$/ => :single_quote,
78
+ /^"$/ => :double_quote
33
79
  }
34
80
  end
35
81
 
36
- # @param [Token] token
37
- # @return [SeparatorAt, nil]
82
+ # token - The Token object we want to scan.
83
+ #
84
+ # Returns a new SeparatorAt object.
38
85
  def self.scan_for_at(token)
39
86
  scan_for token, SeparatorAt, { /^(at|@)$/ => :at }
40
87
  end
41
88
 
42
- # @param [Token] token
43
- # @return [SeparatorIn, nil]
89
+ # token - The Token object we want to scan.
90
+ #
91
+ # Returns a new SeparatorIn object.
44
92
  def self.scan_for_in(token)
45
93
  scan_for token, SeparatorIn, { /^in$/ => :in }
46
94
  end
47
95
 
48
- # @param [Token] token
49
- # @return [SeparatorOn, nil]
96
+ # token - The Token object we want to scan.
97
+ #
98
+ # Returns a new SeparatorOn object.
50
99
  def self.scan_for_on(token)
51
100
  scan_for token, SeparatorOn, { /^on$/ => :on }
52
101
  end
53
102
 
103
+ # token - The Token object we want to scan.
104
+ #
105
+ # Returns a new SeperatorAnd Object object.
106
+ def self.scan_for_and(token)
107
+ scan_for token, SeparatorAnd, { /^and$/ => :and }
108
+ end
109
+
110
+ # token - The Token object we want to scan.
111
+ #
112
+ # Returns a new SeperatorT Object object.
113
+ def self.scan_for_t(token)
114
+ scan_for token, SeparatorT, { /^t$/ => :T }
115
+ end
116
+
117
+ # token - The Token object we want to scan.
118
+ #
119
+ # Returns a new SeperatorW Object object.
120
+ def self.scan_for_w(token)
121
+ scan_for token, SeparatorW, { /^w$/ => :W }
122
+ end
123
+
54
124
  def to_s
55
125
  'separator'
56
126
  end
@@ -62,9 +132,39 @@ module Chronic
62
132
  end
63
133
  end
64
134
 
65
- class SeparatorSlashOrDash < Separator #:nodoc:
135
+ class SeparatorDot < Separator #:nodoc:
66
136
  def to_s
67
- super << '-slashordash-' << @type.to_s
137
+ super << '-dot'
138
+ end
139
+ end
140
+
141
+ class SeparatorColon < Separator #:nodoc:
142
+ def to_s
143
+ super << '-colon'
144
+ end
145
+ end
146
+
147
+ class SeparatorSpace < Separator #:nodoc:
148
+ def to_s
149
+ super << '-space'
150
+ end
151
+ end
152
+
153
+ class SeparatorSlash < Separator #:nodoc:
154
+ def to_s
155
+ super << '-slash'
156
+ end
157
+ end
158
+
159
+ class SeparatorDash < Separator #:nodoc:
160
+ def to_s
161
+ super << '-dash'
162
+ end
163
+ end
164
+
165
+ class SeparatorQuote < Separator #:nodoc:
166
+ def to_s
167
+ super << '-quote-' << @type.to_s
68
168
  end
69
169
  end
70
170
 
@@ -85,4 +185,23 @@ module Chronic
85
185
  super << '-on'
86
186
  end
87
187
  end
88
- end
188
+
189
+ class SeparatorAnd < Separator #:nodoc:
190
+ def to_s
191
+ super << '-and'
192
+ end
193
+ end
194
+
195
+ class SeparatorT < Separator #:nodoc:
196
+ def to_s
197
+ super << '-T'
198
+ end
199
+ end
200
+
201
+ class SeparatorW < Separator #:nodoc:
202
+ def to_s
203
+ super << '-W'
204
+ end
205
+ end
206
+
207
+ end
@@ -0,0 +1,49 @@
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
+ if t = scan_for_plus(token) then token.tag(t); next end
14
+ if t = scan_for_minus(token) then token.tag(t); next end
15
+ end
16
+ end
17
+
18
+ # token - The Token object we want to scan.
19
+ #
20
+ # Returns a new SignPlus object.
21
+ def self.scan_for_plus(token)
22
+ scan_for token, SignPlus, { /^\+$/ => :plus }
23
+ end
24
+
25
+ # token - The Token object we want to scan.
26
+ #
27
+ # Returns a new SignMinus object.
28
+ def self.scan_for_minus(token)
29
+ scan_for token, SignMinus, { /^-$/ => :minus }
30
+ end
31
+
32
+ def to_s
33
+ 'sign'
34
+ end
35
+ end
36
+
37
+ class SignPlus < Sign #:nodoc:
38
+ def to_s
39
+ super << '-plus'
40
+ end
41
+ end
42
+
43
+ class SignMinus < Sign #:nodoc:
44
+ def to_s
45
+ super << '-minus'
46
+ end
47
+ end
48
+
49
+ end
data/lib/chronic/span.rb CHANGED
@@ -25,7 +25,7 @@ module Chronic
25
25
  '(' << self.begin.to_s << '..' << self.end.to_s << ')'
26
26
  end
27
27
 
28
- alias :cover? :include? unless RUBY_VERSION =~ /^1.9/
28
+ alias :cover? :include? if RUBY_VERSION =~ /^1.8/
29
29
 
30
30
  end
31
- end
31
+ end
data/lib/chronic/tag.rb CHANGED
@@ -1,36 +1,31 @@
1
1
  module Chronic
2
2
  # Tokens are tagged with subclassed instances of this class when
3
- # they match specific criteria
3
+ # they match specific criteria.
4
4
  class Tag
5
5
 
6
- # @return [Symbol]
7
6
  attr_accessor :type
8
7
 
9
- # @param [Symbol] type
10
- def initialize(type)
8
+ # type - The Symbol type of this tag.
9
+ def initialize(type, options = {})
11
10
  @type = type
11
+ @options = options
12
12
  end
13
13
 
14
- # @param [Time] s Set the start timestamp for this Tag
15
- def start=(s)
16
- @now = s
14
+ # time - Set the start Time for this Tag.
15
+ def start=(time)
16
+ @now = time
17
17
  end
18
18
 
19
19
  class << self
20
20
  private
21
21
 
22
- # @param [Token] token
23
- # @param [Class] klass The class instance to create
24
- # @param [Regexp, Hash] items
25
- # @return [Object, nil] either a new instance of `klass` or `nil` if
26
- # no match is found
27
- def scan_for(token, klass, items={})
22
+ def scan_for(token, klass, items={}, options = {})
28
23
  case items
29
24
  when Regexp
30
- return klass.new(token.word) if items =~ token.word
25
+ return klass.new(token.word, options) if items =~ token.word
31
26
  when Hash
32
27
  items.each do |item, symbol|
33
- return klass.new(symbol) if item =~ token.word
28
+ return klass.new(symbol, options) if item =~ token.word
34
29
  end
35
30
  end
36
31
  nil
@@ -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)
10
+ hour >= 0 && hour <= 24
11
+ end
12
+
13
+ # Checks if given number could be minute
14
+ def self.could_be_minute?(minute)
15
+ minute >= 0 && minute <= 60
16
+ end
17
+
18
+ # Checks if given number could be second
19
+ def self.could_be_second?(second)
20
+ second >= 0 && second <= 60
21
+ end
22
+
23
+ # Checks if given number could be subsecond
24
+ def self.could_be_subsecond?(subsecond)
25
+ subsecond >= 0 && subsecond <= 999999
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
@@ -1,20 +1,22 @@
1
1
  module Chronic
2
2
  class TimeZone < Tag
3
3
 
4
- # Scan an Array of {Token}s and apply any necessary TimeZone tags to
5
- # each token
4
+ # Scan an Array of Token objects and apply any necessary TimeZone
5
+ # tags to each token.
6
6
  #
7
- # @param [Array<Token>] tokens Array of tokens to scan
8
- # @param [Hash] options Options specified in {Chronic.parse}
9
- # @return [Array] list of tokens
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.
10
11
  def self.scan(tokens, options)
11
12
  tokens.each do |token|
12
13
  if t = scan_for_all(token) then token.tag(t); next end
13
14
  end
14
15
  end
15
16
 
16
- # @param [Token] token
17
- # @return [TimeZone, nil]
17
+ # token - The Token object we want to scan.
18
+ #
19
+ # Returns a new Pointer object.
18
20
  def self.scan_for_all(token)
19
21
  scan_for token, self,
20
22
  {
data/lib/chronic/token.rb CHANGED
@@ -1,10 +1,7 @@
1
1
  module Chronic
2
2
  class Token
3
3
 
4
- # @return [String] The word this Token represents
5
4
  attr_accessor :word
6
-
7
- # @return [Array] A list of tag associated with this Token
8
5
  attr_accessor :tags
9
6
 
10
7
  def initialize(word)
@@ -12,27 +9,32 @@ module Chronic
12
9
  @tags = []
13
10
  end
14
11
 
15
- # Tag this token with the specified tag
12
+ # Tag this token with the specified tag.
13
+ #
14
+ # new_tag - The new Tag object.
16
15
  #
17
- # @param [Tag] new_tag An instance of {Tag} or one of its subclasses
16
+ # Returns nothing.
18
17
  def tag(new_tag)
19
18
  @tags << new_tag
20
19
  end
21
20
 
22
- # Remove all tags of the given class
21
+ # Remove all tags of the given class.
22
+ #
23
+ # tag_class - The tag Class to remove.
23
24
  #
24
- # @param [Class] The tag class to remove
25
+ # Returns nothing.
25
26
  def untag(tag_class)
26
27
  @tags.delete_if { |m| m.kind_of? tag_class }
27
28
  end
28
29
 
29
- # @return [Boolean] true if this token has any tags
30
+ # Returns true if this token has any tags.
30
31
  def tagged?
31
32
  @tags.size > 0
32
33
  end
33
34
 
34
- # @param [Class] tag_class The tag class to search for
35
- # @return [Tag] The first Tag that matches the given class
35
+ # tag_class - The tag Class to search for.
36
+ #
37
+ # Returns The first Tag that matches the given class.
36
38
  def get_tag(tag_class)
37
39
  @tags.find { |m| m.kind_of? tag_class }
38
40
  end
@@ -41,5 +43,9 @@ module Chronic
41
43
  def to_s
42
44
  @word << '(' << @tags.join(', ') << ') '
43
45
  end
46
+
47
+ def inspect
48
+ to_s
49
+ end
44
50
  end
45
51
  end
data/lib/chronic.rb CHANGED
@@ -1,70 +1,10 @@
1
1
  require 'time'
2
2
  require 'date'
3
3
 
4
- # Parse natural language dates and times into Time or {Chronic::Span} objects
5
- #
6
- # @example
7
- # require 'chronic'
8
- #
9
- # Time.now #=> Sun Aug 27 23:18:25 PDT 2006
10
- #
11
- # Chronic.parse('tomorrow')
12
- # #=> Mon Aug 28 12:00:00 PDT 2006
13
- #
14
- # Chronic.parse('monday', :context => :past)
15
- # #=> Mon Aug 21 12:00:00 PDT 2006
16
- #
17
- # Chronic.parse('this tuesday 5:00')
18
- # #=> Tue Aug 29 17:00:00 PDT 2006
19
- #
20
- # Chronic.parse('this tuesday 5:00', :ambiguous_time_range => :none)
21
- # #=> Tue Aug 29 05:00:00 PDT 2006
22
- #
23
- # Chronic.parse('may 27th', :now => Time.local(2000, 1, 1))
24
- # #=> Sat May 27 12:00:00 PDT 2000
25
- #
26
- # Chronic.parse('may 27th', :guess => false)
27
- # #=> Sun May 27 00:00:00 PDT 2007..Mon May 28 00:00:00 PDT 2007
28
- #
29
- # @author Tom Preston-Werner, Lee Jarvis
30
- module Chronic
31
- VERSION = "0.6.3"
32
-
33
- class << self
34
-
35
- # @return [Boolean] true when debug mode is enabled
36
- attr_accessor :debug
37
-
38
- # @example
39
- # require 'chronic'
40
- # require 'active_support/time'
41
- #
42
- # Time.zone = 'UTC'
43
- # Chronic.time_class = Time.zone
44
- # Chronic.parse('June 15 2006 at 5:54 AM')
45
- # # => Thu, 15 Jun 2006 05:45:00 UTC +00:00
46
- #
47
- # @return [Time] The time class Chronic uses internally
48
- attr_accessor :time_class
49
-
50
- # The current Time Chronic is using to base from
51
- #
52
- # @example
53
- # Time.now #=> 2011-06-06 14:13:43 +0100
54
- # Chronic.parse('yesterday') #=> 2011-06-05 12:00:00 +0100
55
- #
56
- # now = Time.local(2025, 12, 24)
57
- # Chronic.parse('tomorrow', :now => now) #=> 2025-12-25 12:00:00 +0000
58
- #
59
- # @return [Time, nil]
60
- attr_accessor :now
61
- end
62
-
63
- self.debug = false
64
- self.time_class = Time
65
- end
4
+ require 'chronic/parser'
5
+ require 'chronic/date'
6
+ require 'chronic/time'
66
7
 
67
- require 'chronic/chronic'
68
8
  require 'chronic/handler'
69
9
  require 'chronic/handlers'
70
10
  require 'chronic/mini_date'
@@ -76,6 +16,7 @@ require 'chronic/pointer'
76
16
  require 'chronic/scalar'
77
17
  require 'chronic/ordinal'
78
18
  require 'chronic/separator'
19
+ require 'chronic/sign'
79
20
  require 'chronic/time_zone'
80
21
  require 'chronic/numerizer'
81
22
  require 'chronic/season'
@@ -98,14 +39,112 @@ require 'chronic/repeaters/repeater_minute'
98
39
  require 'chronic/repeaters/repeater_second'
99
40
  require 'chronic/repeaters/repeater_time'
100
41
 
101
- class Time
102
- def self.construct(year, month = 1, day = 1, hour = 0, minute = 0, second = 0)
103
- warn "Chronic.construct will be deprecated in version 0.7.0. Please use Chronic.construct instead"
104
- Chronic.construct(year, month, day, hour, minute, second)
42
+ # Parse natural language dates and times into Time or Chronic::Span objects.
43
+ #
44
+ # Examples:
45
+ #
46
+ # require 'chronic'
47
+ #
48
+ # Time.now #=> Sun Aug 27 23:18:25 PDT 2006
49
+ #
50
+ # Chronic.parse('tomorrow')
51
+ # #=> Mon Aug 28 12:00:00 PDT 2006
52
+ #
53
+ # Chronic.parse('monday', :context => :past)
54
+ # #=> Mon Aug 21 12:00:00 PDT 2006
55
+ module Chronic
56
+ VERSION = "0.10.2"
57
+
58
+ class << self
59
+
60
+ # Returns true when debug mode is enabled.
61
+ attr_accessor :debug
62
+
63
+ # Examples:
64
+ #
65
+ # require 'chronic'
66
+ # require 'active_support/time'
67
+ #
68
+ # Time.zone = 'UTC'
69
+ # Chronic.time_class = Time.zone
70
+ # Chronic.parse('June 15 2006 at 5:54 AM')
71
+ # # => Thu, 15 Jun 2006 05:45:00 UTC +00:00
72
+ #
73
+ # Returns The Time class Chronic uses internally.
74
+ attr_accessor :time_class
75
+ end
76
+
77
+ self.debug = false
78
+ self.time_class = ::Time
79
+
80
+
81
+ # Parses a string containing a natural language date or time.
82
+ #
83
+ # If the parser can find a date or time, either a Time or Chronic::Span
84
+ # will be returned (depending on the value of `:guess`). If no
85
+ # date or time can be found, `nil` will be returned.
86
+ #
87
+ # text - The String text to parse.
88
+ # opts - An optional Hash of configuration options passed to Parser::new.
89
+ def self.parse(text, options = {})
90
+ Parser.new(options).parse(text)
105
91
  end
106
92
 
107
- def to_minidate
108
- warn "Time.to_minidate will be deprecated in version 0.7.0. Please use Chronic::MiniDate.from_time(time) instead"
109
- Chronic::MiniDate.from_time(self)
93
+ # Construct a new time object determining possible month overflows
94
+ # and leap years.
95
+ #
96
+ # year - Integer year.
97
+ # month - Integer month.
98
+ # day - Integer day.
99
+ # hour - Integer hour.
100
+ # minute - Integer minute.
101
+ # second - Integer second.
102
+ #
103
+ # Returns a new Time object constructed from these params.
104
+ def self.construct(year, month = 1, day = 1, hour = 0, minute = 0, second = 0, offset = nil)
105
+ if second >= 60
106
+ minute += second / 60
107
+ second = second % 60
108
+ end
109
+
110
+ if minute >= 60
111
+ hour += minute / 60
112
+ minute = minute % 60
113
+ end
114
+
115
+ if hour >= 24
116
+ day += hour / 24
117
+ hour = hour % 24
118
+ end
119
+
120
+ # determine if there is a day overflow. this is complicated by our crappy calendar
121
+ # system (non-constant number of days per month)
122
+ day <= 56 || raise("day must be no more than 56 (makes month resolution easier)")
123
+ if day > 28 # no month ever has fewer than 28 days, so only do this if necessary
124
+ days_this_month = ::Date.leap?(year) ? Date::MONTH_DAYS_LEAP[month] : Date::MONTH_DAYS[month]
125
+ if day > days_this_month
126
+ month += day / days_this_month
127
+ day = day % days_this_month
128
+ end
129
+ end
130
+
131
+ if month > 12
132
+ if month % 12 == 0
133
+ year += (month - 12) / 12
134
+ month = 12
135
+ else
136
+ year += month / 12
137
+ month = month % 12
138
+ end
139
+ end
140
+ if Chronic.time_class.name == "Date"
141
+ Chronic.time_class.new(year, month, day)
142
+ elsif not Chronic.time_class.respond_to?(:new) or (RUBY_VERSION.to_f < 1.9 and Chronic.time_class.name == "Time")
143
+ Chronic.time_class.local(year, month, day, hour, minute, second)
144
+ else
145
+ offset = Time::normalize_offset(offset) if Chronic.time_class.name == "DateTime"
146
+ Chronic.time_class.new(year, month, day, hour, minute, second, offset)
147
+ end
110
148
  end
149
+
111
150
  end
data/test/helper.rb CHANGED
@@ -1,6 +1,12 @@
1
1
  unless defined? Chronic
2
- $:.unshift File.expand_path('../../lib')
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
3
  require 'chronic'
4
4
  end
5
5
 
6
- require 'test/unit'
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