Permalink
Browse files

numerizer, time overflow

  • Loading branch information...
mojombo committed Apr 1, 2007
1 parent fc50735 commit 044fbd787e3de2509dd69d386df78aa6c2acfddb
View
@@ -1,3 +1,8 @@
+= 0.2.0 2007-03-20
+
+* fixed time overflow issue
+* implemented numerizer, allowing the use of number words (e.g. five weeks ago) (thanks shalev!)
+
= 0.1.6 2006-01-15
* added 'weekend' support (eventualbuddha)
View
@@ -26,17 +26,19 @@ lib/chronic/repeaters/repeater_weekend.rb
lib/chronic/repeaters/repeater_year.rb
lib/chronic/scalar.rb
lib/chronic/separator.rb
-test/parse_numbers.rb
+lib/numerizer/numerizer.rb
test/suite.rb
test/test_Chronic.rb
test/test_Handler.rb
+test/test_Numerizer.rb
test/test_RepeaterDayName.rb
test/test_RepeaterFortnight.rb
test/test_RepeaterHour.rb
test/test_RepeaterMonth.rb
test/test_RepeaterMonthName.rb
test/test_RepeaterTime.rb
test/test_RepeaterWeek.rb
+test/test_RepeaterWeekend.rb
test/test_RepeaterYear.rb
test/test_Span.rb
test/test_Token.rb
View
@@ -7,35 +7,13 @@ require './lib/chronic.rb'
Hoe.new('chronic', Chronic::VERSION) do |p|
p.rubyforge_name = 'chronic'
p.summary = 'A natural language date parser'
+ p.author = 'Tom Preston-Werner'
+ p.email = 'tom@rubyisawesome.com'
p.description = p.paragraphs_of('README.txt', 2).join("\n\n")
p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
p.need_tar = false
p.extra_deps = []
end
-# vim: syntax=Ruby
-
-__END__
-
-require 'rubygems'
-
-SPEC = Gem::Specification.new do |s|
- s.name = 'chronic'
- s.version = '0.1.5'
- s.author = 'Tom Preston-Werner'
- s.email = 'tom@rubyisawesome.com'
- s.homepage = 'http://chronic.rubyforge.org'
- s.platform = Gem::Platform::RUBY
- s.summary = "A natural language date parser"
- candidates = Dir["{lib,test}/**/*"]
- s.files = candidates.delete_if do |item|
- item.include?('.svn')
- end
- s.require_path = "lib"
- s.autorequire = "chronic"
- s.test_file = "test/suite.rb"
- s.has_rdoc = true
- s.extra_rdoc_files = ['README']
- s.rdoc_options << '--main' << 'README'
-end
+# vim: syntax=Ruby
View
@@ -7,6 +7,8 @@
#
#=============================================================================
+$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
+
require 'chronic/chronic'
require 'chronic/handlers'
@@ -33,8 +35,10 @@
require 'chronic/ordinal'
require 'chronic/separator'
+require 'numerizer/numerizer'
+
module Chronic
- VERSION = "0.1.6"
+ VERSION = "0.2.0"
def self.debug; false; end
end
@@ -44,4 +48,50 @@ def self.debug; false; end
def p(val)
p_orig val
puts
+end
+
+class Time
+ def self.construct(year, month = 1, day = 1, hour = 0, minute = 0, second = 0)
+ if second >= 60
+ minute += second / 60
+ second = second % 60
+ end
+
+ if minute >= 60
+ hour += minute / 60
+ minute = minute % 60
+ end
+
+ if hour >= 24
+ day += hour / 24
+ hour = hour % 24
+ end
+
+ # determine if there is a day overflow. this is complicated by our crappy calendar
+ # system (non-constant number of days per month)
+ day <= 56 || raise("day must be no more than 56 (makes month resolution easier)")
+ if day > 28
+ # no month ever has fewer than 28 days, so only do this if necessary
+ leap_year = (year % 4 == 0) && !(year % 100 == 0)
+ leap_year_month_days = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+ common_year_month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+ days_this_month = leap_year ? leap_year_month_days[month - 1] : common_year_month_days[month - 1]
+ if day > days_this_month
+ month += day / days_this_month
+ day = day % days_this_month
+ end
+ end
+
+ if month > 12
+ if month % 12 == 0
+ year += (month - 12) / 12
+ month = 12
+ else
+ year += month / 12
+ month = month % 12
+ end
+ end
+
+ Time.local(year, month, day, hour, minute, second)
+ end
end
View
@@ -101,6 +101,7 @@ def parse(text, specified_options = {})
# ordinals (third => 3rd)
def pre_normalize(text) #:nodoc:
normalized_text = text.to_s.downcase
+ normalized_text = numericize_numbers(normalized_text)
normalized_text.gsub!(/['"\.]/, '')
normalized_text.gsub!(/([\/\-\,\@])/) { ' ' + $1 + ' ' }
normalized_text.gsub!(/\btoday\b/, 'this day')
@@ -118,15 +119,12 @@ def pre_normalize(text) #:nodoc:
normalized_text.gsub!(/\btonight\b/, 'this night')
normalized_text.gsub!(/(?=\w)([ap]m|oclock)\b/, ' \1')
normalized_text.gsub!(/\b(hence|after|from)\b/, 'future')
- normalized_text.gsub!(/\ba\b/, '1')
- normalized_text.gsub!(/\s+/, ' ')
- normalized_text = numericize_numbers(normalized_text)
normalized_text = numericize_ordinals(normalized_text)
end
# Convert number words to numbers (three => 3)
def numericize_numbers(text) #:nodoc:
- text
+ Numerizer.numerize(text)
end
# Convert ordinal words to numeric ordinals (third => 3rd)
View
@@ -214,17 +214,19 @@ def handle_srp(tokens, span, options) #:nodoc:
def handle_s_r_p(tokens, options) #:nodoc:
repeater = tokens[1].get_tag(Repeater)
- span =
- case true
- when [RepeaterYear, RepeaterSeason, RepeaterSeasonName, RepeaterMonth, RepeaterMonthName, RepeaterFortnight, RepeaterWeek].include?(repeater.class)
- self.parse("this hour", :guess => false, :now => @now)
- when [RepeaterWeekend, RepeaterDay, RepeaterDayName, RepeaterDayPortion, RepeaterHour].include?(repeater.class)
- self.parse("this minute", :guess => false, :now => @now)
- when [RepeaterMinute, RepeaterSecond].include?(repeater.class)
- self.parse("this second", :guess => false, :now => @now)
- else
- raise(ChronicPain, "Invalid repeater: #{repeater.class}")
- end
+ # span =
+ # case true
+ # when [RepeaterYear, RepeaterSeason, RepeaterSeasonName, RepeaterMonth, RepeaterMonthName, RepeaterFortnight, RepeaterWeek].include?(repeater.class)
+ # self.parse("this hour", :guess => false, :now => @now)
+ # when [RepeaterWeekend, RepeaterDay, RepeaterDayName, RepeaterDayPortion, RepeaterHour].include?(repeater.class)
+ # self.parse("this minute", :guess => false, :now => @now)
+ # when [RepeaterMinute, RepeaterSecond].include?(repeater.class)
+ # self.parse("this second", :guess => false, :now => @now)
+ # else
+ # raise(ChronicPain, "Invalid repeater: #{repeater.class}")
+ # end
+
+ span = self.parse("this second", :guess => false, :now => @now)
self.handle_srp(tokens, span, options)
end
View
@@ -10,9 +10,9 @@ def self.scan(tokens)
end
def self.scan_for_all(token)
- scanner = {/past/ => :past,
- /future/ => :future,
- /in/ => :future}
+ scanner = {/\bpast\b/ => :past,
+ /\bfuture\b/ => :future,
+ /\bin\b/ => :future}
scanner.keys.each do |scanner_item|
return self.new(scanner[scanner_item]) if scanner_item =~ token.word
end
@@ -19,14 +19,14 @@ def this(pointer = :future)
case pointer
when :future
- day_begin = Time.local(@now.year, @now.month, @now.day, @now.hour + 1)
- day_end = Time.local(@now.year, @now.month, @now.day) + DAY_SECONDS
+ day_begin = Time.construct(@now.year, @now.month, @now.day, @now.hour + 1)
+ day_end = Time.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
when :past
- day_begin = Time.local(@now.year, @now.month, @now.day)
- day_end = Time.local(@now.year, @now.month, @now.day, @now.hour)
+ day_begin = Time.construct(@now.year, @now.month, @now.day)
+ day_end = Time.construct(@now.year, @now.month, @now.day, @now.hour)
when :none
- day_begin = Time.local(@now.year, @now.month, @now.day)
- day_end = Time.local(@now.year, @now.month, @now.day) + DAY_SECONDS
+ day_begin = Time.construct(@now.year, @now.month, @now.day)
+ day_end = Time.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
end
Chronic::Span.new(day_begin, day_end)
@@ -7,7 +7,7 @@ def next(pointer)
direction = pointer == :future ? 1 : -1
if !@current_day_start
- @current_day_start = Time.local(@now.year, @now.month, @now.day)
+ @current_day_start = Time.construct(@now.year, @now.month, @now.day)
@current_day_start += direction * DAY_SECONDS
day_num = symbol_to_number(@type)
@@ -28,27 +28,27 @@ def next(pointer)
full_day = 60 * 60 * 24
if !@current_span
- now_seconds = @now - Time.local(@now.year, @now.month, @now.day)
+ now_seconds = @now - Time.construct(@now.year, @now.month, @now.day)
if now_seconds < @range.begin
case pointer
when :future
- range_start = Time.local(@now.year, @now.month, @now.day) + @range.begin
+ range_start = Time.construct(@now.year, @now.month, @now.day) + @range.begin
when :past
- range_start = Time.local(@now.year, @now.month, @now.day) - full_day + @range.begin
+ range_start = Time.construct(@now.year, @now.month, @now.day) - full_day + @range.begin
end
elsif now_seconds > @range.end
case pointer
when :future
- range_start = Time.local(@now.year, @now.month, @now.day) + full_day + @range.begin
+ range_start = Time.construct(@now.year, @now.month, @now.day) + full_day + @range.begin
when :past
- range_start = Time.local(@now.year, @now.month, @now.day) + @range.begin
+ range_start = Time.construct(@now.year, @now.month, @now.day) + @range.begin
end
else
case pointer
when :future
- range_start = Time.local(@now.year, @now.month, @now.day) + full_day + @range.begin
+ range_start = Time.construct(@now.year, @now.month, @now.day) + full_day + @range.begin
when :past
- range_start = Time.local(@now.year, @now.month, @now.day) - full_day + @range.begin
+ range_start = Time.construct(@now.year, @now.month, @now.day) - full_day + @range.begin
end
end
@@ -66,7 +66,7 @@ def next(pointer)
def this(context = :future)
super
- range_start = Time.local(@now.year, @now.month, @now.day) + @range.begin
+ range_start = Time.construct(@now.year, @now.month, @now.day) + @range.begin
@current_span = Chronic::Span.new(range_start, range_start + (@range.end - @range.begin))
end
@@ -33,15 +33,15 @@ def this(pointer = :future)
case pointer
when :future
- this_fortnight_start = Time.local(@now.year, @now.month, @now.day, @now.hour) + Chronic::RepeaterHour::HOUR_SECONDS
+ this_fortnight_start = Time.construct(@now.year, @now.month, @now.day, @now.hour) + Chronic::RepeaterHour::HOUR_SECONDS
sunday_repeater = Chronic::RepeaterDayName.new(:sunday)
sunday_repeater.start = @now
sunday_repeater.this(:future)
this_sunday_span = sunday_repeater.this(:future)
this_fortnight_end = this_sunday_span.begin
Chronic::Span.new(this_fortnight_start, this_fortnight_end)
when :past
- this_fortnight_end = Time.local(@now.year, @now.month, @now.day, @now.hour)
+ this_fortnight_end = Time.construct(@now.year, @now.month, @now.day, @now.hour)
sunday_repeater = Chronic::RepeaterDayName.new(:sunday)
sunday_repeater.start = @now
last_sunday_span = sunday_repeater.next(:past)
@@ -7,9 +7,9 @@ def next(pointer)
if !@current_hour_start
case pointer
when :future
- @current_hour_start = Time.local(@now.year, @now.month, @now.day, @now.hour + 1)
+ @current_hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour + 1)
when :past
- @current_hour_start = Time.local(@now.year, @now.month, @now.day, @now.hour - 1)
+ @current_hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour - 1)
end
else
direction = pointer == :future ? 1 : -1
@@ -24,13 +24,13 @@ def this(pointer = :future)
case pointer
when :future
- hour_start = Time.local(@now.year, @now.month, @now.day, @now.hour, @now.min + 1)
- hour_end = Time.local(@now.year, @now.month, @now.day, @now.hour + 1)
+ hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min + 1)
+ hour_end = Time.construct(@now.year, @now.month, @now.day, @now.hour + 1)
when :past
- hour_start = Time.local(@now.year, @now.month, @now.day, @now.hour)
- hour_end = Time.local(@now.year, @now.month, @now.day, @now.hour, @now.min)
+ hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour)
+ hour_end = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
when :none
- hour_start = Time.local(@now.year, @now.month, @now.day, @now.hour)
+ hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour)
hour_end = hour_begin + HOUR_SECONDS
end
@@ -15,13 +15,13 @@ def this(pointer = :future)
case pointer
when :future
minute_begin = @now
- minute_end = Time.local(@now.year, @now.month, @now.day, @now.hour, @now.min)
+ minute_end = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
when :past
- minute_begin = Time.local(@now.year, @now.month, @now.day, @now.hour, @now.min)
+ minute_begin = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
minute_end = @now
when :none
- minute_begin = Time.local(@now.year, @now.month, @now.day, @now.hour, @now.min)
- minute_end = Time.local(@now.year, @now.month, @now.day, @now.hour, @now.min) + MINUTE_SECONDS
+ minute_begin = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
+ minute_end = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min) + MINUTE_SECONDS
end
Chronic::Span.new(minute_begin, minute_end)
@@ -6,27 +6,27 @@ def next(pointer)
super
if !@current_month_start
- @current_month_start = offset_by(Time.local(@now.year, @now.month), 1, pointer)
+ @current_month_start = offset_by(Time.construct(@now.year, @now.month), 1, pointer)
else
- @current_month_start = offset_by(Time.local(@current_month_start.year, @current_month_start.month), 1, pointer)
+ @current_month_start = offset_by(Time.construct(@current_month_start.year, @current_month_start.month), 1, pointer)
end
- Chronic::Span.new(@current_month_start, Time.local(@current_month_start.year, @current_month_start.month + 1))
+ Chronic::Span.new(@current_month_start, Time.construct(@current_month_start.year, @current_month_start.month + 1))
end
def this(pointer = :future)
super
case pointer
when :future
- month_start = Time.local(@now.year, @now.month, @now.day + 1)
- month_end = self.offset_by(Time.local(@now.year, @now.month), 1, :future)
+ month_start = Time.construct(@now.year, @now.month, @now.day + 1)
+ month_end = self.offset_by(Time.construct(@now.year, @now.month), 1, :future)
when :past
- month_start = Time.local(@now.year, @now.month)
- month_end = Time.local(@now.year, @now.month, @now.day)
+ month_start = Time.construct(@now.year, @now.month)
+ month_end = Time.construct(@now.year, @now.month, @now.day)
when :none
- month_start = Time.local(@now.year, @now.month)
- month_end = self.offset_by(Time.local(@now.year, @now.month), 1, :future)
+ month_start = Time.construct(@now.year, @now.month)
+ month_end = self.offset_by(Time.construct(@now.year, @now.month), 1, :future)
end
Chronic::Span.new(month_start, month_end)
@@ -48,7 +48,7 @@ def offset_by(time, amount, pointer)
new_year += 1
new_month -= YEAR_MONTHS
end
- Time.local(new_year, new_month, time.day, time.hour, time.min, time.sec)
+ Time.construct(new_year, new_month, time.day, time.hour, time.min, time.sec)
end
def width
Oops, something went wrong.

0 comments on commit 044fbd7

Please sign in to comment.