diff --git a/lib/xlsxtream/core_extension.rb b/lib/xlsxtream/core_extension.rb new file mode 100644 index 0000000..d7e839e --- /dev/null +++ b/lib/xlsxtream/core_extension.rb @@ -0,0 +1,79 @@ +class Object + def to_xslx_value(cid, _, sst) + to_s.to_xslx_value(cid, false, sst) + end +end + +class Numeric + def to_xslx_value(cid, _, _) + %Q{#{self}} + end +end + +class TrueClass + def to_xslx_value(cid, _, _) + %Q{1} + end +end + +class FalseClass + def to_xslx_value(cid, _, _) + %Q{0} + end +end + +class Time + def to_xslx_value(cid, _, _) + # Local dates are stored as UTC by truncating the offset: + # 1970-01-01 00:00:00 +0200 => 1970-01-01 00:00:00 UTC + # This is done because SpreadsheetML is not timezone aware. + oa_date = (to_f + utc_offset) / 86400 + 25569 + + %Q{#{oa_date}} + end +end + +class DateTime + def to_xslx_value(cid, _, _) + _, jd, df, sf, of = marshal_dump + oa_date = jd - 2415019 + (df + of + sf / 1e9) / 86400 + + %Q{#{oa_date}} + end +end + +class Date + if RUBY_ENGINE == 'ruby' + def to_xslx_value(cid, _, _) + oa_date = (jd - 2415019).to_f + %Q{#{oa_date}} + end + else + def to_xslx_value(cid, _, _) + oa_date = jd - 2415019 + (hour * 3600 + sec + sec_fraction.to_f) / 86400 + %Q{#{oa_date}} + end + end +end + +class String + def to_xslx_value(cid, auto_format, sst) + if empty? + "" + elsif auto_format + Xlsxtream::Row.auto_format(self).to_xslx_value(cid, false, sst) + else + if encoding != Xlsxtream::Row::ENCODING + value = encode(Xlsxtream::Row::ENCODING) + else + value = self + end + + if sst + %Q{#{sst[value]}} + else + %Q{#{Xlsxtream::XML.escape_value(value)}} + end + end + end +end diff --git a/lib/xlsxtream/row.rb b/lib/xlsxtream/row.rb index afba661..a7720a9 100644 --- a/lib/xlsxtream/row.rb +++ b/lib/xlsxtream/row.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true require "date" +require "xlsxtream/core_extension" require "xlsxtream/xml" module Xlsxtream @@ -31,85 +32,33 @@ def to_xml xml = String.new(%Q{}) @row.each do |value| - cid = "#{column}#{@rownum}" - column.next! - - if @auto_format && value.is_a?(String) - value = auto_format(value) - end - - case value - when Numeric - xml << %Q{#{value}} - when TrueClass, FalseClass - xml << %Q{#{value ? 1 : 0}} - when Time - xml << %Q{#{time_to_oa_date(value)}} - when DateTime - xml << %Q{#{datetime_to_oa_date(value)}} - when Date - xml << %Q{#{date_to_oa_date(value)}} - else - value = value.to_s - - unless value.empty? # no xml output for for empty strings - value = value.encode(ENCODING) if value.encoding != ENCODING - - if @sst - xml << %Q{#{@sst[value]}} - else - xml << %Q{#{XML.escape_value(value)}} - end - end + unless value.nil? + xml << value.to_xslx_value("#{column}#{@rownum}", @auto_format, @sst) end + column.next! end xml << '' end - private - # Detects and casts numbers, date, time in text - def auto_format(value) - case value - when TRUE_STRING - true - when FALSE_STRING - false - when NUMBER_PATTERN - value.include?('.') ? value.to_f : value.to_i - when DATE_PATTERN - Date.parse(value) rescue value - when TIME_PATTERN - DateTime.parse(value) rescue value - else - value - end - end - - # Converts Time instance to OLE Automation Date - def time_to_oa_date(time) - # Local dates are stored as UTC by truncating the offset: - # 1970-01-01 00:00:00 +0200 => 1970-01-01 00:00:00 UTC - # This is done because SpreadsheetML is not timezone aware. - (time.to_f + time.utc_offset) / 86400 + 25569 - end - - # Converts DateTime instance to OLE Automation Date - if RUBY_ENGINE == 'ruby' - def datetime_to_oa_date(date) - _, jd, df, sf, of = date.marshal_dump - jd - 2415019 + (df + of + sf / 1e9) / 86400 - end - else - def datetime_to_oa_date(date) - date.jd - 2415019 + (date.hour * 3600 + date.sec + date.sec_fraction.to_f) / 86400 + class << self + def auto_format(value) + case value + when TRUE_STRING + true + when FALSE_STRING + false + when NUMBER_PATTERN + value.include?('.') ? value.to_f : value.to_i + when DATE_PATTERN + Date.parse(value) rescue value + when TIME_PATTERN + DateTime.parse(value) rescue value + else + value + end end end - - # Converts Date instance to OLE Automation Date - def date_to_oa_date(date) - (date.jd - 2415019).to_f - end end end