roo 2.3.0 → 2.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.codeclimate.yml +17 -0
- data/.github/issue_template.md +16 -0
- data/.github/pull_request_template.md +14 -0
- data/.github/workflows/pull-request.yml +15 -0
- data/.github/workflows/ruby.yml +34 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +186 -0
- data/CHANGELOG.md +148 -0
- data/Gemfile +4 -4
- data/LICENSE +2 -0
- data/README.md +84 -27
- data/Rakefile +1 -1
- data/lib/roo/base.rb +111 -237
- data/lib/roo/constants.rb +5 -3
- data/lib/roo/csv.rb +106 -85
- data/lib/roo/errors.rb +2 -0
- data/lib/roo/excelx/cell/base.rb +26 -12
- data/lib/roo/excelx/cell/boolean.rb +9 -6
- data/lib/roo/excelx/cell/date.rb +7 -7
- data/lib/roo/excelx/cell/datetime.rb +50 -44
- data/lib/roo/excelx/cell/empty.rb +3 -2
- data/lib/roo/excelx/cell/number.rb +60 -47
- data/lib/roo/excelx/cell/string.rb +3 -3
- data/lib/roo/excelx/cell/time.rb +17 -16
- data/lib/roo/excelx/cell.rb +11 -7
- data/lib/roo/excelx/comments.rb +3 -3
- data/lib/roo/excelx/coordinate.rb +11 -4
- data/lib/roo/excelx/extractor.rb +20 -3
- data/lib/roo/excelx/format.rb +38 -31
- data/lib/roo/excelx/images.rb +26 -0
- data/lib/roo/excelx/relationships.rb +12 -4
- data/lib/roo/excelx/shared.rb +10 -3
- data/lib/roo/excelx/shared_strings.rb +113 -9
- data/lib/roo/excelx/sheet.rb +49 -10
- data/lib/roo/excelx/sheet_doc.rb +101 -48
- data/lib/roo/excelx/styles.rb +4 -4
- data/lib/roo/excelx/workbook.rb +8 -3
- data/lib/roo/excelx.rb +85 -42
- data/lib/roo/formatters/base.rb +15 -0
- data/lib/roo/formatters/csv.rb +84 -0
- data/lib/roo/formatters/matrix.rb +23 -0
- data/lib/roo/formatters/xml.rb +31 -0
- data/lib/roo/formatters/yaml.rb +40 -0
- data/lib/roo/helpers/default_attr_reader.rb +20 -0
- data/lib/roo/helpers/weak_instance_cache.rb +41 -0
- data/lib/roo/open_office.rb +41 -27
- data/lib/roo/spreadsheet.rb +8 -2
- data/lib/roo/tempdir.rb +24 -0
- data/lib/roo/utils.rb +76 -26
- data/lib/roo/version.rb +1 -1
- data/lib/roo.rb +5 -0
- data/roo.gemspec +22 -12
- data/spec/lib/roo/base_spec.rb +65 -3
- data/spec/lib/roo/csv_spec.rb +19 -0
- data/spec/lib/roo/excelx/cell/time_spec.rb +15 -0
- data/spec/lib/roo/excelx/relationships_spec.rb +43 -0
- data/spec/lib/roo/excelx/sheet_doc_spec.rb +11 -0
- data/spec/lib/roo/excelx_spec.rb +237 -5
- data/spec/lib/roo/openoffice_spec.rb +2 -2
- data/spec/lib/roo/spreadsheet_spec.rb +1 -1
- data/spec/lib/roo/strict_spec.rb +43 -0
- data/spec/lib/roo/utils_spec.rb +22 -9
- data/spec/lib/roo/weak_instance_cache_spec.rb +92 -0
- data/spec/lib/roo_spec.rb +0 -0
- data/spec/spec_helper.rb +2 -7
- data/test/excelx/cell/test_attr_reader_default.rb +72 -0
- data/test/excelx/cell/test_base.rb +6 -2
- data/test/excelx/cell/test_boolean.rb +1 -3
- data/test/excelx/cell/test_date.rb +1 -6
- data/test/excelx/cell/test_datetime.rb +7 -10
- data/test/excelx/cell/test_empty.rb +12 -2
- data/test/excelx/cell/test_number.rb +28 -4
- data/test/excelx/cell/test_string.rb +21 -3
- data/test/excelx/cell/test_time.rb +7 -10
- data/test/excelx/test_coordinate.rb +51 -0
- data/test/formatters/test_csv.rb +136 -0
- data/test/formatters/test_matrix.rb +76 -0
- data/test/formatters/test_xml.rb +78 -0
- data/test/formatters/test_yaml.rb +20 -0
- data/test/helpers/test_accessing_files.rb +81 -0
- data/test/helpers/test_comments.rb +43 -0
- data/test/helpers/test_formulas.rb +9 -0
- data/test/helpers/test_labels.rb +103 -0
- data/test/helpers/test_sheets.rb +55 -0
- data/test/helpers/test_styles.rb +62 -0
- data/test/roo/test_base.rb +182 -0
- data/test/roo/test_csv.rb +88 -0
- data/test/roo/test_excelx.rb +360 -0
- data/test/roo/test_libre_office.rb +9 -0
- data/test/roo/test_open_office.rb +289 -0
- data/test/test_helper.rb +129 -14
- data/test/test_roo.rb +60 -1765
- metadata +91 -21
- data/.travis.yml +0 -14
data/lib/roo/csv.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "csv"
|
4
|
+
require "time"
|
3
5
|
|
4
6
|
# The CSV class can read csv files (must be separated with commas) which then
|
5
7
|
# can be handled like spreadsheets. This means you can access cells like A5
|
@@ -9,112 +11,131 @@ require 'time'
|
|
9
11
|
#
|
10
12
|
# You can pass options to the underlying CSV parse operation, via the
|
11
13
|
# :csv_options option.
|
12
|
-
|
14
|
+
module Roo
|
15
|
+
class CSV < Roo::Base
|
16
|
+
attr_reader :filename
|
17
|
+
|
18
|
+
# Returns an array with the names of the sheets. In CSV class there is only
|
19
|
+
# one dummy sheet, because a csv file cannot have more than one sheet.
|
20
|
+
def sheets
|
21
|
+
["default"]
|
22
|
+
end
|
13
23
|
|
14
|
-
|
24
|
+
def cell(row, col, sheet = nil)
|
25
|
+
sheet ||= default_sheet
|
26
|
+
read_cells(sheet)
|
27
|
+
@cell[normalize(row, col)]
|
28
|
+
end
|
15
29
|
|
16
|
-
|
30
|
+
def celltype(row, col, sheet = nil)
|
31
|
+
sheet ||= default_sheet
|
32
|
+
read_cells(sheet)
|
33
|
+
@cell_type[normalize(row, col)]
|
34
|
+
end
|
17
35
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
['default']
|
22
|
-
end
|
36
|
+
def cell_postprocessing(_row, _col, value)
|
37
|
+
value
|
38
|
+
end
|
23
39
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
@cell[normalize(row,col)]
|
28
|
-
end
|
40
|
+
def csv_options
|
41
|
+
@options[:csv_options] || {}
|
42
|
+
end
|
29
43
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@cell_type[normalize(row,col)]
|
34
|
-
end
|
44
|
+
def set_value(row, col, value, _sheet)
|
45
|
+
@cell[[row, col]] = value
|
46
|
+
end
|
35
47
|
|
36
|
-
|
37
|
-
|
38
|
-
|
48
|
+
def set_type(row, col, type, _sheet)
|
49
|
+
@cell_type[[row, col]] = type
|
50
|
+
end
|
39
51
|
|
40
|
-
|
41
|
-
@options[:csv_options] || {}
|
42
|
-
end
|
52
|
+
private
|
43
53
|
|
44
|
-
|
54
|
+
TYPE_MAP = {
|
55
|
+
String => :string,
|
56
|
+
Float => :float,
|
57
|
+
Date => :date,
|
58
|
+
DateTime => :datetime,
|
59
|
+
}
|
45
60
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
Date => :date,
|
50
|
-
DateTime => :datetime,
|
51
|
-
}
|
61
|
+
def celltype_class(value)
|
62
|
+
TYPE_MAP[value.class]
|
63
|
+
end
|
52
64
|
|
53
|
-
|
54
|
-
|
55
|
-
|
65
|
+
def read_cells(sheet = default_sheet)
|
66
|
+
sheet ||= default_sheet
|
67
|
+
return if @cells_read[sheet]
|
68
|
+
row_num = 0
|
69
|
+
max_col_num = 0
|
70
|
+
|
71
|
+
each_row csv_options do |row|
|
72
|
+
row_num += 1
|
73
|
+
col_num = 0
|
74
|
+
|
75
|
+
row.each do |elem|
|
76
|
+
col_num += 1
|
77
|
+
coordinate = [row_num, col_num]
|
78
|
+
@cell[coordinate] = elem
|
79
|
+
@cell_type[coordinate] = celltype_class(elem)
|
80
|
+
end
|
56
81
|
|
57
|
-
|
58
|
-
if uri?(filename)
|
59
|
-
make_tmpdir do |tmpdir|
|
60
|
-
tmp_filename = download_uri(filename, tmpdir)
|
61
|
-
CSV.foreach(tmp_filename, options, &block)
|
82
|
+
max_col_num = col_num if col_num > max_col_num
|
62
83
|
end
|
63
|
-
|
64
|
-
|
84
|
+
|
85
|
+
set_row_count(sheet, row_num)
|
86
|
+
set_column_count(sheet, max_col_num)
|
87
|
+
@cells_read[sheet] = true
|
65
88
|
end
|
66
|
-
end
|
67
89
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
@first_column[sheet] = 1
|
74
|
-
@last_column[sheet] = 1
|
75
|
-
rownum = 1
|
76
|
-
each_row csv_options do |row|
|
77
|
-
row.each_with_index do |elem,i|
|
78
|
-
@cell[[rownum,i+1]] = cell_postprocessing rownum,i+1, elem
|
79
|
-
@cell_type[[rownum,i+1]] = celltype_class @cell[[rownum,i+1]]
|
80
|
-
if i+1 > @last_column[sheet]
|
81
|
-
@last_column[sheet] += 1
|
82
|
-
end
|
90
|
+
def each_row(options, &block)
|
91
|
+
if uri?(filename)
|
92
|
+
each_row_using_tempdir(options, &block)
|
93
|
+
else
|
94
|
+
csv_foreach(filename_or_stream, options, &block)
|
83
95
|
end
|
84
|
-
rownum += 1
|
85
|
-
@last_row[sheet] += 1
|
86
96
|
end
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
97
|
+
|
98
|
+
def each_row_using_tempdir(options, &block)
|
99
|
+
::Dir.mktmpdir(Roo::TEMP_PREFIX, ENV["ROO_TMP"]) do |tmpdir|
|
100
|
+
tmp_filename = download_uri(filename, tmpdir)
|
101
|
+
csv_foreach(tmp_filename, options, &block)
|
102
|
+
end
|
91
103
|
end
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
104
|
+
|
105
|
+
def csv_foreach(path_or_io, options, &block)
|
106
|
+
if is_stream?(path_or_io)
|
107
|
+
::CSV.new(path_or_io, **options).each(&block)
|
108
|
+
else
|
109
|
+
::CSV.foreach(path_or_io, **options, &block)
|
110
|
+
end
|
96
111
|
end
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
@
|
112
|
+
|
113
|
+
def set_row_count(sheet, last_row)
|
114
|
+
@first_row[sheet] = 1
|
115
|
+
@last_row[sheet] = last_row
|
116
|
+
@last_row[sheet] = @first_row[sheet] if @last_row[sheet].zero?
|
117
|
+
|
118
|
+
nil
|
102
119
|
end
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
@last_column[sheet]
|
120
|
+
|
121
|
+
def set_column_count(sheet, last_col)
|
122
|
+
@first_column[sheet] = 1
|
123
|
+
@last_column[sheet] = last_col
|
124
|
+
@last_column[sheet] = @first_column[sheet] if @last_column[sheet].zero?
|
125
|
+
|
126
|
+
nil
|
108
127
|
end
|
109
|
-
end
|
110
128
|
|
111
|
-
|
112
|
-
|
129
|
+
def clean_sheet(sheet)
|
130
|
+
read_cells(sheet)
|
131
|
+
|
132
|
+
@cell.each_pair do |coord, value|
|
133
|
+
@cell[coord] = sanitize_value(value) if value.is_a?(::String)
|
134
|
+
end
|
113
135
|
|
114
|
-
|
115
|
-
@cell[coord] = sanitize_value(value) if value.is_a?(::String)
|
136
|
+
@cleaned[sheet] = true
|
116
137
|
end
|
117
138
|
|
118
|
-
|
139
|
+
alias_method :filename_or_stream, :filename
|
119
140
|
end
|
120
141
|
end
|
data/lib/roo/errors.rb
CHANGED
data/lib/roo/excelx/cell/base.rb
CHANGED
@@ -1,13 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "roo/helpers/default_attr_reader"
|
4
|
+
|
1
5
|
module Roo
|
2
6
|
class Excelx
|
3
7
|
class Cell
|
4
8
|
class Base
|
9
|
+
extend Roo::Helpers::DefaultAttrReader
|
5
10
|
attr_reader :cell_type, :cell_value, :value
|
6
11
|
|
7
12
|
# FIXME: I think style should be deprecated. Having a style attribute
|
8
13
|
# for a cell doesn't really accomplish much. It seems to be used
|
9
14
|
# when you want to export to excelx.
|
10
|
-
|
15
|
+
attr_reader_with_default default_type: :base, style: 1
|
11
16
|
|
12
17
|
|
13
18
|
# FIXME: Updating a cell's value should be able tochange the cell's type,
|
@@ -34,14 +39,12 @@ module Roo
|
|
34
39
|
attr_writer :value
|
35
40
|
|
36
41
|
def initialize(value, formula, excelx_type, style, link, coordinate)
|
37
|
-
@link = !!link
|
38
42
|
@cell_value = value
|
39
|
-
@cell_type = excelx_type
|
40
|
-
@formula = formula
|
41
|
-
@style = style
|
43
|
+
@cell_type = excelx_type if excelx_type
|
44
|
+
@formula = formula if formula
|
45
|
+
@style = style unless style == 1
|
42
46
|
@coordinate = coordinate
|
43
|
-
@
|
44
|
-
@value = link? ? Roo::Link.new(link, value) : value
|
47
|
+
@value = link ? Roo::Link.new(link, value) : value
|
45
48
|
end
|
46
49
|
|
47
50
|
def type
|
@@ -50,16 +53,16 @@ module Roo
|
|
50
53
|
elsif link?
|
51
54
|
:link
|
52
55
|
else
|
53
|
-
|
56
|
+
default_type
|
54
57
|
end
|
55
58
|
end
|
56
59
|
|
57
60
|
def formula?
|
58
|
-
|
61
|
+
!!(defined?(@formula) && @formula)
|
59
62
|
end
|
60
63
|
|
61
64
|
def link?
|
62
|
-
|
65
|
+
Roo::Link === @value
|
63
66
|
end
|
64
67
|
|
65
68
|
alias_method :formatted_value, :value
|
@@ -68,9 +71,16 @@ module Roo
|
|
68
71
|
formatted_value
|
69
72
|
end
|
70
73
|
|
71
|
-
# DEPRECATED: Please use link instead.
|
74
|
+
# DEPRECATED: Please use link? instead.
|
72
75
|
def hyperlink
|
73
|
-
warn '[DEPRECATION] `hyperlink` is deprecated. Please use `link
|
76
|
+
warn '[DEPRECATION] `hyperlink` is deprecated. Please use `link?` instead.'
|
77
|
+
link?
|
78
|
+
end
|
79
|
+
|
80
|
+
# DEPRECATED: Please use link? instead.
|
81
|
+
def link
|
82
|
+
warn '[DEPRECATION] `link` is deprecated. Please use `link?` instead.'
|
83
|
+
link?
|
74
84
|
end
|
75
85
|
|
76
86
|
# DEPRECATED: Please use cell_value instead.
|
@@ -88,6 +98,10 @@ module Roo
|
|
88
98
|
def empty?
|
89
99
|
false
|
90
100
|
end
|
101
|
+
|
102
|
+
def presence
|
103
|
+
empty? ? nil : self
|
104
|
+
end
|
91
105
|
end
|
92
106
|
end
|
93
107
|
end
|
@@ -1,17 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Roo
|
2
4
|
class Excelx
|
3
5
|
class Cell
|
4
6
|
class Boolean < Cell::Base
|
5
|
-
attr_reader :value, :formula, :format, :
|
7
|
+
attr_reader :value, :formula, :format, :cell_value, :coordinate
|
8
|
+
|
9
|
+
attr_reader_with_default default_type: :boolean, cell_type: :boolean
|
6
10
|
|
7
11
|
def initialize(value, formula, style, link, coordinate)
|
8
|
-
super(value, formula, nil, style,
|
9
|
-
@
|
10
|
-
@value = link? ? Roo::Link.new(link, value) : create_boolean(value)
|
12
|
+
super(value, formula, nil, style, nil, coordinate)
|
13
|
+
@value = link ? Roo::Link.new(link, value) : create_boolean(value)
|
11
14
|
end
|
12
15
|
|
13
16
|
def formatted_value
|
14
|
-
value ? 'TRUE'
|
17
|
+
value ? 'TRUE' : 'FALSE'
|
15
18
|
end
|
16
19
|
|
17
20
|
private
|
@@ -19,7 +22,7 @@ module Roo
|
|
19
22
|
def create_boolean(value)
|
20
23
|
# FIXME: Using a boolean will cause methods like Base#to_csv to fail.
|
21
24
|
# Roo is using some method to ignore false/nil values.
|
22
|
-
value.to_i == 1
|
25
|
+
value.to_i == 1
|
23
26
|
end
|
24
27
|
end
|
25
28
|
end
|
data/lib/roo/excelx/cell/date.rb
CHANGED
@@ -4,23 +4,23 @@ module Roo
|
|
4
4
|
class Excelx
|
5
5
|
class Cell
|
6
6
|
class Date < Roo::Excelx::Cell::DateTime
|
7
|
-
attr_reader :value, :formula, :format, :cell_type, :cell_value, :
|
7
|
+
attr_reader :value, :formula, :format, :cell_type, :cell_value, :coordinate
|
8
|
+
|
9
|
+
attr_reader_with_default default_type: :date
|
8
10
|
|
9
11
|
def initialize(value, formula, excelx_type, style, link, base_date, coordinate)
|
10
12
|
# NOTE: Pass all arguments to the parent class, DateTime.
|
11
13
|
super
|
12
|
-
@type = :date
|
13
14
|
@format = excelx_type.last
|
14
|
-
@value = link
|
15
|
+
@value = link ? Roo::Link.new(link, value) : create_date(base_date, value)
|
15
16
|
end
|
16
17
|
|
17
18
|
private
|
18
19
|
|
19
|
-
def
|
20
|
-
date = base_date + value.to_i
|
21
|
-
yyyy, mm, dd = date.strftime('%Y-%m-%d').split('-')
|
20
|
+
def create_datetime(_,_); end
|
22
21
|
|
23
|
-
|
22
|
+
def create_date(base_date, value)
|
23
|
+
base_date + value.to_i
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
@@ -1,16 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'date'
|
2
4
|
|
3
5
|
module Roo
|
4
6
|
class Excelx
|
5
7
|
class Cell
|
6
8
|
class DateTime < Cell::Base
|
7
|
-
|
9
|
+
SECONDS_IN_DAY = 60 * 60 * 24
|
10
|
+
|
11
|
+
attr_reader :value, :formula, :format, :cell_value, :coordinate
|
8
12
|
|
9
|
-
|
10
|
-
|
11
|
-
|
13
|
+
attr_reader_with_default default_type: :datetime
|
14
|
+
|
15
|
+
def initialize(value, formula, excelx_type, style, link, base_timestamp, coordinate)
|
16
|
+
super(value, formula, excelx_type, style, nil, coordinate)
|
12
17
|
@format = excelx_type.last
|
13
|
-
@value = link
|
18
|
+
@value = link ? Roo::Link.new(link, value) : create_datetime(base_timestamp, value)
|
14
19
|
end
|
15
20
|
|
16
21
|
# Public: Returns formatted value for a datetime. Format's can be an
|
@@ -32,14 +37,9 @@ module Roo
|
|
32
37
|
#
|
33
38
|
# Returns a String representation of a cell's value.
|
34
39
|
def formatted_value
|
35
|
-
date_regex = /(?<date>[dmy]+[\-\/][dmy]+([\-\/][dmy]+)?)/
|
36
|
-
time_regex = /(?<time>(\[?[h]\]?+:)?[m]+(:?ss|:?s)?)/
|
37
|
-
|
38
40
|
formatter = @format.downcase.split(' ').map do |part|
|
39
|
-
if
|
40
|
-
|
41
|
-
elsif part[time_regex]
|
42
|
-
part.gsub(/#{TIME_FORMATS.keys.join('|')}/, TIME_FORMATS)
|
41
|
+
if (parsed_format = parse_date_or_time_format(part))
|
42
|
+
parsed_format
|
43
43
|
else
|
44
44
|
warn 'Unable to parse custom format. Using "YYYY-mm-dd HH:MM:SS" format.'
|
45
45
|
return @value.strftime('%F %T')
|
@@ -51,49 +51,55 @@ module Roo
|
|
51
51
|
|
52
52
|
private
|
53
53
|
|
54
|
+
def parse_date_or_time_format(part)
|
55
|
+
date_regex = /(?<date>[dmy]+[\-\/][dmy]+([\-\/][dmy]+)?)/
|
56
|
+
time_regex = /(?<time>(\[?[h]\]?+:)?[m]+(:?ss|:?s)?)/
|
57
|
+
|
58
|
+
if part[date_regex] == part
|
59
|
+
formats = DATE_FORMATS
|
60
|
+
elsif part[time_regex]
|
61
|
+
formats = TIME_FORMATS
|
62
|
+
else
|
63
|
+
return false
|
64
|
+
end
|
65
|
+
|
66
|
+
part.gsub(/#{formats.keys.join('|')}/, formats)
|
67
|
+
end
|
68
|
+
|
54
69
|
DATE_FORMATS = {
|
55
|
-
'yyyy'
|
56
|
-
'yy'
|
70
|
+
'yyyy' => '%Y', # Year: 2000
|
71
|
+
'yy' => '%y', # Year: 00
|
57
72
|
# mmmmm => J-D
|
58
|
-
'mmmm'
|
59
|
-
'mmm'
|
60
|
-
'mm'
|
61
|
-
'm'
|
62
|
-
'dddd'
|
63
|
-
'ddd'
|
64
|
-
'dd'
|
65
|
-
'd'
|
73
|
+
'mmmm' => '%B', # Month: January
|
74
|
+
'mmm' => '%^b', # Month: JAN
|
75
|
+
'mm' => '%m', # Month: 01
|
76
|
+
'm' => '%-m', # Month: 1
|
77
|
+
'dddd' => '%A', # Day of the Week: Sunday
|
78
|
+
'ddd' => '%^a', # Day of the Week: SUN
|
79
|
+
'dd' => '%d', # Day of the Month: 01
|
80
|
+
'd' => '%-d' # Day of the Month: 1
|
66
81
|
# '\\\\'.freeze => ''.freeze, # NOTE: Fixes a custom format's output.
|
67
82
|
}
|
68
83
|
|
69
84
|
TIME_FORMATS = {
|
70
|
-
'hh'
|
71
|
-
'h'
|
85
|
+
'hh' => '%H', # Hour (24): 01
|
86
|
+
'h' => '%-k', # Hour (24): 1
|
72
87
|
# 'hh'.freeze => '%I'.freeze, # Hour (12): 08
|
73
88
|
# 'h'.freeze => '%-l'.freeze, # Hour (12): 8
|
74
|
-
'mm'
|
89
|
+
'mm' => '%M', # Minute: 01
|
75
90
|
# FIXME: is this used? Seems like 'm' is used for month, not minute.
|
76
|
-
'm'
|
77
|
-
'ss'
|
78
|
-
's'
|
79
|
-
'am/pm'
|
80
|
-
'000'
|
81
|
-
'00'
|
82
|
-
'0'
|
91
|
+
'm' => '%-M', # Minute: 1
|
92
|
+
'ss' => '%S', # Seconds: 01
|
93
|
+
's' => '%-S', # Seconds: 1
|
94
|
+
'am/pm' => '%p', # Meridian: AM
|
95
|
+
'000' => '%3N', # Fractional Seconds: thousandth.
|
96
|
+
'00' => '%2N', # Fractional Seconds: hundredth.
|
97
|
+
'0' => '%1N' # Fractional Seconds: tenths.
|
83
98
|
}
|
84
99
|
|
85
|
-
def create_datetime(
|
86
|
-
|
87
|
-
|
88
|
-
t = round_datetime(datetime_string)
|
89
|
-
|
90
|
-
::DateTime.civil(t.year, t.month, t.day, t.hour, t.min, t.sec)
|
91
|
-
end
|
92
|
-
|
93
|
-
def round_datetime(datetime_string)
|
94
|
-
/(?<yyyy>\d+)-(?<mm>\d+)-(?<dd>\d+) (?<hh>\d+):(?<mi>\d+):(?<ss>\d+.\d+)/ =~ datetime_string
|
95
|
-
|
96
|
-
::Time.new(yyyy.to_i, mm.to_i, dd.to_i, hh.to_i, mi.to_i, ss.to_r).round(0)
|
100
|
+
def create_datetime(base_timestamp, value)
|
101
|
+
timestamp = (base_timestamp + (value.to_f.round(6) * SECONDS_IN_DAY)).round(0)
|
102
|
+
::Time.at(timestamp).utc.to_datetime
|
97
103
|
end
|
98
104
|
end
|
99
105
|
end
|
@@ -3,10 +3,11 @@ module Roo
|
|
3
3
|
class Excelx
|
4
4
|
class Cell
|
5
5
|
class Empty < Cell::Base
|
6
|
-
attr_reader :value, :formula, :format, :cell_type, :cell_value, :
|
6
|
+
attr_reader :value, :formula, :format, :cell_type, :cell_value, :coordinate
|
7
|
+
|
8
|
+
attr_reader_with_default default_type: nil, style: nil
|
7
9
|
|
8
10
|
def initialize(coordinate)
|
9
|
-
@value = @formula = @format = @cell_type = @cell_value = @hyperlink = nil
|
10
11
|
@coordinate = coordinate
|
11
12
|
end
|
12
13
|
|