Permalink
Browse files

Significant performance enhancements for calendars

  • Loading branch information...
1 parent bf113e2 commit cf38a51349ca10255de4cf3e21f2c30cbbef4dd1 Cameron Dutro committed Jan 31, 2013
@@ -7,7 +7,12 @@ module TwitterCldr
module Formatters
class DateFormatter < DateTimeFormatter
def initialize(options = {})
- @tokenizer = TwitterCldr::Tokenizers::DateTokenizer.new(:locale => extract_locale(options), :calendar_type => options[:calendar_type])
+ locale = extract_locale(options)
+ cache_key = TwitterCldr::Utils.compute_cache_key("date", locale, options[:calendar_type])
+ @tokenizer = tokenizer_cache[cache_key] ||= TwitterCldr::Tokenizers::DateTokenizer.new(
+ :locale => locale,
+ :calendar_type => options[:calendar_type]
+ )
end
end
end
@@ -42,7 +42,7 @@ class DateTimeFormatter < Base
def initialize(options = {})
locale = extract_locale(options)
- cache_key = TwitterCldr::Utils.compute_cache_key(locale, options[:calendar_type])
+ cache_key = TwitterCldr::Utils.compute_cache_key("datetime", locale, options[:calendar_type])
@tokenizer = tokenizer_cache[cache_key] ||= TwitterCldr::Tokenizers::DateTimeTokenizer.new(
:locale => locale,
:calendar_type => options[:calendar_type]
@@ -7,7 +7,12 @@ module TwitterCldr
module Formatters
class TimeFormatter < DateTimeFormatter
def initialize(options = {})
- @tokenizer = TwitterCldr::Tokenizers::TimeTokenizer.new(:locale => extract_locale(options), :calendar_type => options[:calendar_type])
+ locale = extract_locale(options)
+ cache_key = TwitterCldr::Utils.compute_cache_key("time", locale, options[:calendar_type])
+ @tokenizer = tokenizer_cache[cache_key] ||= TwitterCldr::Tokenizers::TimeTokenizer.new(
+ :locale => locale,
+ :calendar_type => options[:calendar_type]
+ )
end
end
end
@@ -21,14 +21,18 @@ class TimespanFormatter < Base
}
def initialize(options = {})
+ locale = extract_locale(options)
@direction = options[:direction]
- @tokenizer = TwitterCldr::Tokenizers::TimespanTokenizer.new(:locale => extract_locale(options))
+ cache_key = TwitterCldr::Utils.compute_cache_key(locale)
+ @tokenizer = tokenizer_cache[cache_key] ||= TwitterCldr::Tokenizers::TimespanTokenizer.new(
+ :locale => locale
+ )
end
def format(seconds, fmt_options = {})
options = fmt_options.dup
options[:type] ||= DEFAULT_TYPE
- options[:direction] ||= @direction || (seconds < 0 ? :ago : :until)
+ options[:direction] ||= (seconds < 0 ? :ago : :until)
options[:unit] ||= calculate_unit(seconds.abs, options)
options[:number] = calculate_time(seconds.abs, options[:unit])
@@ -38,6 +42,12 @@ def format(seconds, fmt_options = {})
strings.join.gsub(/\{[0-9]\}/, options[:number].to_s)
end
+ protected
+
+ def tokenizer_cache
+ @@tokenizer_cache ||= {}
+ end
+
def calculate_unit(seconds, unit_options = {})
options = unit_options.dup
options[:approximate] = false if options[:approximate].nil?
@@ -12,6 +12,19 @@ def to_datetime(time)
LocalizedDateTime.new(DateTime.parse("#{@base_obj.strftime("%Y-%m-%d")}T#{time_obj.strftime("%H:%M:%S%z")}"), @locale, :calendar_type => @calendar_type)
end
+ def to_time(base = Time.now)
+ time = Time.gm(
+ @base_obj.year,
+ @base_obj.month,
+ @base_obj.day,
+ base.hour,
+ base.min,
+ base.sec
+ )
+
+ LocalizedTime.new(time, @locale, :calendar_type => @calendar_type)
+ end
+
protected
def formatter_const
@@ -27,15 +27,15 @@ def to_timespan(options = {})
end
def ago(options = {})
- base_time = options[:base_time] || Time.now
- seconds = self.to_time.base_obj.to_i - base_time.to_i
+ base_time = (options[:base_time] || Time.now).gmtime
+ seconds = self.to_time(base_time).base_obj.gmtime.to_i - base_time.to_i
raise ArgumentError.new('Start date is after end date. Consider using "until" function.') if seconds > 0
TwitterCldr::Localized::LocalizedTimespan.new(seconds, options.merge(:locale => @locale))
end
def until(options = {})
- base_time = options[:base_time] || Time.now
- seconds = self.to_time.base_obj.to_i - base_time.to_i
+ base_time = (options[:base_time] || Time.now).gmtime
+ seconds = self.to_time(base_time).base_obj.gmtime.to_i - base_time.to_i
raise ArgumentError.new('End date is before start date. Consider using "ago" function.') if seconds < 0
TwitterCldr::Localized::LocalizedTimespan.new(seconds, options.merge(:locale => @locale))
end
@@ -49,11 +49,24 @@ def to_s(options = {})
end
def to_date
- LocalizedDate.new(Date.parse(@base_obj.strftime("%Y-%m-%dT%H:%M:%S%z")), @locale, :calendar_type => @calendar_type)
+ date = Date.new(@base_obj.year, @base_obj.month, @base_obj.day)
+ LocalizedDate.new(date, @locale, :calendar_type => @calendar_type)
end
- def to_time
- LocalizedTime.new(Time.parse(@base_obj.strftime("%Y-%m-%dT%H:%M:%S%z")), @locale, :calendar_type => @calendar_type)
+ def to_time(base = Time.now)
+ utc_dt = @base_obj.new_offset(0)
+
+ time = Time.gm(
+ utc_dt.year,
+ utc_dt.month,
+ utc_dt.day,
+ utc_dt.hour,
+ utc_dt.min,
+ utc_dt.sec,
+ utc_dt.sec_fraction * (RUBY_VERSION < '1.9' ? 86400000000 : 1000000)
+ )
+
+ LocalizedTime.new(time, @locale, :calendar_type => @calendar_type)
end
protected
@@ -12,6 +12,18 @@ def to_datetime(date)
LocalizedDateTime.new(DateTime.parse("#{date_obj.strftime("%Y-%m-%d")}T#{@base_obj.strftime("%H:%M:%S%z")}"), @locale, :calendar_type => @calendar_type)
end
+ def to_time(base_time = Time.now)
+ self
+ end
+
+ def gmtime
+ LocalizedTime.new(@base_obj.gmtime, @locale, :calendar_type => @calendar_type)
+ end
+
+ def localtime
+ LocalizedTime.new(@base_obj.localtime, @locale, :calendar_type => @calendar_type)
+ end
+
protected
def formatter_const
@@ -34,7 +34,7 @@ def tokenize_format(text)
content = token.match(regexes[token_type][:content])[1]
ret << CompositeToken.new(tokenize_format(content))
else
- ret << Token.new(:value => token, :type => token_type)
+ ret << Token.new(:value => token, :type => token_type) # .gsub(/\A\'/, "").chomp("'")
end
end
ret
@@ -75,7 +75,7 @@ def tokens_for_pattern(pattern, path, additional_cache_key_params = [])
end
def tokens_with_placeholders_for(key)
- cache_key = compute_cache_key(@locale, key, type)
+ cache_key = TwitterCldr::Utils.compute_cache_key(@locale, key, type)
unless token_cache.include?(cache_key)
result = []
@@ -93,14 +93,6 @@ def token_cache
@@token_cache ||= {}
end
- def compute_cache_key(*pieces)
- if pieces && pieces.size > 0
- pieces.join("|").hash
- else
- 0
- end
- end
-
def init_placeholders
@placeholders = {}
end
@@ -55,13 +55,18 @@ def calendar
end
def additional_format_selector
- @additional_format_selector ||= AdditionalDateFormatSelector.new(
+ cache_key = TwitterCldr::Utils.compute_cache_key(@locale, @calendar_type)
+ @additional_format_selector = format_selector_cache[cache_key] ||= AdditionalDateFormatSelector.new(
@resource[:calendars][@calendar_type][:additional_formats]
)
end
protected
+ def format_selector_cache
+ @@format_selector_cache ||= {}
+ end
+
def merge_token_type_regexes(first, second)
TwitterCldr::Utils.deep_merge_hash(first, second) do |left, right|
if right.is_a?(Regexp) && left.is_a?(Regexp)
@@ -56,6 +56,7 @@ def initialize(options = {})
end
def tokens(options = {})
+ # tokens_with_placeholders_for(full_path(:none, :second, :short, :two))
path = full_path(options[:direction], options[:unit], options[:type])
pluralization = options[:rule] || TwitterCldr::Formatters::Plurals::Rules.rule_for(options[:number], @locale)
available = traverse(path)
@@ -72,17 +73,13 @@ def tokens(options = {})
if available.include?(pluralization)
path << pluralization
else
+ return [] unless available.keys.first
path << available.keys.first
end
tokens_with_placeholders_for(path)
end
- def token_exists(path)
- cache_key = compute_cache_key(@locale, path.join('.'))
- token_cache.include?(cache_key) || !!traverse(path)
- end
-
def all_types_for(unit, direction)
traverse(@base_path + [@paths[direction][unit]]).keys
end
@@ -12,66 +12,64 @@
let(:date) { Date.today }
describe "#ago" do
- let(:date) { Date.new(2010,7,6) }
- let(:base_time) { Time.gm(2010,8,6,12,12,30) }
+ let(:date) { Date.new(2010, 7, 6) }
+ let(:base_time) { Time.gm(2010, 8, 6, 12, 12, 30) }
it "should ago-ify from now when no base_time given" do
- stub(Time).now { Time.gm(2010,8,6,12,12,30) }
+ stub(Time).now { Time.gm(2010, 8, 6, 12, 12, 30) }
loc_date = date.localize(:ko)
- loc_date.ago.to_s(:unit => :hour).should match_normalized("756시간")
+ loc_date.ago.to_s(:unit => :hour).should match_normalized("744시간")
end
it "should ago-ify with appropriate unit when no unit given" do
loc_date = date.localize(:en)
loc_date.ago(:base_time => base_time).to_s.should match_normalized("1 month ago")
- loc_date.ago(:base_time => Time.gm(2010,12,6,12,12,30)).to_s.should match_normalized("5 months ago")
- loc_date.ago(:base_time => Time.gm(2010,7,7,12,12,30)).to_s.should match_normalized("2 days ago")
- loc_date.ago(:base_time => Time.gm(2010,7,6,12,12,30)).to_s.should match_normalized("12 hours ago")
- loc_date.ago(:base_time => Time.gm(2010,7,6,0,39,0)).to_s.should match_normalized("39 minutes ago")
+ loc_date.ago(:base_time => Time.gm(2010, 12, 6, 12, 12, 30)).to_s.should match_normalized("5 months ago")
+ loc_date.ago(:base_time => Time.gm(2010, 7, 7, 12, 12, 30)).to_s.should match_normalized("1 day ago")
end
it "should ago-ify with strings regardless of variable's placement or existence" do
loc_date = date.localize(:ar)
- loc_date.ago(:base_time => base_time).to_s(:unit => :hour).should match_normalized("قبل 756 ساعة")
- loc_date.ago(:base_time => base_time).to_s(:unit => :day).should match_normalized("قبل 32 يومًا")
+ loc_date.ago(:base_time => base_time).to_s(:unit => :hour).should match_normalized("قبل 744 ساعة")
+ loc_date.ago(:base_time => base_time).to_s(:unit => :day).should match_normalized("قبل 31 يومًا")
loc_date.ago(:base_time => base_time).to_s(:unit => :month).should match_normalized("قبل 1 من الشهور")
loc_date.ago(:base_time => base_time).to_s(:unit => :year).should match_normalized("قبل 0 من السنوات")
loc_date = date.localize(:fa)
- loc_date.ago(:base_time => base_time).to_s(:unit => :day).should match_normalized("32 روز پیش")
+ loc_date.ago(:base_time => base_time).to_s(:unit => :day).should match_normalized("31 روز پیش")
loc_date = date.localize(:en)
- loc_date.ago(:base_time => base_time).to_s(:unit => :day).should match_normalized("32 days ago")
+ loc_date.ago(:base_time => base_time).to_s(:unit => :day).should match_normalized("31 days ago")
end
it "should ago-ify a date with a number of different units" do
- date = Date.new(2010,6,6)
+ date = Date.new(2010, 6, 6)
loc_date = date.localize(:de)
- loc_date.ago(:base_time => base_time).to_s(:unit => :second).should match_normalized("Vor 5314350 Sekunden")
- loc_date.ago(:base_time => base_time).to_s(:unit => :minute).should match_normalized("Vor 88573 Minuten")
- loc_date.ago(:base_time => base_time).to_s(:unit => :hour).should match_normalized("Vor 1476 Stunden")
- loc_date.ago(:base_time => base_time).to_s(:unit => :day).should match_normalized("Vor 62 Tagen")
+ loc_date.ago(:base_time => base_time).to_s(:unit => :second).should match_normalized("Vor 5270400 Sekunden")
+ loc_date.ago(:base_time => base_time).to_s(:unit => :minute).should match_normalized("Vor 87840 Minuten")
+ loc_date.ago(:base_time => base_time).to_s(:unit => :hour).should match_normalized("Vor 1464 Stunden")
+ loc_date.ago(:base_time => base_time).to_s(:unit => :day).should match_normalized("Vor 61 Tagen")
loc_date.ago(:base_time => base_time).to_s(:unit => :month).should match_normalized("Vor 2 Monaten")
loc_date.ago(:base_time => base_time).to_s(:unit => :year).should match_normalized("Vor 0 Jahren")
end
it "should return an error if called on a date in the future" do
- date = Date.new(2010,10,10)
+ date = Date.new(2010, 10, 10)
loc_date = date.localize(:de)
lambda { loc_date.ago(base_time, :second)}.should raise_error(ArgumentError)
end
end
describe "#until" do
- let(:base_time) { Time.gm(2010,8,6,12,12,30) }
+ let(:base_time) { Time.gm(2010, 8, 6, 12, 12, 30) }
it "should until-ify with a number of different units" do
- date = Date.new(2010,10,10)
+ date = Date.new(2010, 10, 10)
loc_date = date.localize(:de)
- loc_date.until(:base_time => base_time).to_s(:unit => :second).should match_normalized("In 5572050 Sekunden")
- loc_date.until(:base_time => base_time).to_s(:unit => :minute).should match_normalized("In 92868 Minuten")
- loc_date.until(:base_time => base_time).to_s(:unit => :hour).should match_normalized("In 1548 Stunden")
- loc_date.until(:base_time => base_time).to_s(:unit => :day).should match_normalized("In 64 Tagen")
+ loc_date.until(:base_time => base_time).to_s(:unit => :second).should match_normalized("In 5616000 Sekunden")
+ loc_date.until(:base_time => base_time).to_s(:unit => :minute).should match_normalized("In 93600 Minuten")
+ loc_date.until(:base_time => base_time).to_s(:unit => :hour).should match_normalized("In 1560 Stunden")
+ loc_date.until(:base_time => base_time).to_s(:unit => :day).should match_normalized("In 65 Tagen")
loc_date.until(:base_time => base_time).to_s(:unit => :month).should match_normalized("In 2 Monaten")
loc_date.until(:base_time => base_time).to_s(:unit => :year).should match_normalized("In 0 Jahren")
end

0 comments on commit cf38a51

Please sign in to comment.