chronic 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,11 +1,18 @@
1
- = 0.2.0 2007-03-20
1
+ = 0.2.1
2
2
 
3
3
  * fixed time overflow issue
4
- * implemented numerizer, allowing the use of number words (e.g. five weeks ago) (shalev)
4
+ * implemented "next" for minute repeater
5
+ * generalized time dealiasing to dealias regardless of day portion and time position
6
+ * added additional token match for cases like "friday evening at 7" and "tomorrow evening at 7"
7
+ * added support for Time#to_s output format: "Mon Apr 02 17:00:00 PDT 2007"
8
+
9
+ = 0.2.0 2007-03-20
10
+
11
+ * implemented numerizer, allowing the use of number words (e.g. five weeks ago) (by shalev)
5
12
 
6
13
  = 0.1.6 2006-01-15
7
14
 
8
- * added 'weekend' support (eventualbuddha)
15
+ * added 'weekend' support (by eventualbuddha)
9
16
 
10
17
  = 0.1.5 2006-12-20
11
18
 
@@ -20,7 +27,7 @@
20
27
 
21
28
  = 0.1.3
22
29
 
23
- * improved regexes for word variations (Josh Goebel)
30
+ * improved regexes for word variations (by Josh Goebel)
24
31
  * fixed a bug that caused "today at 3am" to return nil if current time is after 3am
25
32
 
26
33
  = 0.1.2
data/Rakefile CHANGED
@@ -7,6 +7,8 @@ require './lib/chronic.rb'
7
7
  Hoe.new('chronic', Chronic::VERSION) do |p|
8
8
  p.rubyforge_name = 'chronic'
9
9
  p.summary = 'A natural language date parser'
10
+ p.author = 'Tom Preston-Werner'
11
+ p.email = 'tom@rubyisawesome.com'
10
12
  p.description = p.paragraphs_of('README.txt', 2).join("\n\n")
11
13
  p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
12
14
  p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
@@ -14,28 +16,4 @@ Hoe.new('chronic', Chronic::VERSION) do |p|
14
16
  p.extra_deps = []
15
17
  end
16
18
 
17
- # vim: syntax=Ruby
18
-
19
- __END__
20
-
21
- require 'rubygems'
22
-
23
- SPEC = Gem::Specification.new do |s|
24
- s.name = 'chronic'
25
- s.version = '0.1.5'
26
- s.author = 'Tom Preston-Werner'
27
- s.email = 'tom@rubyisawesome.com'
28
- s.homepage = 'http://chronic.rubyforge.org'
29
- s.platform = Gem::Platform::RUBY
30
- s.summary = "A natural language date parser"
31
- candidates = Dir["{lib,test}/**/*"]
32
- s.files = candidates.delete_if do |item|
33
- item.include?('.svn')
34
- end
35
- s.require_path = "lib"
36
- s.autorequire = "chronic"
37
- s.test_file = "test/suite.rb"
38
- s.has_rdoc = true
39
- s.extra_rdoc_files = ['README']
40
- s.rdoc_options << '--main' << 'README'
41
- end
19
+ # vim: syntax=Ruby
@@ -34,11 +34,12 @@ require 'chronic/pointer'
34
34
  require 'chronic/scalar'
35
35
  require 'chronic/ordinal'
36
36
  require 'chronic/separator'
37
+ require 'chronic/time_zone'
37
38
 
38
39
  require 'numerizer/numerizer'
39
40
 
40
41
  module Chronic
41
- VERSION = "0.2.0"
42
+ VERSION = "0.2.1"
42
43
 
43
44
  def self.debug; false; end
44
45
  end
@@ -50,6 +51,33 @@ def p(val)
50
51
  puts
51
52
  end
52
53
 
54
+ # class Time
55
+ # def self.construct(year, month = 1, day = 1, hour = 0, minute = 0, second = 0)
56
+ # # extra_seconds = second > 60 ? second - 60 : 0
57
+ # # extra_minutes = minute > 59 ? minute - 59 : 0
58
+ # # extra_hours = hour > 23 ? hour - 23 : 0
59
+ # # extra_days = day >
60
+ #
61
+ # if month > 12
62
+ # if month % 12 == 0
63
+ # year += (month - 12) / 12
64
+ # month = 12
65
+ # else
66
+ # year += month / 12
67
+ # month = month % 12
68
+ # end
69
+ # end
70
+ #
71
+ # base = Time.local(year, month)
72
+ # puts base
73
+ # offset = ((day - 1) * 24 * 60 * 60) + (hour * 60 * 60) + (minute * 60) + second
74
+ # puts offset.to_s
75
+ # date = base + offset
76
+ # puts date
77
+ # date
78
+ # end
79
+ # end
80
+
53
81
  class Time
54
82
  def self.construct(year, month = 1, day = 1, hour = 0, minute = 0, second = 0)
55
83
  if second >= 60
@@ -67,6 +95,31 @@ class Time
67
95
  hour = hour % 24
68
96
  end
69
97
 
