chronic 0.2.0 → 0.2.1

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.
@@ -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