module DBF module ColumnType class Base attr_reader :decimal, :encoding # @param decimal [Integer] # @param encoding [String, Encoding] def initialize(decimal, encoding) @decimal = decimal @encoding = encoding end end class Nil < Base # @param _value [String] def type_cast(_value) nil end end class Number < Base # @param value [String] def type_cast(value) return nil if value.strip.empty? @decimal.zero? ? value.to_i : value.to_f end end class Currency < Base # @param value [String] def type_cast(value) (value.unpack1('q<') / 10_000.0).to_f end end class SignedLong < Base # @param value [String] def type_cast(value) value.unpack1('l<') end end class SignedLong2 < Base # @param value [String] def type_cast(value) s = value.unpack1('B*') sign_multiplier = s[0] == '0' ? -1 : 1 s[1, 31].to_i(2) * sign_multiplier end end class Float < Base # @param value [String] def type_cast(value) value.to_f end end class Double < Base # @param value [String] def type_cast(value) value.unpack1('E') end end class Boolean < Base # @param value [String] def type_cast(value) value.strip.match?(/^(y|t)$/i) end end class Date < Base # @param value [String] def type_cast(value) value.match?(/\d{8}/) && ::Date.strptime(value, '%Y%m%d') rescue StandardError nil end end class DateTime < Base # @param value [String] def type_cast(value) days, msecs = value.unpack('l2') secs = (msecs / 1000).to_i ::DateTime.jd(days, (secs / 3600).to_i, (secs / 60).to_i % 60, secs % 60).to_time rescue StandardError nil end end class Memo < Base # @param value [String] def type_cast(value) if encoding && !value.nil? value.force_encoding(@encoding).encode(Encoding.default_external, undef: :replace, invalid: :replace) else value end end end class General < Base # @param value [String] def type_cast(value) value end end class String < Base # @param value [String] def type_cast(value) value = value.strip @encoding ? value.force_encoding(@encoding).encode(Encoding.default_external, undef: :replace, invalid: :replace) : value end end end end