98
+ # determine if there is a day overflow. this is complicated by our crappy calendar
99
+ # system (non-constant number of days per month)
100
+ day <= 56 || raise("day must be no more than 56 (makes month resolution easier)")
101
+ if day > 28
102
+ # no month ever has fewer than 28 days, so only do this if necessary
103
+ leap_year = (year % 4 == 0) && !(year % 100 == 0)
104
+ leap_year_month_days = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
105
+ common_year_month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
106
+ days_this_month = leap_year ? leap_year_month_days[month - 1] : common_year_month_days[month - 1]
107
+ if day > days_this_month
108
+ month += day / days_this_month
109
+ day = day % days_this_month
110
+ end
111
+ end
112
+
113
+ if month > 12
114
+ if month % 12 == 0
115
+ year += (month - 12) / 12
116
+ month = 12
117
+ else
118
+ year += month / 12
119
+ month = month % 12
120
+ end
121
+ end
122
+
70
123
  Time.local(year, month, day, hour, minute, second)
71
124
  end
72
125
  end
@@ -66,7 +66,7 @@ module Chronic
66
66
  @tokens = tokenizer.scan(@tokens, options)
67
67
  end
68
68
 
69
- [Grabber, Pointer, Scalar, Ordinal, Separator].each do |tokenizer|
69
+ [Grabber, Pointer, Scalar, Ordinal, Separator, TimeZone].each do |tokenizer|
70
70
  @tokens = tokenizer.scan(@tokens)
71
71
  end
72
72
 
@@ -6,7 +6,8 @@ module Chronic
6
6
  @definitions ||=
7
7
  {:time => [Handler.new([:repeater_time, :repeater_day_portion?], nil)],
8
8
 
9
- :date => [Handler.new([:repeater_month_name, :scalar_day, :scalar_year], :handle_rmn_sd_sy),
9
+ :date => [Handler.new([:repeater_day_name, :repeater_month_name, :scalar_day, :repeater_time, :time_zone, :scalar_year], :handle_rdn_rmn_sd_t_tz_sy),
10
+ Handler.new([:repeater_month_name, :scalar_day, :scalar_year], :handle_rmn_sd_sy),
10
11
  Handler.new([:repeater_month_name, :scalar_day, :scalar_year, :separator_at?, 'time?'], :handle_rmn_sd_sy),
11
12
  Handler.new([:repeater_month_name, :scalar_day, :separator_at?, 'time?'], :handle_rmn_sd),
12
13
  Handler.new([:repeater_month_name, :ordinal_day, :separator_at?, 'time?'], :handle_rmn_od),
@@ -17,13 +18,17 @@ module Chronic
17
18
  Handler.new([:scalar_year, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_day, :separator_at?, 'time?'], :handle_sy_sm_sd),
18
19
  Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_year], :handle_sm_sy)],
19
20
 
