Permalink
Browse files

MySQL: speedup date/time parsing.

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@7769 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
1 parent a76fa61 commit 8d7eb308981693817aa1101fc1ca3b4cd597afd0 @jeremy jeremy committed Oct 7, 2007
View
@@ -1,5 +1,7 @@
*SVN*
+* MySQL: speedup date/time parsing. [Jeremy Kemper]
+
* Fix calling .clear on a has_many :dependent=>:delete_all association. [tarmo]
* Allow change_column to set NOT NULL in the PostgreSQL adapter [tarmo]
@@ -92,76 +92,82 @@ def human_name
Base.human_attribute_name(@name)
end
- # Used to convert from Strings to BLOBs
- def self.string_to_binary(value)
- value
- end
+ class << self
+ # Used to convert from Strings to BLOBs
+ def string_to_binary(value)
+ value
+ end
- # Used to convert from BLOBs to Strings
- def self.binary_to_string(value)
- value
- end
+ # Used to convert from BLOBs to Strings
+ def binary_to_string(value)
+ value
+ end
- def self.string_to_date(string)
- return string unless string.is_a?(String)
- date_array = ParseDate.parsedate(string)
- # treat 0000-00-00 as nil
- Date.new(date_array[0], date_array[1], date_array[2]) rescue nil
- end
+ def string_to_date(string)
+ return string unless string.is_a?(String)
+ new_date *ParseDate.parsedate(string)[0..2]
+ end
- def self.string_to_time(string)
- return string unless string.is_a?(String)
- time_hash = Date._parse(string)
- time_hash[:sec_fraction] = microseconds(time_hash)
- time_array = time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction)
- # treat 0000-00-00 00:00:00 as nil
- begin
- Time.send(Base.default_timezone, *time_array)
- rescue
- zone_offset = if Base.default_timezone == :local then DateTime.now.offset else 0 end
- # Append zero calendar reform start to account for dates skipped by calendar reform
- DateTime.new(*time_array[0..5] << zone_offset << 0) rescue nil
+ def string_to_time(string)
+ return string unless string.is_a?(String)
+ return nil if string.empty?
+ time_hash = Date._parse(string)
+ time_hash[:sec_fraction] = microseconds(time_hash)
+ new_time *time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction)
end
- end
- def self.string_to_dummy_time(string)
- return string unless string.is_a?(String)
- return nil if string.empty?
- time_hash = Date._parse(string)
- time_hash[:sec_fraction] = microseconds(time_hash)
- # pad the resulting array with dummy date information
- time_array = [2000, 1, 1]
- time_array += time_hash.values_at(:hour, :min, :sec, :sec_fraction)
- Time.send(Base.default_timezone, *time_array) rescue nil
- end
+ def string_to_dummy_time(string)
+ return string unless string.is_a?(String)
+ return nil if string.empty?
- # convert something to a boolean
- def self.value_to_boolean(value)
- if value == true || value == false
- value
- else
- %w(true t 1).include?(value.to_s.downcase)
+ string_to_time "2000-01-01 #{string}"
end
- end
- # convert something to a BigDecimal
- def self.value_to_decimal(value)
- if value.is_a?(BigDecimal)
- value
- elsif value.respond_to?(:to_d)
- value.to_d
- else
- value.to_s.to_d
+ # convert something to a boolean
+ def value_to_boolean(value)
+ if value == true || value == false
+ value
+ else
+ %w(true t 1).include?(value.to_s.downcase)
+ end
end
- end
- private
- # '0.123456' -> 123456
- # '1.123456' -> 123456
- def self.microseconds(time)
- ((time[:sec_fraction].to_f % 1) * 1_000_000).to_i
+ # convert something to a BigDecimal
+ def value_to_decimal(value)
+ if value.is_a?(BigDecimal)
+ value
+ elsif value.respond_to?(:to_d)
+ value.to_d
+ else
+ value.to_s.to_d
+ end
end
+ protected
+ # '0.123456' -> 123456
+ # '1.123456' -> 123456
+ def microseconds(time)
+ ((time[:sec_fraction].to_f % 1) * 1_000_000).to_i
+ end
+
+ def new_date(year, mon, mday)
+ Date.new(year, mon, mday) unless year == 0
+ end
+
+ def new_time(year, mon, mday, hour, min, sec, microsec)
+ # Treat 0000-00-00 00:00:00 as nil.
+ return nil if year == 0
+
+ Time.send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec)
+ # Over/underflow to DateTime
+ rescue ArgumentError, TypeError
+ zone_offset = if Base.default_timezone == :local then DateTime.now.offset else 0 end
+ # Append zero calendar reform start to account for dates skipped by calendar reform
+ DateTime.new(year, mon, mday, hour, min, sec, zone_offset, 0)
+ end
+ end
+
+ private
def extract_limit(sql_type)
$1.to_i if sql_type =~ /\((.*)\)/
end
@@ -94,13 +94,43 @@ class MysqlColumn < Column #:nodoc:
TYPES_DISALLOWING_DEFAULT = Set.new([:binary, :text])
TYPES_ALLOWING_EMPTY_STRING_DEFAULT = Set.new([:string])
+ module Format
+ DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
+ DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(?:\.(\d{6}))?\z/
+ end
+
def initialize(name, default, sql_type = nil, null = true)
@original_default = default
super
@default = nil if no_default_allowed? || missing_default_forged_as_empty_string?
@default = '' if @original_default == '' && no_default_allowed?
end
+ class << self
+ def string_to_date(string)
+ return string unless string.is_a?(String)
+ return nil if string.empty?
+
+ if string =~ Format::DATE
+ new_date $1.to_i, $2.to_i, $3.to_i
+ else
+ new_date *ParseDate.parsedate(string)[0..2]
+ end
+ end
+
+ def string_to_time(string)
+ return string unless string.is_a?(String)
+ return nil if string.empty?
+
+ if string =~ Format::DATETIME
+ new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, $7.to_i
+ else
+ time_hash = Date._parse(string)
+ new_time *(time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec) << microseconds(time_hash))
+ end
+ end
+ end
+
private
def simplified_type(field_type)
return :boolean if MysqlAdapter.emulate_booleans && field_type.downcase.index("tinyint(1)")

0 comments on commit 8d7eb30

Please sign in to comment.