21
+ # tonight at 7pm
20
22
  :anchor => [Handler.new([:grabber?, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),
23
+ Handler.new([:grabber?, :repeater, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),
21
24
  Handler.new([:repeater, :grabber, :repeater], :handle_r_g_r)],
22
25
 
26
+ # 3 weeks from now, in 2 months
23
27
  :arrow => [Handler.new([:scalar, :repeater, :pointer], :handle_s_r_p),
24
28
  Handler.new([:pointer, :scalar, :repeater], :handle_p_s_r),
25
29
  Handler.new([:scalar, :repeater, :pointer, 'anchor'], :handle_s_r_p_a)],
26
30
 
31
+ # 3rd week in march
27
32
  :narrow => [Handler.new([:ordinal, :repeater, :separator_in, :repeater], :handle_o_r_s_r),
28
33
  Handler.new([:ordinal, :repeater, :grabber, :repeater], :handle_o_r_g_r)]
29
34
  }
@@ -34,6 +39,7 @@ module Chronic
34
39
 
35
40
  self.definitions[:date].each do |handler|
36
41
  if handler.match(tokens, self.definitions)
42
+ puts "-date" if Chronic.debug
37
43
  good_tokens = tokens.select { |o| !o.get_tag Separator }
38
44
  return self.send(handler.handler_method, good_tokens, options)
39
45
  end
@@ -43,6 +49,7 @@ module Chronic
43
49
 
44
50
  self.definitions[:anchor].each do |handler|
45
51
  if handler.match(tokens, self.definitions)
52
+ puts "-anchor" if Chronic.debug
46
53
  good_tokens = tokens.select { |o| !o.get_tag Separator }
47
54
  return self.send(handler.handler_method, good_tokens, options)
48
55
  end
@@ -52,21 +59,24 @@ module Chronic
52
59
 
53
60
  self.definitions[:arrow].each do |handler|
54
61
  if handler.match(tokens, self.definitions)
62
+ puts "-arrow" if Chronic.debug
55
63
  good_tokens = tokens.reject { |o| o.get_tag(SeparatorAt) || o.get_tag(SeparatorSlashOrDash) || o.get_tag(SeparatorComma) }
56
64
  return self.send(handler.handler_method, good_tokens, options)
57
65
  end
58
66
  end
59
67
 
60
- # not an arrow, let's hope it's an narrow
68
+ # not an arrow, let's hope it's a narrow
61
69
 
62
70
  self.definitions[:narrow].each do |handler|
63
71
  if handler.match(tokens, self.definitions)
72
+ puts "-narrow" if Chronic.debug
64
73
  #good_tokens = tokens.select { |o| !o.get_tag Separator }
65
74
  return self.send(handler.handler_method, tokens, options)
66
75
  end
67
76
  end
68
77
 
69
78
  # I guess you're out of luck!
79
+ puts "-none" if Chronic.debug
70
80
  return nil
71
81
  end
72
82
 
@@ -122,6 +132,19 @@ module Chronic
122
132
  end
123
133
  end
124
134
 
135
+ def handle_rdn_rmn_sd_t_tz_sy(tokens, options) #:nodoc:
136
+ month = tokens[1].get_tag(RepeaterMonthName).index
137
+ day = tokens[2].get_tag(ScalarDay).type
138
+ year = tokens[5].get_tag(ScalarYear).type
139
+
140
+ begin
141
+ day_start = Time.local(year, month, day)
142
+ day_or_time(day_start, [tokens[3]], options)
143
+ rescue ArgumentError
144
+ nil
145
+ end
146
+ end
147
+
125
148
  def handle_rmn_sd_sy(tokens, options) #:nodoc:
126
149
  month = tokens[0].get_tag(RepeaterMonthName).index
127
150
  day = tokens[1].get_tag(ScalarDay).type
@@ -333,22 +356,54 @@ module Chronic
333
356
 
334
357
  def dealias_and_disambiguate_times(tokens, options) #:nodoc:
335
358
  # handle aliases of am/pm
336
- # 5:00 in the morning => 5:00 am
337
- # 7:00 in the evening => 7:00 pm
338
- #ttokens = []
339
- tokens.each_with_index do |t0, i|
340
- t1 = tokens[i + 1]
341
- if t1 && (t1tag = t1.get_tag(RepeaterDayPortion)) && t0.get_tag(RepeaterTime)
342
- if [:morning].include?(t1tag.type)
343
- t1.untag(RepeaterDayPortion)
344
- t1.tag(RepeaterDayPortion.new(:am))
345
- elsif [:afternoon, :evening, :night].include?(t1tag.type)
346
- t1.untag(RepeaterDayPortion)
347
- t1.tag(RepeaterDayPortion.new(:pm))
348
- end
359
+ # 5:00 in the morning -> 5:00 am
360
+ # 7:00 in the evening -> 7:00 pm
361
+
362
+ day_portion_index = nil
363
+ tokens.each_with_index do |t, i|
364
+ if t.get_tag(RepeaterDayPortion)
365
+ day_portion_index = i
366
+ break
367
+ end
368
+ end
369
+
370
+ time_index = nil
371
+ tokens.each_with_index do |t, i|
372
+ if t.get_tag(RepeaterTime)
373
+ time_index = i
374
+ break
375
+ end
376
+ end
377
+
378
+ if (day_portion_index && time_index)
379
+ t1 = tokens[day_portion_index]
380
+ t1tag = t1.get_tag(RepeaterDayPortion)
381
+
382
+ if [:morning].include?(t1tag.type)
383
+ puts '--morning->am' if Chronic.debug
384
+ t1.untag(RepeaterDayPortion)
385
+ t1.tag(RepeaterDayPortion.new(:am))
386
+ elsif [:afternoon, :evening, :night].include?(t1tag.type)
387
+ puts "--#{t1tag.type}->pm" if Chronic.debug
388
+ t1.untag(RepeaterDayPortion)
389
+ t1.tag(RepeaterDayPortion.new(:pm))
349
390
  end
350
391
  end
351
- #tokens = ttokens
392
+
393
+ # tokens.each_with_index do |t0, i|
394
+ # t1 = tokens[i + 1]
395
+ # if t1 && (t1tag = t1.get_tag(RepeaterDayPortion)) && t0.get_tag(RepeaterTime)
396
+ # if [:morning].include?(t1tag.type)
397
+ # puts '--morning->am' if Chronic.debug
398
+ # t1.untag(RepeaterDayPortion)
399
+ # t1.tag(RepeaterDayPortion.new(:am))
400
+ # elsif [:afternoon, :evening, :night].include?(t1tag.type)
401
+ # puts "--#{t1tag.type}->pm" if Chronic.debug
402
+ # t1.untag(RepeaterDayPortion)
403
+ # t1.tag(RepeaterDayPortion.new(:pm))
404
+ # end
405
+ # end
406
+ # end
352
407
 
353
408
  # handle ambiguous times if :ambiguous_time_range is specified
354
409
  if options[:ambiguous_time_range] != :none
@@ -33,6 +33,7 @@ class Chronic::Repeater < Chronic::Tag #:nodoc:
33
33
  def self.scan_for_day_names(token)
34
34
  scanner = {/^m[ou]n(day)?$/ => :monday,
35
35
  /^t(ue|eu|oo|u|)s(day)?$/ => :tuesday,
36
+ /^tue$/ => :tuesday,
36
37
  /^we(dnes|nds|nns)day$/ => :wednesday,
37
38
  /^wed$/ => :wednesday,
38
39
  /^th(urs|ers)day$/ => :thursday,
@@ -19,14 +19,14 @@ class Chronic::RepeaterDay < Chronic::Repeater #:nodoc:
19
19
 
20
20
  case pointer
21
21
  when :future
22
- day_begin = Time.local(@now.year, @now.month, @now.day, @now.hour + 1)
23
- day_end = Time.local(@now.year, @now.month, @now.day) + DAY_SECONDS
22
+ day_begin = Time.construct(@now.year, @now.month, @now.day, @now.hour + 1)
23
+ day_end = Time.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
24
24
  when :past
25
- day_begin = Time.local(@now.year, @now.month, @now.day)
26
- day_end = Time.local(@now.year, @now.month, @now.day, @now.hour)
25
+ day_begin = Time.construct(@now.year, @now.month, @now.day)
26
+ day_end = Time.construct(@now.year, @now.month, @now.day, @now.hour)
27
27
  when :none
28
- day_begin = Time.local(@now.year, @now.month, @now.day)
29
- day_end = Time.local(@now.year, @now.month, @now.day) + DAY_SECONDS
28
+ day_begin = Time.construct(@now.year, @now.month, @now.day)
29
+ day_end = Time.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
30
30
  end
31
31
 
32
32
  Chronic::Span.new(day_begin, day_end)
@@ -7,7 +7,7 @@ class Chronic::RepeaterDayName < Chronic::Repeater #:nodoc:
7
7
  direction = pointer == :future ? 1 : -1
8
8
 
9
9
  if !@current_day_start
10
- @current_day_start = Time.local(@now.year, @now.month, @now.day)
10
+ @current_day_start = Time.construct(@now.year, @now.month, @now.day)
11
11
  @current_day_start += direction * DAY_SECONDS
12
12
 
13
13
  day_num = symbol_to_number(@type)
@@ -34,7 +34,7 @@ class Chronic::RepeaterDayName < Chronic::Repeater #:nodoc:
34
34
  end
35
35
 
36
36
  def to_s
37
- super << '-dayofweek-' << @type.to_s
37
+ super << '-dayname-' << @type.to_s
38
38
  end
39
39
 
40
40
  private
@@ -28,27 +28,27 @@ class Chronic::RepeaterDayPortion < Chronic::Repeater #:nodoc:
28
28
  full_day = 60 * 60 * 24
29
29
 
30
30
  if !@current_span
31
- now_seconds = @now - Time.local(@now.year, @now.month, @now.day)
31
+ now_seconds = @now - Time.construct(@now.year, @now.month, @now.day)
32
32
  if now_seconds < @range.begin
33
33
  case pointer
34
34
  when :future
35
- range_start = Time.local(@now.year, @now.month, @now.day) + @range.begin
35
+ range_start = Time.construct(@now.year, @now.month, @now.day) + @range.begin
36
36
  when :past
37
- range_start = Time.local(@now.year, @now.month, @now.day) - full_day + @range.begin
37
+ range_start = Time.construct(@now.year, @now.month, @now.day) - full_day + @range.begin
38
38
  end
39
39
  elsif now_seconds > @range.end
40
40
  case pointer
41
41
  when :future
42
- range_start = Time.local(@now.year, @now.month, @now.day) + full_day + @range.begin
42
+ range_start = Time.construct(@now.year, @now.month, @now.day) + full_day + @range.begin
43
43
  when :past
44
- range_start = Time.local(@now.year, @now.month, @now.day) + @range.begin
44
+ range_start = Time.construct(@now.year, @now.month, @now.day) + @range.begin
45
45
  end
46
46
  else
47
47
  case pointer
48
48
  when :future
49
- range_start = Time.local(@now.year, @now.month, @now.day) + full_day + @range.begin
49
+ range_start = Time.construct(@now.year, @now.month, @now.day) + full_day + @range.begin
50
50
  when :past
51
- range_start = Time.local(@now.year, @now.month, @now.day) - full_day + @range.begin
51
+ range_start = Time.construct(@now.year, @now.month, @now.day) - full_day + @range.begin
52
52
  end
53
53
  end
54
54
 
@@ -66,7 +66,7 @@ class Chronic::RepeaterDayPortion < Chronic::Repeater #:nodoc:
66
66
  def this(context = :future)
67
67
  super
68
68
 
69
- range_start = Time.local(@now.year, @now.month, @now.day) + @range.begin
69
+ range_start = Time.construct(@now.year, @now.month, @now.day) + @range.begin
70
70
  @current_span = Chronic::Span.new(range_start, range_start + (@range.end - @range.begin))
71
71
  end
72
72
 
@@ -33,7 +33,7 @@ class Chronic::RepeaterFortnight < Chronic::Repeater #:nodoc:
33
33
 
34
34
  case pointer
35
35
  when :future
36
- this_fortnight_start = Time.local(@now.year, @now.month, @now.day, @now.hour) + Chronic::RepeaterHour::HOUR_SECONDS
36
+ this_fortnight_start = Time.construct(@now.year, @now.month, @now.day, @now.hour) + Chronic::RepeaterHour::HOUR_SECONDS
37
37
  sunday_repeater = Chronic::RepeaterDayName.new(:sunday)
38
38
  sunday_repeater.start = @now
39
39
  sunday_repeater.this(:future)
@@ -41,7 +41,7 @@ class Chronic::RepeaterFortnight < Chronic::Repeater #:nodoc:
41
41
  this_fortnight_end = this_sunday_span.begin
42
42
  Chronic::Span.new(this_fortnight_start, this_fortnight_end)
43
43
  when :past
44
- this_fortnight_end = Time.local(@now.year, @now.month, @now.day, @now.hour)
44
+ this_fortnight_end = Time.construct(@now.year, @now.month, @now.day, @now.hour)
45
45
  sunday_repeater = Chronic::RepeaterDayName.new(:sunday)
46
46
  sunday_repeater.start = @now
47
47
  last_sunday_span = sunday_repeater.next(:past)
@@ -7,9 +7,9 @@ class Chronic::RepeaterHour < Chronic::Repeater #:nodoc:
7
7
  if !@current_hour_start
8
8
  case pointer
9
9
  when :future
10
- @current_hour_start = Time.local(@now.year, @now.month, @now.day, @now.hour + 1)
10
+ @current_hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour + 1)
11
11
  when :past
12
- @current_hour_start = Time.local(@now.year, @now.month, @now.day, @now.hour - 1)
12
+ @current_hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour - 1)
13
13
  end
14
14
  else
15
15
  direction = pointer == :future ? 1 : -1
@@ -27,10 +27,10 @@ class Chronic::RepeaterHour < Chronic::Repeater #:nodoc:
27
27
  hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min + 1)
28
28
  hour_end = Time.construct(@now.year, @now.month, @now.day, @now.hour + 1)
29
29
  when :past
30
- hour_start = Time.local(@now.year, @now.month, @now.day, @now.hour)
31
- hour_end = Time.local(@now.year, @now.month, @now.day, @now.hour, @now.min)
30
+ hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour)
31
+ hour_end = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
32
32
  when :none
33
- hour_start = Time.local(@now.year, @now.month, @now.day, @now.hour)
33
+ hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour)
34
34
  hour_end = hour_begin + HOUR_SECONDS
35
35
  end
36
36
 
@@ -4,9 +4,19 @@ class Chronic::RepeaterMinute < Chronic::Repeater #:nodoc:
4
4
  def next(pointer = :future)
5
5
  super
6
6
 
7
- direction = pointer == :future ? 1 : -1
7
+ if !@current_minute_start
8
+ case pointer
9
+ when :future
10
+ @current_minute_start = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min + 1)
11
+ when :past
12
+ @current_minute_start = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min - 1)
13
+ end
14
+ else
15
+ direction = pointer == :future ? 1 : -1
16
+ @current_minute_start += direction * MINUTE_SECONDS
17
+ end
8
18
 
9
- raise 'not implemented'
19
+ Chronic::Span.new(@current_minute_start, @current_minute_start + MINUTE_SECONDS)
10
20
  end
11
21
 
12
22
  def this(pointer = :future)
@@ -15,13 +25,13 @@ class Chronic::RepeaterMinute < Chronic::Repeater #:nodoc:
15
25
  case pointer
16
26
  when :future
17
27
  minute_begin = @now
18
- minute_end = Time.local(@now.year, @now.month, @now.day, @now.hour, @now.min)
28
+ minute_end = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
19
29
  when :past
20
- minute_begin = Time.local(@now.year, @now.month, @now.day, @now.hour, @now.min)
30
+ minute_begin = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
21
31
  minute_end = @now
22
32
  when :none
23
- minute_begin = Time.local(@now.year, @now.month, @now.day, @now.hour, @now.min)
24
- minute_end = Time.local(@now.year, @now.month, @now.day, @now.hour, @now.min) + MINUTE_SECONDS
33
+ minute_begin = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
34
+ minute_end = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min) + MINUTE_SECONDS
25
35
  end
26
36
 
27
37
  Chronic::Span.new(minute_begin, minute_end)
@@ -6,12 +6,12 @@ class Chronic::RepeaterMonth < Chronic::Repeater #:nodoc:
6
6
  super
7
7
 
8
8
  if !@current_month_start
9
- @current_month_start = offset_by(Time.local(@now.year, @now.month), 1, pointer)
9
+ @current_month_start = offset_by(Time.construct(@now.year, @now.month), 1, pointer)
10
10
  else
11
- @current_month_start = offset_by(Time.local(@current_month_start.year, @current_month_start.month), 1, pointer)
11
+ @current_month_start = offset_by(Time.construct(@current_month_start.year, @current_month_start.month), 1, pointer)
12
12
  end
13
13
 
14
- Chronic::Span.new(@current_month_start, Time.local(@current_month_start.year, @current_month_start.month + 1))
14
+ Chronic::Span.new(@current_month_start, Time.construct(@current_month_start.year, @current_month_start.month + 1))
15
15
  end
16
16
 
17
17
  def this(pointer = :future)
@@ -19,14 +19,14 @@ class Chronic::RepeaterMonth < Chronic::Repeater #:nodoc:
19
19
 
20
20
  case pointer
21
21
  when :future
22
- month_start = Time.local(@now.year, @now.month, @now.day + 1)
23
- month_end = self.offset_by(Time.local(@now.year, @now.month), 1, :future)
22
+ month_start = Time.construct(@now.year, @now.month, @now.day + 1)
23
+ month_end = self.offset_by(Time.construct(@now.year, @now.month), 1, :future)
24
24
  when :past
25
- month_start = Time.local(@now.year, @now.month)
26
- month_end = Time.local(@now.year, @now.month, @now.day)
25
+ month_start = Time.construct(@now.year, @now.month)
26
+ month_end = Time.construct(@now.year, @now.month, @now.day)
27
27
  when :none
28
- month_start = Time.local(@now.year, @now.month)
29
- month_end = self.offset_by(Time.local(@now.year, @now.month), 1, :future)
28
+ month_start = Time.construct(@now.year, @now.month)
29
+ month_end = self.offset_by(Time.construct(@now.year, @now.month), 1, :future)
30
30
  end
31
31
 
32
32
  Chronic::Span.new(month_start, month_end)
@@ -48,10 +48,14 @@ class Chronic::RepeaterMonth < Chronic::Repeater #:nodoc:
48
48
  new_year += 1
49
49
  new_month -= YEAR_MONTHS
50
50
  end
51
- Time.local(new_year, new_month, time.day, time.hour, time.min, time.sec)
51
+ Time.construct(new_year, new_month, time.day, time.hour, time.min, time.sec)
52
52
  end
53
53
 
54
54
  def width
55
55
  MONTH_SECONDS
56
56
  end
57
+
58
+ def to_s
59
+ super << '-month'
60
+ end
57
61
  end
@@ -9,30 +9,30 @@ class Chronic::RepeaterMonthName < Chronic::Repeater #:nodoc:
9
9
  case pointer
10
10
  when :future
11
11
  if @now.month < target_month
12
- @current_month_begin = Time.local(@now.year, target_month)
12
+ @current_month_begin = Time.construct(@now.year, target_month)
13
13
  else @now.month > target_month
14
- @current_month_begin = Time.local(@now.year + 1, target_month)
14
+ @current_month_begin = Time.construct(@now.year + 1, target_month)
15
15
  end
16
16
  when :none
17
17
  if @now.month <= target_month
18
- @current_month_begin = Time.local(@now.year, target_month)
18
+ @current_month_begin = Time.construct(@now.year, target_month)
19
19
  else @now.month > target_month
20
- @current_month_begin = Time.local(@now.year + 1, target_month)
20
+ @current_month_begin = Time.construct(@now.year + 1, target_month)
21
21
  end
22
22
  when :past
23
23
  if @now.month > target_month
24
- @current_month_begin = Time.local(@now.year, target_month)
24
+ @current_month_begin = Time.construct(@now.year, target_month)
25
25
  else @now.month < target_month
26
- @current_month_begin = Time.local(@now.year - 1, target_month)
26
+ @current_month_begin = Time.construct(@now.year - 1, target_month)
27
27
  end
28
28
  end
29
29
  @current_month_begin || raise("Current month should be set by now")
30
30
  else
31
31
  case pointer
32
32
  when :future
33
- @current_month_begin = Time.local(@current_month_begin.year + 1, @current_month_begin.month)
33
+ @current_month_begin = Time.construct(@current_month_begin.year + 1, @current_month_begin.month)
34
34
  when :past
35
- @current_month_begin = Time.local(@current_month_begin.year - 1, @current_month_begin.month)
35
+ @current_month_begin = Time.construct(@current_month_begin.year - 1, @current_month_begin.month)
36
36
  end
37
37
  end
38
38
 
@@ -47,7 +47,7 @@ class Chronic::RepeaterMonthName < Chronic::Repeater #:nodoc:
47
47
  next_month_month = cur_month_month + 1
48
48
  end
49
49
 
50
- Chronic::Span.new(@current_month_begin, Time.local(next_month_year, next_month_month))
50
+ Chronic::Span.new(@current_month_begin, Time.construct(next_month_year, next_month_month))
51
51
  end
52
52
 
53
53
  def this(pointer = :future)
@@ -70,7 +70,7 @@ class Chronic::RepeaterMonthName < Chronic::Repeater #:nodoc:
70
70
  end
71
71
 
72
72
  def to_s
73
- super << '-month-' << @type.to_s
73
+ super << '-monthname-' << @type.to_s
74
74
  end
75
75
 
76
76
  private
@@ -6,16 +6,16 @@ class Chronic::RepeaterYear < Chronic::Repeater #:nodoc:
6
6
  if !@current_year_start
7
7
  case pointer
8
8
  when :future
9
- @current_year_start = Time.local(@now.year + 1)
9
+ @current_year_start = Time.construct(@now.year + 1)
10
10
  when :past
11
- @current_year_start = Time.local(@now.year - 1)
11
+ @current_year_start = Time.construct(@now.year - 1)
12
12
  end
13
13
  else
14
14
  diff = pointer == :future ? 1 : -1
15
- @current_year_start = Time.local(@current_year_start.year + diff)
15
+ @current_year_start = Time.construct(@current_year_start.year + diff)
16
16
  end
17
17
 
18
- Chronic::Span.new(@current_year_start, Time.local(@current_year_start.year + 1))
18
+ Chronic::Span.new(@current_year_start, Time.construct(@current_year_start.year + 1))
19
19
  end
20
20
 
21
21
  def this(pointer = :future)
@@ -23,14 +23,14 @@ class Chronic::RepeaterYear < Chronic::Repeater #:nodoc:
23
23
 
24
24
  case pointer
25
25
  when :future
26
- this_year_start = Time.local(@now.year, @now.month, @now.day) + Chronic::RepeaterDay::DAY_SECONDS
27
- this_year_end = Time.local(@now.year + 1, 1, 1)
26
+ this_year_start = Time.construct(@now.year, @now.month, @now.day) + Chronic::RepeaterDay::DAY_SECONDS
27
+ this_year_end = Time.construct(@now.year + 1, 1, 1)
28
28
  when :past
29
- this_year_start = Time.local(@now.year, 1, 1)
30
- this_year_end = Time.local(@now.year, @now.month, @now.day)
29
+ this_year_start = Time.construct(@now.year, 1, 1)
30
+ this_year_end = Time.construct(@now.year, @now.month, @now.day)
31
31
  when :none
32
- this_year_start = Time.local(@now.year, 1, 1)
33
- this_year_end = Time.local(@now.year + 1, 1, 1)
32
+ this_year_start = Time.construct(@now.year, 1, 1)
33
+ this_year_end = Time.construct(@now.year + 1, 1, 1)
34
34
  end
35
35
 
36
36
  Chronic::Span.new(this_year_start, this_year_end)
@@ -40,10 +40,10 @@ class Chronic::RepeaterYear < Chronic::Repeater #:nodoc:
40
40
  direction = pointer == :future ? 1 : -1
41
41
 
42
42
  sb = span.begin
43
- new_begin = Time.local(sb.year + (amount * direction), sb.month, sb.day, sb.hour, sb.min, sb.sec)
43
+ new_begin = Time.construct(sb.year + (amount * direction), sb.month, sb.day, sb.hour, sb.min, sb.sec)
44
44
 
45
45
  se = span.end
46
- new_end = Time.local(se.year + (amount * direction), se.month, se.day, se.hour, se.min, se.sec)
46
+ new_end = Time.construct(se.year + (amount * direction), se.month, se.day, se.hour, se.min, se.sec)
47
47
 
48
48
  Chronic::Span.new(new_begin, new_end)
49
49
  end
@@ -0,0 +1,50 @@
1
+ require 'chronic'
2
+ require 'test/unit'
3
+
4
+ class TestTime < Test::Unit::TestCase
5
+
6
+ def setup
7
+ end
8
+
9
+ def test_normal
10
+ assert_equal Time.local(2006, 1, 2, 0, 0, 0), Time.construct(2006, 1, 2, 0, 0, 0)
11
+ assert_equal Time.local(2006, 1, 2, 3, 0, 0), Time.construct(2006, 1, 2, 3, 0, 0)
12
+ assert_equal Time.local(2006, 1, 2, 3, 4, 0), Time.construct(2006, 1, 2, 3, 4, 0)
13
+ assert_equal Time.local(2006, 1, 2, 3, 4, 5), Time.construct(2006, 1, 2, 3, 4, 5)
14
+ end
15
+
16
+ def test_second_overflow
17
+ assert_equal Time.local(2006, 1, 1, 0, 1, 30), Time.construct(2006, 1, 1, 0, 0, 90)
18
+ assert_equal Time.local(2006, 1, 1, 0, 5, 0), Time.construct(2006, 1, 1, 0, 0, 300)
19
+ end
20
+
21
+ def test_minute_overflow
22
+ assert_equal Time.local(2006, 1, 1, 1, 30), Time.construct(2006, 1, 1, 0, 90)
23
+ assert_equal Time.local(2006, 1, 1, 5), Time.construct(2006, 1, 1, 0, 300)
24
+ end
25
+
26
+ def test_hour_overflow
27
+ assert_equal Time.local(2006, 1, 2, 12), Time.construct(2006, 1, 1, 36)
28
+ assert_equal Time.local(2006, 1, 7), Time.construct(2006, 1, 1, 144)
29
+ end
30
+
31
+ def test_day_overflow
32
+ assert_equal Time.local(2006, 2, 1), Time.construct(2006, 1, 32)
33
+ assert_equal Time.local(2006, 3, 5), Time.construct(2006, 2, 33)
34
+ assert_equal Time.local(2004, 3, 4), Time.construct(2004, 2, 33)
35
+ assert_equal Time.local(2000, 3, 5), Time.construct(2000, 2, 33)
36
+
37
+ assert_nothing_raised do
38
+ Time.construct(2006, 1, 56)
39
+ end
40
+
41
+ assert_raise(RuntimeError) do
42
+ Time.construct(2006, 1, 57)
43
+ end
44
+ end
45
+
46
+ def test_month_overflow
47
+ assert_equal Time.local(2006, 1), Time.construct(2005, 13)
48
+ assert_equal Time.local(2005, 12), Time.construct(2000, 72)
49
+ end
50
+ end
@@ -136,6 +136,15 @@ class TestParsing < Test::Unit::TestCase
136
136
  time = parse_now("2006-08-20 15:30.30")
137
137
  assert_equal Time.local(2006, 8, 20, 15, 30, 30), time
138
138
 
139
+ # rdn_rm_rd_rt_rtz_ry
140
+
141
+ time = parse_now("Mon Apr 02 17:00:00 PDT 2007")
142
+ assert_equal Time.local(2007, 4, 2, 17), time
143
+
144
+ now = Time.now
145
+ time = parse_now(now.to_s)
146
+ assert_equal now.to_s, time.to_s
147
+
139
148
  # rm_sd_rt
140
149
 
141
150
  #time = parse_now("jan 5 13:00")
@@ -152,11 +161,18 @@ class TestParsing < Test::Unit::TestCase
152
161
  time = parse_now("1800-08-20")
153
162
  assert_equal nil, time
154
163
  end
164
+
165
+ def test_foo
166
+ Chronic.parse('two months ago this friday')
167
+ end
155
168
 
156
169
  def test_parse_guess_r
157
170
  time = parse_now("friday")
158
171
  assert_equal Time.local(2006, 8, 18, 12), time
159
172
 
173
+ time = parse_now("tue")
174
+ assert_equal Time.local(2006, 8, 22, 12), time
175
+
160
176
  time = parse_now("5")
161
177
  assert_equal Time.local(2006, 8, 16, 17), time
162
178
 
@@ -214,6 +230,9 @@ class TestParsing < Test::Unit::TestCase
214
230
 
215
231
  time = parse_now("sunday 6am")
216
232
  assert_equal Time.local(2006, 8, 20, 6), time
233
+
234
+ time = parse_now("friday evening at 7")
235
+ assert_equal Time.local(2006, 8, 18, 19), time
217
236
  end
218
237
 
219
238
  def test_parse_guess_gr
@@ -233,6 +252,9 @@ class TestParsing < Test::Unit::TestCase
233
252
  time = parse_now("this month", :context => :past)
234
253
  assert_equal Time.local(2006, 8, 8, 12), time
235
254
 
255
+ time = Chronic.parse("next month", :now => Time.local(2006, 11, 15))
256
+ assert_equal Time.local(2006, 12, 16, 12), time
257
+
236
258
  # month name
237
259
 
238
260
  time = parse_now("last november")
@@ -310,6 +332,11 @@ class TestParsing < Test::Unit::TestCase
310
332
  time = parse_now("tonight")
311
333
  assert_equal Time.local(2006, 8, 16, 22), time
312
334
 
335
+ # minute
336
+
337
+ time = parse_now("next minute")
338
+ assert_equal Time.local(2006, 8, 16, 14, 1, 30), time
339
+
313
340
  # second
314
341
 
315
342
  time = parse_now("this second")
@@ -355,8 +382,17 @@ class TestParsing < Test::Unit::TestCase
355
382
 
356
383
  time = parse_now("last week tuesday")
357
384
  assert_equal Time.local(2006, 8, 8, 12), time
385
+
386
+ time = parse_now("tonight at 7")
387
+ assert_equal Time.local(2006, 8, 16, 19), time
388
+
389
+ time = parse_now("tonight 7")
390
+ assert_equal Time.local(2006, 8, 16, 19), time
391
+
392
+ time = parse_now("7 tonight")
393
+ assert_equal Time.local(2006, 8, 16, 19), time
358
394
  end
359
-
395
+
360
396
  def test_parse_guess_grrr
361
397
  time = parse_now("today at 6:00pm")
362
398
  assert_equal Time.local(2006, 8, 16, 18), time
@@ -369,6 +405,12 @@ class TestParsing < Test::Unit::TestCase
369
405
 
370
406
  time = parse_now("yesterday at 4:00pm")
371
407
  assert_equal Time.local(2006, 8, 15, 16), time
408
+
409
+ time = parse_now("tomorrow evening at 7")
410
+ assert_equal Time.local(2006, 8, 17, 19), time
411
+
412
+ time = parse_now("tomorrow morning at 5:30")
413
+ assert_equal Time.local(2006, 8, 17, 5, 30), time
372
414
  end
373
415
 
374
416
  def test_parse_guess_rgr
metadata CHANGED
@@ -3,12 +3,12 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: chronic
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.0
7
- date: 2007-03-20 00:00:00 -07:00
6
+ version: 0.2.1
7
+ date: 2007-04-21 00:00:00 -07:00
8
8
  summary: A natural language date parser
9
9
  require_paths:
10
10
  - lib
11
- email: ryand-ruby@zenspider.com
11
+ email: tom@rubyisawesome.com
12
12
  homepage: "\thttp://chronic.rubyforge.org/"
13
13
  rubyforge_project: chronic
14
14
  description: Chronic is a natural language date/time parser written in pure Ruby. See below for the wide variety of formats Chronic will parse.
@@ -27,7 +27,7 @@ signing_key:
27
27
  cert_chain:
28
28
  post_install_message:
29
29
  authors:
30
- - Ryan Davis
30
+ - Tom Preston-Werner
31
31
  files:
32
32
  - History.txt
33
33
  - Manifest.txt
@@ -89,6 +89,7 @@ test_files:
89
89
  - test/test_RepeaterWeekend.rb
90
90
  - test/test_RepeaterYear.rb
91
91
  - test/test_Span.rb
92
+ - test/test_Time.rb
92
93
  - test/test_Token.rb
93
94
  rdoc_options: []
94
95