diff options
author | Unit 193 <unit193@ubuntu.com> | 2019-01-14 01:40:56 -0500 |
---|---|---|
committer | Unit 193 <unit193@ubuntu.com> | 2019-01-14 01:40:56 -0500 |
commit | dddfa903d2b856146f05ffb4415c31d6127bb5bf (patch) | |
tree | e38c2aca92b54f06ccd0185f48dc47e3e1b3d77e | |
parent | 8280a21a23d44aa90177e2bc041d0b8dc8556f4b (diff) | |
download | ruby-roo-dddfa903d2b856146f05ffb4415c31d6127bb5bf.tar.bz2 ruby-roo-dddfa903d2b856146f05ffb4415c31d6127bb5bf.tar.xz ruby-roo-dddfa903d2b856146f05ffb4415c31d6127bb5bf.tar.zst |
New upstream version 2.8.0upstream/2.8.0
70 files changed, 2373 insertions, 463 deletions
diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE deleted file mode 100644 index 186ffc1..0000000 --- a/.github/ISSUE_TEMPLATE +++ /dev/null @@ -1,10 +0,0 @@ -Thanks for filing an issue. Following these instructions will help us solve your problem sooner. - -1. Describe the issue. -2. Create a gist for this issue (Sample gist: https://gist.github.com/stevendaniels/98a05849036e99bb8b3c)? - -Here are some instructions for creating such a gist. - -1. Create a gist (https://gist.github.com) with code that creates the error. -2. Clone the gist repo locally, add a stripped down version of the offending spreadsheet to the gist repo, and push the gist's changes master. -3. Paste the gist url here. diff --git a/.github/issue_template.md b/.github/issue_template.md new file mode 100644 index 0000000..b2bfc6d --- /dev/null +++ b/.github/issue_template.md @@ -0,0 +1,16 @@ +Thanks for filing an issue. Following these instructions will help us solve your problem sooner. + +### Steps to reproduce + +1. Create an executable test case for this issue ([sample test case](https://gist.github.com/tgturner/e4b7f491639b8a6dd883fe2ace408652)) +2. You can share your executable test case as a [gist](https://gist.github.com), or simply paste the content into the issue description. + - You can execute the test case by running `ruby the_file.rb` in your terminal. If all goes well, you should see your test case failing. +3. Please provide a stripped down version of the offending spreadsheet. + +### Issue +Describe the issue + +### System configuration +**Roo version**: + +**Ruby version**:
\ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..a0d7f6e --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,14 @@ +### Summary + +Provide a general description of the code changes in your pull +request... were there any bugs you had fixed? If so, mention them. If +these bugs have open GitHub issues, be sure to tag them here as well, +to keep the conversation linked together. + +### Other Information + +If there's anything else that's important and relevant to your pull +request, mention that information here. This could include +benchmarks, or other information. + +Thanks for contributing to Roo!
\ No newline at end of file diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..e6ce1a7 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,186 @@ +AllCops: + TargetRubyVersion: 2.4 + # RuboCop has a bunch of cops enabled by default. This setting tells RuboCop + # to ignore them, so only the ones explicitly set in this file are enabled. + DisabledByDefault: true + +Performance: + Exclude: + - '**/test/**/*' + - '**/spec/**/*' + +# Prefer &&/|| over and/or. +Style/AndOr: + Enabled: true + +# Do not use braces for hash literals when they are the last argument of a +# method call. +Style/BracesAroundHashParameters: + Enabled: true + EnforcedStyle: context_dependent + +# Align `when` with `case`. +Layout/CaseIndentation: + Enabled: true + +# Align comments with method definitions. +Layout/CommentIndentation: + Enabled: true + +Layout/ElseAlignment: + Enabled: true + +# Align `end` with the matching keyword or starting expression except for +# assignments, where it should be aligned with the LHS. +Layout/EndAlignment: + Enabled: true + EnforcedStyleAlignWith: variable + AutoCorrect: true + +Layout/EmptyLineAfterMagicComment: + Enabled: true + +Layout/EmptyLinesAroundBlockBody: + Enabled: true + +# In a regular class definition, no empty lines around the body. +Layout/EmptyLinesAroundClassBody: + Enabled: true + +# In a regular method definition, no empty lines around the body. +Layout/EmptyLinesAroundMethodBody: + Enabled: true + +# In a regular module definition, no empty lines around the body. +Layout/EmptyLinesAroundModuleBody: + Enabled: true + +Layout/FirstParameterIndentation: + Enabled: true + +# Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }. +Style/HashSyntax: + Enabled: true + +# Method definitions after `private` or `protected` isolated calls need one +# extra level of indentation. +Layout/IndentationConsistency: + Enabled: true + +# Two spaces, no tabs (for indentation). +Layout/IndentationWidth: + Enabled: true + +Layout/LeadingCommentSpace: + Enabled: true + +Layout/SpaceAfterColon: + Enabled: true + +Layout/SpaceAfterComma: + Enabled: true + +Layout/SpaceAroundEqualsInParameterDefault: + Enabled: true + +Layout/SpaceAroundKeyword: + Enabled: true + +Layout/SpaceAroundOperators: + Enabled: true + +Layout/SpaceBeforeComma: + Enabled: true + +Layout/SpaceBeforeFirstArg: + Enabled: true + +Style/DefWithParentheses: + Enabled: true + +# Defining a method with parameters needs parentheses. +Style/MethodDefParentheses: + Enabled: true + +Style/FrozenStringLiteralComment: + Enabled: true + EnforcedStyle: always + +# Use `foo {}` not `foo{}`. +Layout/SpaceBeforeBlockBraces: + Enabled: true + +# Use `foo { bar }` not `foo {bar}`. +Layout/SpaceInsideBlockBraces: + Enabled: true + +# Use `{ a: 1 }` not `{a:1}`. +Layout/SpaceInsideHashLiteralBraces: + Enabled: true + +Layout/SpaceInsideParens: + Enabled: true + +# Check quotes usage according to lint rule below. +Style/StringLiterals: + Enabled: true + EnforcedStyle: double_quotes + +# Detect hard tabs, no hard tabs. +Layout/Tab: + Enabled: true + +# Blank lines should not have any spaces. +Layout/TrailingBlankLines: + Enabled: true + +# No trailing whitespace. +Layout/TrailingWhitespace: + Enabled: true + +# Use quotes for string literals when they are enough. +Style/UnneededPercentQ: + Enabled: true + +# Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg. +Lint/RequireParentheses: + Enabled: true + +Lint/StringConversionInInterpolation: + Enabled: true + +Lint/UriEscapeUnescape: + Enabled: true + +Style/ParenthesesAroundCondition: + Enabled: true + +Style/RedundantReturn: + Enabled: true + AllowMultipleReturnValues: true + +Style/Semicolon: + Enabled: true + AllowAsExpressionSeparator: true + +# Prefer Foo.method over Foo::method +Style/ColonMethodCall: + Enabled: true + +Style/TrivialAccessors: + Enabled: true + +Performance/FlatMap: + Enabled: true + +Performance/RedundantMerge: + Enabled: true + +Performance/StartWith: + Enabled: true + +Performance/EndWith: + Enabled: true + +Performance/RegexpMatch: + Enabled: true
\ No newline at end of file diff --git a/.travis.yml b/.travis.yml index e40d15e..4dc862b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,22 @@ language: ruby rvm: - - 2.2.4 - - 2.3.1 - - 2.4.0 + - 2.3 + - 2.4 + - 2.5 + - 2.6 - ruby-head - jruby-9.1.6.0 +env: + - LONG_RUN=true matrix: include: - - rvm: 2.0.0 - gemfile: Gemfile_ruby2 - - rvm: 2.1.8 - gemfile: Gemfile_ruby2 + - rvm: 2.6 + env: RUBYOPT=--jit LONG_RUN=true + - rvm: ruby-head + env: RUBYOPT=--jit LONG_RUN=true allow_failures: - rvm: ruby-head + - rvm: ruby-head + env: RUBYOPT=--jit LONG_RUN=true - rvm: jruby-9.1.6.0 bundler_args: --without local_development diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a89cab..d93da95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,33 @@ ## Unreleased +### Fixed +- Fixed inconsistent column length for CSV [375](https://github.com/roo-rb/roo/pull/375) +- Fixed formatted_value with `%` for Excelx [416](https://github.com/roo-rb/roo/pull/416) +- Improved Memory consumption and performance [434](https://github.com/roo-rb/roo/pull/434) [449](https://github.com/roo-rb/roo/pull/449) [454](https://github.com/roo-rb/roo/pull/454) [456](https://github.com/roo-rb/roo/pull/456) [458](https://github.com/roo-rb/roo/pull/458) [462](https://github.com/roo-rb/roo/pull/462) [466](https://github.com/roo-rb/roo/pull/466) +- Accept both Transitional and Strict Type for Excelx's worksheets [441](https://github.com/roo-rb/roo/pull/441) +- Fixed ruby warnings [442](https://github.com/roo-rb/roo/pull/442) [476](https://github.com/roo-rb/roo/pull/476) +- Restore support for URL as file identifier for CSV [462](https://github.com/roo-rb/roo/pull/462) +- Fixed missing location for Excelx's links [482](https://github.com/roo-rb/roo/pull/482) + +### Changed / Added +- Drop support for ruby 2.2.x and lower +- Updated rubyzip version for fixing security issue. Now minimal version is 1.2.1 +- Roo::Excelx::Coordinate now inherits Array [458](https://github.com/roo-rb/roo/pull/458) +- Improved Roo::HeaderRowNotFoundError exception's message [461](https://github.com/roo-rb/roo/pull/461) +- Added `empty_cell` option which by default disable allocation for Roo::Excelx::Cell::Empty [464](https://github.com/roo-rb/roo/pull/464) +- Added support for variable number of decimals for Excelx's formatted_value [387](https://github.com/roo-rb/roo/pull/387) +- Added `disable_html_injection` option to disable html injection for shared string in `Roo::Excelx` [392](https://github.com/roo-rb/roo/pull/392) +- Added image extraction for Excelx [414](https://github.com/roo-rb/roo/pull/414) [397](https://github.com/roo-rb/roo/pull/397) +- Added support for `1e6` as scientific notation for Excelx [433](https://github.com/roo-rb/roo/pull/433) +- Added support for Integer as 0 based index for Excelx's `sheet_for` [455](https://github.com/roo-rb/roo/pull/455) +- Extended `no_hyperlinks` option for non streaming Excelx methods [459](https://github.com/roo-rb/roo/pull/459) +- Added `empty_cell` option to disable Roo::Excelx::Cell::Empty allocation for Excelx [464](https://github.com/roo-rb/roo/pull/464) +- Added support for Integer with leading zero for Roo:Excelx [479](https://github.com/roo-rb/roo/pull/479) +- Refactored Excelx code [453](https://github.com/roo-rb/roo/pull/453) [477](https://github.com/roo-rb/roo/pull/477) [483](https://github.com/roo-rb/roo/pull/483) [484](https://github.com/roo-rb/roo/pull/484) + +### Deprecations +- Roo::Excelx::Sheet#present_cells is deprecated [454](https://github.com/roo-rb/roo/pull/454) +- Roo::Utils.split_coordinate is deprecated [458](https://github.com/roo-rb/roo/pull/458) +- Roo::Excelx::Cell::Base#link is deprecated [457](https://github.com/roo-rb/roo/pull/457) ## [2.7.1] 2017-01-03 ### Fixed @@ -48,7 +77,7 @@ - Discard hyperlinks lookups to allow streaming parsing without loading whole files ## [2.4.0] 2016-05-14 -### Fixed +### Fixed - Fixed opening spreadsheets with charts [315](https://github.com/roo-rb/roo/pull/315) - Fixed memory issues for Roo::Utils.number_to_letter [308](https://github.com/roo-rb/roo/pull/308) - Fixed Roo::Excelx::Cell::Number to recognize floating point numbers [306](https://github.com/roo-rb/roo/pull/306) diff --git a/Gemfile_ruby2 b/Gemfile_ruby2 deleted file mode 100644 index 20bcade..0000000 --- a/Gemfile_ruby2 +++ /dev/null @@ -1,30 +0,0 @@ -source 'https://rubygems.org' - -gemspec - -gem 'nokogiri', "< 1.7.0" - -group :test do - # additional testing libs - gem 'shoulda' - gem 'rspec', '>= 3.0.0' - gem 'simplecov', '>= 0.9.0', require: false - gem 'coveralls', require: false - gem "activesupport", "~> 4.2.0" - gem "tins", '~> 1.6.0' - gem "term-ansicolor", "~> 1.3.2" - gem "minitest-reporters" -end - -group :local_development do - gem "listen", "~> 3.0.6" - gem 'terminal-notifier-guard', require: false if RUBY_PLATFORM.downcase.include?('darwin') - gem 'guard-rspec', '>= 4.3.1', require: false - gem 'guard-minitest', require: false - gem 'guard-bundler', require: false - gem 'guard-preek', require: false - gem 'guard-rubocop', require: false - gem 'guard-reek', github: 'pericles/guard-reek', require: false - gem 'rb-readline' - gem 'pry' -end @@ -1,4 +1,6 @@ Copyright (c) 2008-2014 Thomas Preymesser, Ben Woosley +Copyright (c) 2014-2017 Ben Woosley +Copyright (c) 2015-2017 Oleksandr Simonov, Steven Daniels MIT License @@ -1,6 +1,6 @@ # Roo -[![Build Status](https://img.shields.io/travis/roo-rb/roo.svg?style=flat-square)](https://travis-ci.org/roo-rb/roo) [![Code Climate](https://img.shields.io/codeclimate/github/roo-rb/roo.svg?style=flat-square)](https://codeclimate.com/github/roo-rb/roo) [![Coverage Status](https://img.shields.io/coveralls/roo-rb/roo.svg?style=flat-square)](https://coveralls.io/r/roo-rb/roo) [![Gem Version](https://img.shields.io/gem/v/roo.svg?style=flat-square)](https://rubygems.org/gems/roo) +[![Build Status](https://img.shields.io/travis/roo-rb/roo.svg?style=flat-square)](https://travis-ci.org/roo-rb/roo) [![Maintainability](https://api.codeclimate.com/v1/badges/be8d7bf34e2aeaf67c62/maintainability)](https://codeclimate.com/github/roo-rb/roo/maintainability) [![Coverage Status](https://img.shields.io/coveralls/roo-rb/roo.svg?style=flat-square)](https://coveralls.io/r/roo-rb/roo) [![Gem Version](https://img.shields.io/gem/v/roo.svg?style=flat-square)](https://rubygems.org/gems/roo) Roo implements read access for all common spreadsheet types. It can handle: * Excel 2007 - 2013 formats (xlsx, xlsm) @@ -89,13 +89,13 @@ sheet.last_column You can access the top-left cell in the following ways ```ruby -s.cell(1,1) -s.cell('A',1) -s.cell(1,'A') -s.a1 +sheet.cell(1,1) +sheet.cell('A',1) +sheet.cell(1,'A') +sheet.a1 # Access the second sheet's top-left cell. -s.cell(1,'A',s.sheets[1]) +sheet.cell(1,'A',sheet.sheets[1]) ``` #### Querying a spreadsheet @@ -117,6 +117,12 @@ sheet.parse(id: /UPC|SKU/, qty: /ATS*\sATP\s*QTY\z/) # => [{:id => 727880013358, :qty => 12}, ...] ``` +Use the ``:headers`` option to include the header row in the parsed content. + +```ruby +sheet.parse(headers: true) +``` + Use the ``:header_search`` option to locate the header row and assign the header names. ```ruby @@ -129,6 +135,16 @@ Use the ``:clean`` option to strip out control characters and surrounding white sheet.parse(clean: true) ``` +#### Options + +When opening the file you can add a hash of options. + +##### expand_merged_ranges +If you open a document with merged cells and do not want to end up with nil values for the rows after the first one. +```ruby +xlsx = Roo::Excelx.new('./roo_error.xlsx', {:expand_merged_ranges => true}) +``` + ### Exporting spreadsheets Roo has the ability to export sheets using the following formats. It will only export the ``default_sheet``. @@ -230,7 +246,7 @@ ods.formula('A', 2) ```ruby # Load a CSV file -s = Roo::CSV.new("mycsv.csv") +csv = Roo::CSV.new("mycsv.csv") ``` Because Roo uses the [standard CSV library](), you can use options available to that library to parse csv files. You can pass options using the ``csv_options`` key. @@ -240,10 +256,10 @@ For instance, you can load tab-delimited files (``.tsv``), and you can use a par ```ruby # Load a tab-delimited csv -s = Roo::CSV.new("mytsv.tsv", csv_options: {col_sep: "\t"}) +csv = Roo::CSV.new("mytsv.tsv", csv_options: {col_sep: "\t"}) # Load a csv with an explicit encoding -s = Roo::CSV.new("mycsv.csv", csv_options: {encoding: Encoding::ISO_8859_1}) +csv = Roo::CSV.new("mycsv.csv", csv_options: {encoding: Encoding::ISO_8859_1}) ``` ## Upgrading from Roo 1.13.x @@ -272,9 +288,6 @@ You can run the tests/examples with Rspec like reporters by running Roo also has a few tests that take a long time (5+ seconds). To run these, use `LONG_RUN=true bundle exec rake` -When testing using Ruby 2.0 or 2.1, use this command: -`BUNDLE_GEMFILE=Gemfile_ruby2 bundle exec rake` - ### Issues If you find an issue, please create a gist and refer to it in an issue ([sample gist](https://gist.github.com/stevendaniels/98a05849036e99bb8b3c)). Here are some instructions for creating such a gist. @@ -1,3 +1,6 @@ +# frozen_string_literal: true + +require 'roo/version' require 'roo/constants' require 'roo/errors' require 'roo/spreadsheet' @@ -9,7 +12,7 @@ module Roo autoload :Excelx, 'roo/excelx' autoload :CSV, 'roo/csv' - TEMP_PREFIX = 'roo_'.freeze + TEMP_PREFIX = 'roo_' CLASS_FOR_EXTENSION = { ods: Roo::OpenOffice, diff --git a/lib/roo/base.rb b/lib/roo/base.rb index 53e4075..19eb844 100644 --- a/lib/roo/base.rb +++ b/lib/roo/base.rb @@ -1,9 +1,7 @@ -# encoding: utf-8 - -require 'tmpdir' -require 'stringio' -require 'nokogiri' -require 'roo/utils' +require "tmpdir" +require "stringio" +require "nokogiri" +require "roo/utils" require "roo/formatters/base" require "roo/formatters/csv" require "roo/formatters/matrix" @@ -19,8 +17,8 @@ class Roo::Base include Roo::Formatters::XML include Roo::Formatters::YAML - MAX_ROW_COL = 999_999.freeze - MIN_ROW_COL = 0.freeze + MAX_ROW_COL = 999_999 + MIN_ROW_COL = 0 attr_reader :headers @@ -28,7 +26,7 @@ class Roo::Base attr_accessor :header_line def self.TEMP_PREFIX - warn '[DEPRECATION] please access TEMP_PREFIX via Roo::TEMP_PREFIX' + warn "[DEPRECATION] please access TEMP_PREFIX via Roo::TEMP_PREFIX" Roo::TEMP_PREFIX end @@ -56,6 +54,11 @@ class Roo::Base if self.class.respond_to?(:finalize_tempdirs) self.class.finalize_tempdirs(object_id) end + + instance_variables.each do |instance_variable| + instance_variable_set(instance_variable, nil) + end + nil end @@ -64,10 +67,10 @@ class Roo::Base end # sets the working sheet in the document - # 'sheet' can be a number (1 = first sheet) or the name of a sheet. + # 'sheet' can be a number (0 = first sheet) or the name of a sheet. def default_sheet=(sheet) validate_sheet!(sheet) - @default_sheet = sheet + @default_sheet = sheet.is_a?(String) ? sheet : sheets[sheet] @first_row[sheet] = @last_row[sheet] = @first_column[sheet] = @last_column[sheet] = nil @cells_read[sheet] = false end @@ -100,7 +103,7 @@ class Roo::Base def collect_last_row_col_for_sheet(sheet) first_row = first_column = MAX_ROW_COL last_row = last_column = MIN_ROW_COL - @cell[sheet].each_pair do|key, value| + @cell[sheet].each_pair do |key, value| next unless value first_row = [first_row, key.first.to_i].min last_row = [last_row, key.first.to_i].max @@ -110,13 +113,12 @@ class Roo::Base { first_row: first_row, first_column: first_column, last_row: last_row, last_column: last_column } end - %w(first_row last_row first_column last_column).each do |key| - class_eval <<-EOS, __FILE__, __LINE__ + 1 - def #{key}(sheet = default_sheet) # def first_row(sheet = default_sheet) - read_cells(sheet) # read_cells(sheet) - @#{key}[sheet] ||= first_last_row_col_for_sheet(sheet)[:#{key}] # @first_row[sheet] ||= first_last_row_col_for_sheet(sheet)[:first_row] - end # end - EOS + %i(first_row last_row first_column last_column).each do |key| + ivar = "@#{key}".to_sym + define_method(key) do |sheet = default_sheet| + read_cells(sheet) + instance_variable_get(ivar)[sheet] ||= first_last_row_col_for_sheet(sheet)[key] + end end def inspect @@ -203,16 +205,16 @@ class Roo::Base "Number of sheets: #{sheets.size}\n"\ "Sheets: #{sheets.join(', ')}\n" n = 1 - sheets.each do|sheet| + sheets.each do |sheet| self.default_sheet = sheet - result << 'Sheet ' + n.to_s + ":\n" + result << "Sheet " + n.to_s + ":\n" if first_row result << " First row: #{first_row}\n" result << " Last row: #{last_row}\n" result << " First column: #{::Roo::Utils.number_to_letter(first_column)}\n" result << " Last column: #{::Roo::Utils.number_to_letter(last_column)}" else - result << ' - empty -' + result << " - empty -" end result << "\n" if sheet != sheets.last n += 1 @@ -286,12 +288,12 @@ class Roo::Base clean_sheet_if_need(options) search_or_set_header(options) headers = @headers || - Hash[(first_column..last_column).map do |col| - [cell(@header_line, col), col] - end] + (first_column..last_column).each_with_object({}) do |col, hash| + hash[cell(@header_line, col)] = col + end @header_line.upto(last_row) do |line| - yield(Hash[headers.map { |k, v| [k, cell(line, v)] }]) + yield(headers.each_with_object({}) { |(k, v), hash| hash[k] = cell(line, v) }) end end end @@ -306,18 +308,22 @@ class Roo::Base def row_with(query, return_headers = false) line_no = 0 + closest_mismatched_headers = [] each do |row| line_no += 1 headers = query.map { |q| row.grep(q)[0] }.compact - if headers.length == query.length @header_line = line_no return return_headers ? headers : line_no - elsif line_no > 100 - raise Roo::HeaderRowNotFoundError + else + closest_mismatched_headers = headers if headers.length > closest_mismatched_headers.length + if line_no > 100 + break + end end end - raise Roo::HeaderRowNotFoundError + missing_headers = query.select { |q| closest_mismatched_headers.grep(q).empty? } + raise Roo::HeaderRowNotFoundError, missing_headers end protected @@ -330,7 +336,7 @@ class Roo::Base filename = File.basename(filename, File.extname(filename)) end - if uri?(filename) && (qs_begin = filename.rindex('?')) + if uri?(filename) && (qs_begin = filename.rindex("?")) filename = filename[0..qs_begin - 1] end exts = Array(exts) @@ -356,7 +362,7 @@ class Roo::Base # Diese Methode ist eine temp. Loesung, um zu erforschen, ob der # Zugriff mit numerischen Keys schneller ist. def key_to_num(str) - r, c = str.split(',') + r, c = str.split(",") [r.to_i, c.to_i] end @@ -418,9 +424,9 @@ class Roo::Base def find_by_conditions(options) rows = first_row.upto(last_row) - header_for = Hash[1.upto(last_column).map do |col| - [col, cell(@header_line, col)] - end] + header_for = 1.upto(last_column).each_with_object({}) do |col, hash| + hash[col] = cell(@header_line, col) + end # are all conditions met? conditions = options[:conditions] @@ -435,9 +441,9 @@ class Roo::Base rows.map { |i| row(i) } else rows.map do |i| - Hash[1.upto(row(i).size).map do |j| - [header_for.fetch(j), cell(i, j)] - end] + 1.upto(row(i).size).each_with_object({}) do |j, hash| + hash[header_for.fetch(j)] = cell(i, j) + end end end end @@ -455,7 +461,7 @@ class Roo::Base def find_basename(filename) if uri?(filename) - require 'uri' + require "uri" uri = URI.parse filename File.basename(uri.path) elsif !is_stream?(filename) @@ -464,9 +470,9 @@ class Roo::Base end def make_tmpdir(prefix = nil, root = nil, &block) - warn '[DEPRECATION] extend Roo::Tempdir and use its .make_tempdir instead' + warn "[DEPRECATION] extend Roo::Tempdir and use its .make_tempdir instead" prefix = "#{Roo::TEMP_PREFIX}#{prefix}" - root ||= ENV['ROO_TMP'] + root ||= ENV["ROO_TMP"] if block_given? # folder is deleted at end of block @@ -485,14 +491,17 @@ class Roo::Base end def sanitize_value(v) - v.gsub(/[[:cntrl:]]|^[\p{Space}]+|[\p{Space}]+$/, '') + v.gsub(/[[:cntrl:]]|^[\p{Space}]+|[\p{Space}]+$/, "") end def set_headers(hash = {}) # try to find header row with all values or give an error # then create new hash by indexing strings and keeping integers for header array - @headers = row_with(hash.values, true) - @headers = Hash[hash.keys.zip(@headers.map { |x| header_index(x) })] + header_row = row_with(hash.values, true) + @headers = {} + hash.each_with_index do |(key, _), index| + @headers[key] = header_index(header_row[index]) + end end def header_index(query) @@ -525,17 +534,17 @@ class Roo::Base end def uri?(filename) - filename.start_with?('http://', 'https://', 'ftp://') + filename.start_with?("http://", "https://", "ftp://") rescue false end def download_uri(uri, tmpdir) - require 'open-uri' + require "open-uri" tempfilename = File.join(tmpdir, find_basename(uri)) begin - File.open(tempfilename, 'wb') do |file| - open(uri, 'User-Agent' => "Ruby/#{RUBY_VERSION}") do |net| + File.open(tempfilename, "wb") do |file| + open(uri, "User-Agent" => "Ruby/#{RUBY_VERSION}") do |net| file.write(net.read) end end @@ -546,15 +555,15 @@ class Roo::Base end def open_from_stream(stream, tmpdir) - tempfilename = File.join(tmpdir, 'spreadsheet') - File.open(tempfilename, 'wb') do |file| + tempfilename = File.join(tmpdir, "spreadsheet") + File.open(tempfilename, "wb") do |file| file.write(stream[7..-1]) end - File.join(tmpdir, 'spreadsheet') + File.join(tmpdir, "spreadsheet") end def unzip(filename, tmpdir) - require 'zip/filesystem' + require "zip/filesystem" Zip::File.open(filename) do |zip| process_zipfile_packed(zip, tmpdir) @@ -567,7 +576,7 @@ class Roo::Base when nil fail ArgumentError, "Error: sheet 'nil' not valid" when Integer - sheets.fetch(sheet - 1) do + sheets.fetch(sheet) do fail RangeError, "sheet index #{sheet} not found" end when String @@ -579,16 +588,16 @@ class Roo::Base end end - def process_zipfile_packed(zip, tmpdir, path = '') + def process_zipfile_packed(zip, tmpdir, path = "") if zip.file.file? path # extract and return filename - File.open(File.join(tmpdir, path), 'wb') do |file| + File.open(File.join(tmpdir, path), "wb") do |file| file.write(zip.read(path)) end File.join(tmpdir, path) else ret = nil - path += '/' unless path.empty? + path += "/" unless path.empty? zip.dir.foreach(path) do |filename| ret = process_zipfile_packed(zip, tmpdir, path + filename) end diff --git a/lib/roo/constants.rb b/lib/roo/constants.rb index 90d54ee..1ae5ca7 100644 --- a/lib/roo/constants.rb +++ b/lib/roo/constants.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + module Roo - ROO_EXCEL_NOTICE = "Excel support has been extracted to roo-xls due to its dependency on the GPL'd spreadsheet gem. Install roo-xls to use Roo::Excel.".freeze - ROO_EXCELML_NOTICE = "Excel SpreadsheetML support has been extracted to roo-xls. Install roo-xls to use Roo::Excel2003XML.".freeze - ROO_GOOGLE_NOTICE = "Google support has been extracted to roo-google. Install roo-google to use Roo::Google.".freeze + ROO_EXCEL_NOTICE = "Excel support has been extracted to roo-xls due to its dependency on the GPL'd spreadsheet gem. Install roo-xls to use Roo::Excel." + ROO_EXCELML_NOTICE = "Excel SpreadsheetML support has been extracted to roo-xls. Install roo-xls to use Roo::Excel2003XML." + ROO_GOOGLE_NOTICE = "Google support has been extracted to roo-google. Install roo-google to use Roo::Google." end
\ No newline at end of file diff --git a/lib/roo/csv.rb b/lib/roo/csv.rb index c161c64..516def6 100644 --- a/lib/roo/csv.rb +++ b/lib/roo/csv.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "csv" require "time" @@ -63,25 +65,31 @@ module Roo def read_cells(sheet = default_sheet) sheet ||= default_sheet return if @cells_read[sheet] - set_row_count(sheet) - set_column_count(sheet) - row_num = 1 + row_num = 0 + max_col_num = 0 each_row csv_options do |row| - row.each_with_index do |elem, col_num| - coordinate = [row_num, col_num + 1] + row_num += 1 + col_num = 0 + + row.each do |elem| + col_num += 1 + coordinate = [row_num, col_num] @cell[coordinate] = elem @cell_type[coordinate] = celltype_class(elem) end - row_num += 1 + + max_col_num = col_num if col_num > max_col_num end + set_row_count(sheet, row_num) + set_column_count(sheet, max_col_num) @cells_read[sheet] = true end def each_row(options, &block) if uri?(filename) - each_row_using_temp_dir(filename) + each_row_using_tempdir(options, &block) elsif is_stream?(filename_or_stream) ::CSV.new(filename_or_stream, options).each(&block) else @@ -89,24 +97,24 @@ module Roo end end - def each_row_using_tempdir + def each_row_using_tempdir(options, &block) ::Dir.mktmpdir(Roo::TEMP_PREFIX, ENV["ROO_TMP"]) do |tmpdir| tmp_filename = download_uri(filename, tmpdir) ::CSV.foreach(tmp_filename, options, &block) end end - def set_row_count(sheet) + def set_row_count(sheet, last_row) @first_row[sheet] = 1 - @last_row[sheet] = ::CSV.readlines(@filename, csv_options).size + @last_row[sheet] = last_row @last_row[sheet] = @first_row[sheet] if @last_row[sheet].zero? nil end - def set_column_count(sheet) + def set_column_count(sheet, last_col) @first_column[sheet] = 1 - @last_column[sheet] = (::CSV.readlines(@filename, csv_options).first || []).size + @last_column[sheet] = last_col @last_column[sheet] = @first_column[sheet] if @last_column[sheet].zero? nil diff --git a/lib/roo/excelx.rb b/lib/roo/excelx.rb index 82c1431..f9f0ee2 100644..100755 --- a/lib/roo/excelx.rb +++ b/lib/roo/excelx.rb @@ -24,8 +24,9 @@ module Roo require 'roo/excelx/sheet_doc' require 'roo/excelx/coordinate' require 'roo/excelx/format' + require 'roo/excelx/images' - delegate [:styles, :workbook, :shared_strings, :rels_files, :sheet_files, :comments_files] => :@shared + delegate [:styles, :workbook, :shared_strings, :rels_files, :sheet_files, :comments_files, :image_rels, :image_files] => :@shared ExceedsMaxError = Class.new(StandardError) # initialization and opening of a spreadsheet file @@ -39,7 +40,10 @@ module Roo sheet_options = {} sheet_options[:expand_merged_ranges] = (options[:expand_merged_ranges] || false) sheet_options[:no_hyperlinks] = (options[:no_hyperlinks] || false) + sheet_options[:empty_cell] = (options[:empty_cell] || false) + shared_options = {} + shared_options[:disable_html_wrapper] = (options[:disable_html_wrapper] || false) unless is_stream?(filename_or_stream) file_type_check(filename_or_stream, %w[.xlsx .xlsm], 'an Excel 2007', file_warning, packed) basename = find_basename(filename_or_stream) @@ -52,7 +56,7 @@ module Roo @tmpdir = self.class.make_tempdir(self, basename, options[:tmpdir_root]) ObjectSpace.define_finalizer(self, self.class.finalize(object_id)) - @shared = Shared.new(@tmpdir) + @shared = Shared.new(@tmpdir, shared_options) @filename = local_filename(filename_or_stream, @tmpdir, packed) process_zipfile(@filename || filename_or_stream) @@ -62,10 +66,10 @@ module Roo end end.compact @sheets = [] - @sheets_by_name = Hash[@sheet_names.map.with_index do |sheet_name, n| - @sheets[n] = Sheet.new(sheet_name, @shared, n, sheet_options) - [sheet_name, @sheets[n]] - end] + @sheets_by_name = {} + @sheet_names.each_with_index do |sheet_name, n| + @sheets_by_name[sheet_name] = @sheets[n] = Sheet.new(sheet_name, @shared, n, sheet_options) + end if cell_max cell_count = ::Roo::Utils.num_cells_in_range(sheet_for(options.delete(:sheet)).dimensions) @@ -94,7 +98,12 @@ module Roo def sheet_for(sheet) sheet ||= default_sheet validate_sheet!(sheet) - @sheets_by_name[sheet] + @sheets_by_name[sheet] || @sheets[sheet] + end + + def images(sheet = nil) + images_names = sheet_for(sheet).images.map(&:last) + images_names.map { |iname| image_files.find { |ifile| ifile[iname] } } end # Returns the content of a spreadsheet-cell. @@ -325,7 +334,7 @@ module Roo wb.extract(path) workbook_doc = Roo::Utils.load_xml(path).remove_namespaces! - workbook_doc.xpath('//sheet').map { |s| s.attributes['id'].value } + workbook_doc.xpath('//sheet').map { |s| s['id'] } end # Internal @@ -349,17 +358,13 @@ module Roo wb_rels.extract(path) rels_doc = Roo::Utils.load_xml(path).remove_namespaces! - worksheet_type = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet' relationships = rels_doc.xpath('//Relationship').select do |relationship| - relationship.attributes['Type'].value == worksheet_type + worksheet_types.include? relationship['Type'] end - relationships.inject({}) do |hash, relationship| - attributes = relationship.attributes - id = attributes['Id'] - hash[id.value] = attributes['Target'].value - hash + relationships.each_with_object({}) do |relationship, hash| + hash[relationship['Id']] = relationship['Target'] end end @@ -376,6 +381,15 @@ module Roo end end + def extract_images(entries, tmpdir) + img_entries = entries.select { |e| e.name[/media\/image([0-9]+)/] } + img_entries.each do |entry| + path = "#{@tmpdir}/roo#{entry.name.gsub(/xl\/|\//, "_")}" + image_files << path + entry.extract(path) + end + end + # Extracts all needed files from the zip file def process_zipfile(zipfilename_or_stream) @sheet_files = [] @@ -409,6 +423,7 @@ module Roo sheet_ids = extract_worksheet_ids(entries, "#{@tmpdir}/roo_workbook.xml") sheets = extract_worksheet_rels(entries, "#{@tmpdir}/roo_workbook.xml.rels") extract_sheets_in_order(entries, sheet_ids, sheets, @tmpdir) + extract_images(entries, @tmpdir) entries.each do |entry| path = @@ -435,6 +450,10 @@ module Roo # drawings, etc. nr = Regexp.last_match[1].to_i rels_files[nr - 1] = "#{@tmpdir}/roo_rels#{nr}" + when /drawing([0-9]+).xml.rels$/ + # Extracting drawing relationships to make images lists for each sheet + nr = Regexp.last_match[1].to_i + image_rels[nr - 1] = "#{@tmpdir}/roo_image_rels#{nr}" end entry.extract(path) if path @@ -442,7 +461,14 @@ module Roo end def safe_send(object, method, *args) - object.send(method, *args) if object && object.respond_to?(method) + object.send(method, *args) if object&.respond_to?(method) + end + + def worksheet_types + [ + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet', # OOXML Transitional + 'http://purl.oclc.org/ooxml/officeDocument/relationships/worksheet' # OOXML Strict + ] end end end diff --git a/lib/roo/excelx/cell.rb b/lib/roo/excelx/cell.rb index 2fc78e1..7abd074 100644 --- a/lib/roo/excelx/cell.rb +++ b/lib/roo/excelx/cell.rb @@ -40,19 +40,23 @@ module Roo end def self.create_cell(type, *values) + cell_class(type)&.new(*values) + end + + def self.cell_class(type) case type when :string - Cell::String.new(*values) + Cell::String when :boolean - Cell::Boolean.new(*values) + Cell::Boolean when :number - Cell::Number.new(*values) + Cell::Number when :date - Cell::Date.new(*values) + Cell::Date when :datetime - Cell::DateTime.new(*values) + Cell::DateTime when :time - Cell::Time.new(*values) + Cell::Time end end diff --git a/lib/roo/excelx/cell/base.rb b/lib/roo/excelx/cell/base.rb index aea8808..51fc75f 100644 --- a/lib/roo/excelx/cell/base.rb +++ b/lib/roo/excelx/cell/base.rb @@ -1,13 +1,18 @@ +# frozen_string_literal: true + +require "roo/helpers/default_attr_reader" + module Roo class Excelx class Cell class Base + extend Roo::Helpers::DefaultAttrReader attr_reader :cell_type, :cell_value, :value # FIXME: I think style should be deprecated. Having a style attribute # for a cell doesn't really accomplish much. It seems to be used # when you want to export to excelx. - attr_reader :style + attr_reader_with_default default_type: :base, style: 1 # FIXME: Updating a cell's value should be able tochange the cell's type, @@ -34,14 +39,12 @@ module Roo attr_writer :value def initialize(value, formula, excelx_type, style, link, coordinate) - @link = !!link @cell_value = value - @cell_type = excelx_type - @formula = formula - @style = style + @cell_type = excelx_type if excelx_type + @formula = formula if formula + @style = style unless style == 1 @coordinate = coordinate - @type = :base - @value = link? ? Roo::Link.new(link, value) : value + @value = link ? Roo::Link.new(link, value) : value end def type @@ -50,16 +53,16 @@ module Roo elsif link? :link else - @type + default_type end end def formula? - !!@formula + !!(defined?(@formula) && @formula) end def link? - !!@link + Roo::Link === @value end alias_method :formatted_value, :value @@ -68,9 +71,16 @@ module Roo formatted_value end - # DEPRECATED: Please use link instead. + # DEPRECATED: Please use link? instead. def hyperlink - warn '[DEPRECATION] `hyperlink` is deprecated. Please use `link` instead.' + warn '[DEPRECATION] `hyperlink` is deprecated. Please use `link?` instead.' + link? + end + + # DEPRECATED: Please use link? instead. + def link + warn '[DEPRECATION] `link` is deprecated. Please use `link?` instead.' + link? end # DEPRECATED: Please use cell_value instead. @@ -88,6 +98,10 @@ module Roo def empty? false end + + def presence + empty? ? nil : self + end end end end diff --git a/lib/roo/excelx/cell/boolean.rb b/lib/roo/excelx/cell/boolean.rb index fe1f691..2cdfc22 100644 --- a/lib/roo/excelx/cell/boolean.rb +++ b/lib/roo/excelx/cell/boolean.rb @@ -1,17 +1,20 @@ +# frozen_string_literal: true + module Roo class Excelx class Cell class Boolean < Cell::Base - attr_reader :value, :formula, :format, :cell_type, :cell_value, :link, :coordinate + attr_reader :value, :formula, :format, :cell_value, :coordinate + + attr_reader_with_default default_type: :boolean, cell_type: :boolean def initialize(value, formula, style, link, coordinate) - super(value, formula, nil, style, link, coordinate) - @type = @cell_type = :boolean - @value = link? ? Roo::Link.new(link, value) : create_boolean(value) + super(value, formula, nil, style, nil, coordinate) + @value = link ? Roo::Link.new(link, value) : create_boolean(value) end def formatted_value - value ? 'TRUE'.freeze : 'FALSE'.freeze + value ? 'TRUE' : 'FALSE' end private @@ -19,7 +22,7 @@ module Roo def create_boolean(value) # FIXME: Using a boolean will cause methods like Base#to_csv to fail. # Roo is using some method to ignore false/nil values. - value.to_i == 1 ? true : false + value.to_i == 1 end end end diff --git a/lib/roo/excelx/cell/date.rb b/lib/roo/excelx/cell/date.rb index 8e2c6cb..8627bc5 100644 --- a/lib/roo/excelx/cell/date.rb +++ b/lib/roo/excelx/cell/date.rb @@ -4,23 +4,23 @@ module Roo class Excelx class Cell class Date < Roo::Excelx::Cell::DateTime - attr_reader :value, :formula, :format, :cell_type, :cell_value, :link, :coordinate + attr_reader :value, :formula, :format, :cell_type, :cell_value, :coordinate + + attr_reader_with_default default_type: :date def initialize(value, formula, excelx_type, style, link, base_date, coordinate) # NOTE: Pass all arguments to the parent class, DateTime. super - @type = :date @format = excelx_type.last - @value = link? ? Roo::Link.new(link, value) : create_date(base_date, value) + @value = link ? Roo::Link.new(link, value) : create_date(base_date, value) end private - def create_date(base_date, value) - date = base_date + value.to_i - yyyy, mm, dd = date.strftime('%Y-%m-%d').split('-') + def create_datetime(_,_); end - ::Date.new(yyyy.to_i, mm.to_i, dd.to_i) + def create_date(base_date, value) + base_date + value.to_i end end end diff --git a/lib/roo/excelx/cell/datetime.rb b/lib/roo/excelx/cell/datetime.rb index 35d93ac..63f3260 100644 --- a/lib/roo/excelx/cell/datetime.rb +++ b/lib/roo/excelx/cell/datetime.rb @@ -1,16 +1,21 @@ +# frozen_string_literal: true + require 'date' module Roo class Excelx class Cell class DateTime < Cell::Base - attr_reader :value, :formula, :format, :cell_value, :link, :coordinate + SECONDS_IN_DAY = 60 * 60 * 24 + + attr_reader :value, :formula, :format, :cell_value, :coordinate - def initialize(value, formula, excelx_type, style, link, base_date, coordinate) - super(value, formula, excelx_type, style, link, coordinate) - @type = :datetime + attr_reader_with_default default_type: :datetime + + def initialize(value, formula, excelx_type, style, link, base_timestamp, coordinate) + super(value, formula, excelx_type, style, nil, coordinate) @format = excelx_type.last - @value = link? ? Roo::Link.new(link, value) : create_datetime(base_date, value) + @value = link ? Roo::Link.new(link, value) : create_datetime(base_timestamp, value) end # Public: Returns formatted value for a datetime. Format's can be an @@ -78,7 +83,7 @@ module Roo TIME_FORMATS = { 'hh' => '%H', # Hour (24): 01 - 'h' => '%-k'.freeze, # Hour (24): 1 + 'h' => '%-k', # Hour (24): 1 # 'hh'.freeze => '%I'.freeze, # Hour (12): 08 # 'h'.freeze => '%-l'.freeze, # Hour (12): 8 'mm' => '%M', # Minute: 01 @@ -92,18 +97,9 @@ module Roo '0' => '%1N' # Fractional Seconds: tenths. } - def create_datetime(base_date, value) - date = base_date + value.to_f.round(6) - datetime_string = date.strftime('%Y-%m-%d %H:%M:%S.%N') - t = round_datetime(datetime_string) - - ::DateTime.civil(t.year, t.month, t.day, t.hour, t.min, t.sec) - end - - def round_datetime(datetime_string) - /(?<yyyy>\d+)-(?<mm>\d+)-(?<dd>\d+) (?<hh>\d+):(?<mi>\d+):(?<ss>\d+.\d+)/ =~ datetime_string - - ::Time.new(yyyy, mm, dd, hh, mi, ss.to_r).round(0) + def create_datetime(base_timestamp, value) + timestamp = (base_timestamp + (value.to_f.round(6) * SECONDS_IN_DAY)).round(0) + ::Time.at(timestamp).utc.to_datetime end end end diff --git a/lib/roo/excelx/cell/empty.rb b/lib/roo/excelx/cell/empty.rb index 49a20e7..f0c683c 100644 --- a/lib/roo/excelx/cell/empty.rb +++ b/lib/roo/excelx/cell/empty.rb @@ -3,10 +3,11 @@ module Roo class Excelx class Cell class Empty < Cell::Base - attr_reader :value, :formula, :format, :cell_type, :cell_value, :hyperlink, :coordinate + attr_reader :value, :formula, :format, :cell_type, :cell_value, :coordinate + + attr_reader_with_default default_type: nil, style: nil def initialize(coordinate) - @value = @formula = @format = @cell_type = @cell_value = @hyperlink = nil @coordinate = coordinate end diff --git a/lib/roo/excelx/cell/number.rb b/lib/roo/excelx/cell/number.rb index 2015562..9f23c4f 100644 --- a/lib/roo/excelx/cell/number.rb +++ b/lib/roo/excelx/cell/number.rb @@ -1,16 +1,19 @@ +# frozen_string_literal: true + module Roo class Excelx class Cell class Number < Cell::Base - attr_reader :value, :formula, :format, :cell_value, :link, :coordinate + attr_reader :value, :formula, :format, :cell_value, :coordinate + + # FIXME: change default_type to number. This will break brittle tests. + attr_reader_with_default default_type: :float def initialize(value, formula, excelx_type, style, link, coordinate) super - # FIXME: change @type to number. This will break brittle tests. # FIXME: Excelx_type is an array, but the first value isn't used. - @type = :float @format = excelx_type.last - @value = link? ? Roo::Link.new(link, value) : create_numeric(value) + @value = link ? Roo::Link.new(link, value) : create_numeric(value) end def create_numeric(number) @@ -21,48 +24,50 @@ module Roo when /\.0/ Float(number) else - (number.include?('.') || (/\A[-+]?\d+E[-+]\d+\z/i =~ number)) ? Float(number) : Integer(number) + (number.include?('.') || (/\A[-+]?\d+E[-+]?\d+\z/i =~ number)) ? Float(number) : Integer(number, 10) end end def formatted_value return @cell_value if Excelx::ERROR_VALUES.include?(@cell_value) - formatter = formats[@format] + formatter = generate_formatter(@format) if formatter.is_a? Proc formatter.call(@cell_value) - elsif zero_padded_number? - "%0#{@format.size}d" % @cell_value else Kernel.format(formatter, @cell_value) end end - def formats + def generate_formatter(format) # FIXME: numbers can be other colors besides red: # [BLACK], [BLUE], [CYAN], [GREEN], [MAGENTA], [RED], [WHITE], [YELLOW], [COLOR n] - { - 'General' => '%.0f', - '0' => '%.0f', - '0.00' => '%.2f', - '0.000000' => '%.6f', - '#,##0' => number_format('%.0f'), - '#,##0.00' => number_format('%.2f'), - '0%' => proc do |number| - Kernel.format('%d%', number.to_f * 100) - end, - '0.00%' => proc do |number| - Kernel.format('%.2f%', number.to_f * 100) - end, - '0.00E+00' => '%.2E', - '#,##0 ;(#,##0)' => number_format('%.0f', '(%.0f)'), - '#,##0 ;[Red](#,##0)' => number_format('%.0f', '[Red](%.0f)'), - '#,##0.00;(#,##0.00)' => number_format('%.2f', '(%.2f)'), - '#,##0.00;[Red](#,##0.00)' => number_format('%.2f', '[Red](%.2f)'), + case format + when /^General$/i then '%.0f' + when '0' then '%.0f' + when /^(0+)$/ then "%0#{$1.size}d" + when /^0\.(0+)$/ then "%.#{$1.size}f" + when '#,##0' then number_format('%.0f') + when '#,##0.00' then number_format('%.2f') + when '0%' + proc do |number| + Kernel.format('%d%%', number.to_f * 100) + end + when '0.00%' + proc do |number| + Kernel.format('%.2f%%', number.to_f * 100) + end + when '0.00E+00' then '%.2E' + when '#,##0 ;(#,##0)' then number_format('%.0f', '(%.0f)') + when '#,##0 ;[Red](#,##0)' then number_format('%.0f', '[Red](%.0f)') + when '#,##0.00;(#,##0.00)' then number_format('%.2f', '(%.2f)') + when '#,##0.00;[Red](#,##0.00)' then number_format('%.2f', '[Red](%.2f)') # FIXME: not quite sure what the format should look like in this case. - '##0.0E+0' => '%.1E', - '@' => proc { |number| number } - } + when '##0.0E+0' then '%.1E' + when '@' then proc { |number| number } + else + raise "Unknown format: #{format.inspect}" + end end private @@ -77,10 +82,6 @@ module Roo Kernel.format(formatter, number).reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse end end - - def zero_padded_number? - @format[/0+/] == @format - end end end end diff --git a/lib/roo/excelx/cell/string.rb b/lib/roo/excelx/cell/string.rb index 7967806..dacf0b6 100644 --- a/lib/roo/excelx/cell/string.rb +++ b/lib/roo/excelx/cell/string.rb @@ -2,12 +2,12 @@ module Roo class Excelx class Cell class String < Cell::Base - attr_reader :value, :formula, :format, :cell_type, :cell_value, :link, :coordinate + attr_reader :value, :formula, :format, :cell_value, :coordinate + + attr_reader_with_default default_type: :string, cell_type: :string def initialize(value, formula, style, link, coordinate) super(value, formula, nil, style, link, coordinate) - @type = @cell_type = :string - @value = link? ? Roo::Link.new(link, value) : value end def empty? diff --git a/lib/roo/excelx/cell/time.rb b/lib/roo/excelx/cell/time.rb index d661ab8..a1f0864 100644 --- a/lib/roo/excelx/cell/time.rb +++ b/lib/roo/excelx/cell/time.rb @@ -4,15 +4,16 @@ module Roo class Excelx class Cell class Time < Roo::Excelx::Cell::DateTime - attr_reader :value, :formula, :format, :cell_value, :link, :coordinate + attr_reader :value, :formula, :format, :cell_value, :coordinate + + attr_reader_with_default default_type: :time def initialize(value, formula, excelx_type, style, link, base_date, coordinate) # NOTE: Pass all arguments to DateTime super class. super - @type = :time @format = excelx_type.last @datetime = create_datetime(base_date, value) - @value = link? ? Roo::Link.new(link, value) : (value.to_f * 86_400).to_i + @value = link ? Roo::Link.new(link, value) : (value.to_f * 86_400).to_i end def formatted_value diff --git a/lib/roo/excelx/comments.rb b/lib/roo/excelx/comments.rb index 1a89908..65044a9 100644 --- a/lib/roo/excelx/comments.rb +++ b/lib/roo/excelx/comments.rb @@ -12,10 +12,10 @@ module Roo def extract_comments return {} unless doc_exists? - Hash[doc.xpath('//comments/commentList/comment').map do |comment| + doc.xpath('//comments/commentList/comment').each_with_object({}) do |comment, hash| value = (comment.at_xpath('./text/r/t') || comment.at_xpath('./text/t')).text - [::Roo::Utils.ref_to_key(comment.attributes['ref'].to_s), value] - end] + hash[::Roo::Utils.ref_to_key(comment['ref'].to_s)] = value + end end end end diff --git a/lib/roo/excelx/coordinate.rb b/lib/roo/excelx/coordinate.rb index 53b24ba..4f61b17 100644 --- a/lib/roo/excelx/coordinate.rb +++ b/lib/roo/excelx/coordinate.rb @@ -1,11 +1,18 @@ module Roo class Excelx - class Coordinate - attr_accessor :row, :column + class Coordinate < ::Array def initialize(row, column) - @row = row - @column = column + super() << row << column + freeze + end + + def row + self[0] + end + + def column + self[1] end end end diff --git a/lib/roo/excelx/extractor.rb b/lib/roo/excelx/extractor.rb index 1cdd13b..b87a84e 100644..100755 --- a/lib/roo/excelx/extractor.rb +++ b/lib/roo/excelx/extractor.rb @@ -1,16 +1,34 @@ +# frozen_string_literal: true + +require "roo/helpers/weak_instance_cache" + module Roo class Excelx class Extractor - def initialize(path) + include Roo::Helpers::WeakInstanceCache + + COMMON_STRINGS = { + t: "t", + r: "r", + s: "s", + ref: "ref", + html_tag_open: "<html>", + html_tag_closed: "</html>" + } + + def initialize(path, options = {}) @path = path + @options = options end private def doc - raise FileNotFound, "#{@path} file not found" unless doc_exists? + instance_cache(:@doc) do + raise FileNotFound, "#{@path} file not found" unless doc_exists? - ::Roo::Utils.load_xml(@path).remove_namespaces! + ::Roo::Utils.load_xml(@path).remove_namespaces! + end end def doc_exists? diff --git a/lib/roo/excelx/format.rb b/lib/roo/excelx/format.rb index 72b36d9..1c1968a 100644 --- a/lib/roo/excelx/format.rb +++ b/lib/roo/excelx/format.rb @@ -1,49 +1,57 @@ +# frozen_string_literal: true + module Roo class Excelx module Format + extend self EXCEPTIONAL_FORMATS = { 'h:mm am/pm' => :date, 'h:mm:ss am/pm' => :date } STANDARD_FORMATS = { - 0 => 'General'.freeze, - 1 => '0'.freeze, - 2 => '0.00'.freeze, - 3 => '#,##0'.freeze, - 4 => '#,##0.00'.freeze, - 9 => '0%'.freeze, - 10 => '0.00%'.freeze, - 11 => '0.00E+00'.freeze, - 12 => '# ?/?'.freeze, - 13 => '# ??/??'.freeze, - 14 => 'mm-dd-yy'.freeze, - 15 => 'd-mmm-yy'.freeze, - 16 => 'd-mmm'.freeze, - 17 => 'mmm-yy'.freeze, - 18 => 'h:mm AM/PM'.freeze, - 19 => 'h:mm:ss AM/PM'.freeze, - 20 => 'h:mm'.freeze, - 21 => 'h:mm:ss'.freeze, - 22 => 'm/d/yy h:mm'.freeze, - 37 => '#,##0 ;(#,##0)'.freeze, - 38 => '#,##0 ;[Red](#,##0)'.freeze, - 39 => '#,##0.00;(#,##0.00)'.freeze, - 40 => '#,##0.00;[Red](#,##0.00)'.freeze, - 45 => 'mm:ss'.freeze, - 46 => '[h]:mm:ss'.freeze, - 47 => 'mmss.0'.freeze, - 48 => '##0.0E+0'.freeze, - 49 => '@'.freeze + 0 => 'General', + 1 => '0', + 2 => '0.00', + 3 => '#,##0', + 4 => '#,##0.00', + 9 => '0%', + 10 => '0.00%', + 11 => '0.00E+00', + 12 => '# ?/?', + 13 => '# ??/??', + 14 => 'mm-dd-yy', + 15 => 'd-mmm-yy', + 16 => 'd-mmm', + 17 => 'mmm-yy', + 18 => 'h:mm AM/PM', + 19 => 'h:mm:ss AM/PM', + 20 => 'h:mm', + 21 => 'h:mm:ss', + 22 => 'm/d/yy h:mm', + 37 => '#,##0 ;(#,##0)', + 38 => '#,##0 ;[Red](#,##0)', + 39 => '#,##0.00;(#,##0.00)', + 40 => '#,##0.00;[Red](#,##0.00)', + 45 => 'mm:ss', + 46 => '[h]:mm:ss', + 47 => 'mmss.0', + 48 => '##0.0E+0', + 49 => '@' } def to_type(format) + @to_type ||= {} + @to_type[format] ||= _to_type(format) + end + + def _to_type(format) format = format.to_s.downcase if (type = EXCEPTIONAL_FORMATS[format]) type elsif format.include?('#') :float - elsif !format.match(/d+(?![\]])/).nil? || format.include?('y') + elsif format.include?('y') || !format.match(/d+(?![\]])/).nil? if format.include?('h') || format.include?('s') :datetime else @@ -58,7 +66,6 @@ module Roo end end - module_function :to_type end - end + end end diff --git a/lib/roo/excelx/images.rb b/lib/roo/excelx/images.rb new file mode 100644 index 0000000..cf0221f --- /dev/null +++ b/lib/roo/excelx/images.rb @@ -0,0 +1,26 @@ +require 'roo/excelx/extractor' + +module Roo + class Excelx + class Images < Excelx::Extractor + + # Returns: Hash { id1: extracted_file_name1 }, + # Example: { "rId1"=>"roo_media_image1.png", + # "rId2"=>"roo_media_image2.png", + # "rId3"=>"roo_media_image3.png" } + def list + @images ||= extract_images_names + end + + private + + def extract_images_names + return {} unless doc_exists? + + doc.xpath('/Relationships/Relationship').each_with_object({}) do |rel, hash| + hash[rel['Id']] = "roo" + rel['Target'].gsub(/\.\.\/|\//, '_') + end + end + end + end +end diff --git a/lib/roo/excelx/relationships.rb b/lib/roo/excelx/relationships.rb index 8a0ed97..19f9919 100644 --- a/lib/roo/excelx/relationships.rb +++ b/lib/roo/excelx/relationships.rb @@ -16,9 +16,9 @@ module Roo def extract_relationships return [] unless doc_exists? - Hash[doc.xpath('/Relationships/Relationship').map do |rel| - [rel.attribute('Id').text, rel] - end] + doc.xpath('/Relationships/Relationship').each_with_object({}) do |rel, hash| + hash[rel['Id']] = rel + end end end end diff --git a/lib/roo/excelx/shared.rb b/lib/roo/excelx/shared.rb index 3677fa2..bcd2c08 100644..100755 --- a/lib/roo/excelx/shared.rb +++ b/lib/roo/excelx/shared.rb @@ -4,12 +4,15 @@ module Roo # reduce memory usage and reduce the number of objects being passed # to various inititializers. class Shared - attr_accessor :comments_files, :sheet_files, :rels_files - def initialize(dir) + attr_accessor :comments_files, :sheet_files, :rels_files, :image_rels, :image_files + def initialize(dir, options = {}) @dir = dir @comments_files = [] @sheet_files = [] @rels_files = [] + @options = options + @image_rels = [] + @image_files = [] end def styles @@ -17,7 +20,7 @@ module Roo end def shared_strings - @shared_strings ||= SharedStrings.new(File.join(@dir, 'roo_sharedStrings.xml')) + @shared_strings ||= SharedStrings.new(File.join(@dir, 'roo_sharedStrings.xml'), @options) end def workbook @@ -27,6 +30,10 @@ module Roo def base_date workbook.base_date end + + def base_timestamp + workbook.base_timestamp + end end end end diff --git a/lib/roo/excelx/shared_strings.rb b/lib/roo/excelx/shared_strings.rb index f7caf7c..e70b623 100755 --- a/lib/roo/excelx/shared_strings.rb +++ b/lib/roo/excelx/shared_strings.rb @@ -1,16 +1,10 @@ +# frozen_string_literal: true + require 'roo/excelx/extractor' module Roo class Excelx class SharedStrings < Excelx::Extractor - - COMMON_STRINGS = { - t: "t", - r: "r", - html_tag_open: "<html>", - html_tag_closed: "</html>" - } - def [](index) to_a[index] end @@ -26,6 +20,7 @@ module Roo # Use to_html or to_a for html returns # See what is happening with commit??? def use_html?(index) + return false if @options[:disable_html_wrapper] to_html[index][/<([biu]|sup|sub)>/] end @@ -45,7 +40,7 @@ module Roo document = fix_invalid_shared_strings(doc) # read the shared strings xml document document.xpath('/sst/si').map do |si| - shared_string = '' + shared_string = +"" si.children.each do |elem| case elem.name when 'r' @@ -65,7 +60,7 @@ module Roo fix_invalid_shared_strings(doc) # read the shared strings xml document doc.xpath('/sst/si').map do |si| - html_string = '<html>' + html_string = '<html>'.dup si.children.each do |elem| case elem.name when 'r' @@ -95,7 +90,7 @@ module Roo # # Expected Output ::: "<html><sub|sup><b><i><u>TEXT</u></i></b></sub|/sup></html>" def extract_html_r(r_elem) - str = '' + str = +"" xml_elems = { sub: false, sup: false, @@ -103,7 +98,6 @@ module Roo i: false, u: false } - b, i, u, sub, sup = false, false, false, false, false r_elem.children.each do |elem| case elem.name when 'rPr' @@ -141,13 +135,13 @@ module Roo # This will return an html string def create_html(text, formatting) - tmp_str = '' + tmp_str = +"" formatting.each do |elem, val| tmp_str << "<#{elem}>" if val end tmp_str << text - reverse_format = Hash[formatting.to_a.reverse] - reverse_format.each do |elem, val| + + formatting.reverse_each do |elem, val| tmp_str << "</#{elem}>" if val end tmp_str diff --git a/lib/roo/excelx/sheet.rb b/lib/roo/excelx/sheet.rb index add92f0..840a053 100644 --- a/lib/roo/excelx/sheet.rb +++ b/lib/roo/excelx/sheet.rb @@ -4,11 +4,15 @@ module Roo class Sheet extend Forwardable - delegate [:styles, :workbook, :shared_strings, :rels_files, :sheet_files, :comments_files] => :@shared + delegate [:styles, :workbook, :shared_strings, :rels_files, :sheet_files, :comments_files, :image_rels] => :@shared + + attr_reader :images def initialize(name, shared, sheet_index, options = {}) @name = name @shared = shared + @sheet_index = sheet_index + @images = Images.new(image_rels[sheet_index]).list @rels = Relationships.new(rels_files[sheet_index]) @comments = Comments.new(comments_files[sheet_index]) @sheet = SheetDoc.new(sheet_files[sheet_index], @rels, shared, options) @@ -19,7 +23,14 @@ module Roo end def present_cells - @present_cells ||= cells.select { |_, cell| cell && !cell.empty? } + @present_cells ||= begin + warn %{ +[DEPRECATION] present_cells is deprecated. Alternate: + with activesupport => cells[key].presence + without activesupport => cells[key]&.presence + } + cells.select { |_, cell| cell&.presence } + end end # Yield each row as array of Excelx::Cell objects @@ -39,33 +50,33 @@ module Roo def row(row_number) first_column.upto(last_column).map do |col| - cells[[row_number, col]] - end.map { |cell| cell && cell.value } + cells[[row_number, col]]&.value + end end def column(col_number) first_row.upto(last_row).map do |row| - cells[[row, col_number]] - end.map { |cell| cell && cell.value } + cells[[row, col_number]]&.value + end end # returns the number of the first non-empty row def first_row - @first_row ||= present_cells.keys.map { |row, _| row }.min + @first_row ||= first_last_row_col[:first_row] end def last_row - @last_row ||= present_cells.keys.map { |row, _| row }.max + @last_row ||= first_last_row_col[:last_row] end # returns the number of the first non-empty column def first_column - @first_column ||= present_cells.keys.map { |_, col| col }.min + @first_column ||= first_last_row_col[:first_column] end # returns the number of the last non-empty column def last_column - @last_column ||= present_cells.keys.map { |_, col| col }.max + @last_column ||= first_last_row_col[:last_column] end def excelx_format(key) @@ -107,6 +118,34 @@ module Roo (cell.coordinate.column - 1 - last_column).times { pad << nil } pad end + + def first_last_row_col + @first_last_row_col ||= begin + first_row = last_row = first_col = last_col = nil + + cells.each do |(row, col), cell| + next unless cell&.presence + first_row ||= row + last_row ||= row + first_col ||= col + last_col ||= col + + if row > last_row + last_row = row + elsif row < first_row + first_row = row + end + + if col > last_col + last_col = col + elsif col < first_col + first_col = col + end + end + + {first_row: first_row, last_row: last_row, first_column: first_col, last_column: last_col} + end + end end end end diff --git a/lib/roo/excelx/sheet_doc.rb b/lib/roo/excelx/sheet_doc.rb index a705958..8b0c686 100755 --- a/lib/roo/excelx/sheet_doc.rb +++ b/lib/roo/excelx/sheet_doc.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'forwardable' require 'roo/excelx/extractor' @@ -5,7 +7,7 @@ module Roo class Excelx class SheetDoc < Excelx::Extractor extend Forwardable - delegate [:styles, :workbook, :shared_strings, :base_date] => :@shared + delegate [:workbook] => :@shared def initialize(path, relationships, shared, options = {}) super(path) @@ -19,7 +21,12 @@ module Roo end def hyperlinks(relationships) - @hyperlinks ||= extract_hyperlinks(relationships) + # If you're sure you're not going to need this hyperlinks you can discard it + @hyperlinks ||= if @options[:no_hyperlinks] + {} + else + extract_hyperlinks(relationships) + end end # Get the dimensions for the sheet. @@ -39,13 +46,10 @@ module Roo def each_cell(row_xml) return [] unless row_xml row_xml.children.each do |cell_element| - # If you're sure you're not going to need this hyperlinks you can discard it - hyperlinks = unless @options[:no_hyperlinks] - key = ::Roo::Utils.ref_to_key(cell_element['r']) - hyperlinks(@relationships)[key] - end + coordinate = ::Roo::Utils.extract_coordinate(cell_element["r"]) + hyperlinks = hyperlinks(@relationships)[coordinate] - yield cell_from_xml(cell_element, hyperlinks) + yield cell_from_xml(cell_element, hyperlinks, coordinate) end end @@ -53,13 +57,13 @@ module Roo def cell_value_type(type, format) case type - when 's'.freeze + when 's' :shared - when 'b'.freeze + when 'b' :boolean - when 'str'.freeze + when 'str' :string - when 'inlineStr'.freeze + when 'inlineStr' :inlinestr else Excelx::Format.to_type(format) @@ -74,42 +78,58 @@ module Roo # </c> # hyperlink - a String for the hyperlink for the cell or nil when no # hyperlink is present. + # coordinate - a Roo::Excelx::Coordinate for the coordinate for the cell + # or nil to extract coordinate from cell_xml. + # empty_cell - an Optional Boolean value. # # Examples # - # cells_from_xml(<Nokogiri::XML::Element>, nil) + # cells_from_xml(<Nokogiri::XML::Element>, nil, nil) # # => <Excelx::Cell::String> # # Returns a type of <Excelx::Cell>. - def cell_from_xml(cell_xml, hyperlink) - coordinate = extract_coordinate(cell_xml['r']) - return Excelx::Cell::Empty.new(coordinate) if cell_xml.children.empty? + def cell_from_xml(cell_xml, hyperlink, coordinate, empty_cell=true) + coordinate ||= ::Roo::Utils.extract_coordinate(cell_xml["r"]) + cell_xml_children = cell_xml.children + return create_empty_cell(coordinate, empty_cell) if cell_xml_children.empty? # NOTE: This is error prone, to_i will silently turn a nil into a 0. # This works by coincidence because Format[0] is General. - style = cell_xml['s'].to_i - format = styles.style_format(style) - value_type = cell_value_type(cell_xml['t'], format) + style = cell_xml["s"].to_i formula = nil - cell_xml.children.each do |cell| + cell_xml_children.each do |cell| case cell.name when 'is' - content_arr = cell.search('t').map(&:content) - unless content_arr.empty? - return Excelx::Cell.create_cell(:string, content_arr.join(''), formula, style, hyperlink, coordinate) + content = +"" + cell.children.each do |inline_str| + if inline_str.name == 't' + content << inline_str.content + end + end + unless content.empty? + return Excelx::Cell.cell_class(:string).new(content, formula, style, hyperlink, coordinate) end when 'f' formula = cell.content when 'v' - return create_cell_from_value(value_type, cell, formula, format, style, hyperlink, base_date, coordinate) + format = style_format(style) + value_type = cell_value_type(cell_xml["t"], format) + + return create_cell_from_value(value_type, cell, formula, format, style, hyperlink, coordinate) end end - Excelx::Cell::Empty.new(coordinate) + create_empty_cell(coordinate) end - def create_cell_from_value(value_type, cell, formula, format, style, hyperlink, base_date, coordinate) + def create_empty_cell(coordinate, empty_cell) + if empty_cell + Excelx::Cell::Empty.new(coordinate) + end + end + + def create_cell_from_value(value_type, cell, formula, format, style, hyperlink, coordinate) # NOTE: format.to_s can replace excelx_type as an argument for # Cell::Time, Cell::DateTime, Cell::Date or Cell::Number, but # it will break some brittle tests. @@ -125,11 +145,12 @@ module Roo # 3. formula case value_type when :shared - value = shared_strings.use_html?(cell.content.to_i) ? shared_strings.to_html[cell.content.to_i] : shared_strings[cell.content.to_i] - Excelx::Cell.create_cell(:string, value, formula, style, hyperlink, coordinate) + cell_content = cell.content.to_i + value = shared_strings.use_html?(cell_content) ? shared_strings.to_html[cell_content] : shared_strings[cell_content] + Excelx::Cell.cell_class(:string).new(value, formula, style, hyperlink, coordinate) when :boolean, :string value = cell.content - Excelx::Cell.create_cell(value_type, value, formula, style, hyperlink, coordinate) + Excelx::Cell.cell_class(value_type).new(value, formula, style, hyperlink, coordinate) when :time, :datetime cell_content = cell.content.to_f # NOTE: A date will be a whole number. A time will have be > 1. And @@ -148,35 +169,32 @@ module Roo else :date end - Excelx::Cell.create_cell(cell_type, cell.content, formula, excelx_type, style, hyperlink, base_date, coordinate) + base_value = cell_type == :date ? base_date : base_timestamp + Excelx::Cell.cell_class(cell_type).new(cell_content, formula, excelx_type, style, hyperlink, base_value, coordinate) when :date - Excelx::Cell.create_cell(value_type, cell.content, formula, excelx_type, style, hyperlink, base_date, coordinate) + Excelx::Cell.cell_class(:date).new(cell.content, formula, excelx_type, style, hyperlink, base_date, coordinate) else - Excelx::Cell.create_cell(:number, cell.content, formula, excelx_type, style, hyperlink, coordinate) + Excelx::Cell.cell_class(:number).new(cell.content, formula, excelx_type, style, hyperlink, coordinate) end end - def extract_coordinate(coordinate) - row, column = ::Roo::Utils.split_coordinate(coordinate) - - Excelx::Coordinate.new(row, column) - end - def extract_hyperlinks(relationships) return {} unless (hyperlinks = doc.xpath('/worksheet/hyperlinks/hyperlink')) - Hash[hyperlinks.map do |hyperlink| - if hyperlink.attribute('id') && (relationship = relationships[hyperlink.attribute('id').text]) - [::Roo::Utils.ref_to_key(hyperlink.attributes['ref'].to_s), relationship.attribute('Target').text] + hyperlinks.each_with_object({}) do |hyperlink, hash| + if relationship = relationships[hyperlink['id']] + target_link = relationship['Target'] + target_link += "##{hyperlink['location']}" if hyperlink['location'] + hash[::Roo::Utils.ref_to_key(hyperlink["ref"].to_s)] = target_link end - end.compact] + end end def expand_merged_ranges(cells) # Extract merged ranges from xml merges = {} doc.xpath('/worksheet/mergeCells/mergeCell').each do |mergecell_xml| - tl, br = mergecell_xml['ref'].split(/:/).map { |ref| ::Roo::Utils.ref_to_key(ref) } + tl, br = mergecell_xml["ref"].split(/:/).map { |ref| ::Roo::Utils.ref_to_key(ref) } for row in tl[0]..br[0] do for col in tl[1]..br[1] do next if row == tl[0] && col == tl[1] @@ -191,10 +209,14 @@ module Roo end def extract_cells(relationships) - extracted_cells = Hash[doc.xpath('/worksheet/sheetData/row/c').map do |cell_xml| - key = ::Roo::Utils.ref_to_key(cell_xml['r']) - [key, cell_from_xml(cell_xml, hyperlinks(relationships)[key])] - end] + extracted_cells = {} + empty_cell = @options[:empty_cell] + + doc.xpath('/worksheet/sheetData/row/c').each do |cell_xml| + coordinate = ::Roo::Utils.extract_coordinate(cell_xml["r"]) + cell = cell_from_xml(cell_xml, hyperlinks(relationships)[coordinate], coordinate, empty_cell) + extracted_cells[coordinate] = cell if cell + end expand_merged_ranges(extracted_cells) if @options[:expand_merged_ranges] @@ -203,9 +225,25 @@ module Roo def extract_dimensions Roo::Utils.each_element(@path, 'dimension') do |dimension| - return dimension.attributes['ref'].value + return dimension["ref"] end end + + def style_format(style) + @shared.styles.style_format(style) + end + + def base_date + @shared.base_date + end + + def base_timestamp + @shared.base_timestamp + end + + def shared_strings + @shared.shared_strings + end end end end diff --git a/lib/roo/excelx/styles.rb b/lib/roo/excelx/styles.rb index 87f1713..25f0bf0 100644 --- a/lib/roo/excelx/styles.rb +++ b/lib/roo/excelx/styles.rb @@ -55,9 +55,9 @@ module Roo end def extract_num_fmts - Hash[doc.xpath('//numFmt').map do |num_fmt| - [num_fmt['numFmtId'], num_fmt['formatCode']] - end] + doc.xpath('//numFmt').each_with_object({}) do |num_fmt, hash| + hash[num_fmt['numFmtId']] = num_fmt['formatCode'] + end end end end diff --git a/lib/roo/excelx/workbook.rb b/lib/roo/excelx/workbook.rb index 7ef841f..c21bb1f 100644 --- a/lib/roo/excelx/workbook.rb +++ b/lib/roo/excelx/workbook.rb @@ -29,13 +29,17 @@ module Roo # aka labels def defined_names - Hash[doc.xpath('//definedName').map do |defined_name| + doc.xpath('//definedName').each_with_object({}) do |defined_name, hash| # "Sheet1!$C$5" sheet, coordinates = defined_name.text.split('!$', 2) col, row = coordinates.split('$') name = defined_name['name'] - [name, Label.new(name, sheet, row, col)] - end] + hash[name] = Label.new(name, sheet, row, col) + end + end + + def base_timestamp + @base_timestamp ||= base_date.to_datetime.to_time.to_i end def base_date diff --git a/lib/roo/helpers/default_attr_reader.rb b/lib/roo/helpers/default_attr_reader.rb new file mode 100644 index 0000000..a02ba84 --- /dev/null +++ b/lib/roo/helpers/default_attr_reader.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Roo + module Helpers + module DefaultAttrReader + def attr_reader_with_default(attr_hash) + attr_hash.each do |attr_name, default_value| + instance_variable = :"@#{attr_name}" + define_method attr_name do + if instance_variable_defined? instance_variable + instance_variable_get instance_variable + else + default_value + end + end + end + end + end + end +end diff --git a/lib/roo/helpers/weak_instance_cache.rb b/lib/roo/helpers/weak_instance_cache.rb new file mode 100644 index 0000000..db26de1 --- /dev/null +++ b/lib/roo/helpers/weak_instance_cache.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require "weakref" + +module Roo + module Helpers + module WeakInstanceCache + private + + def instance_cache(key) + object = nil + + if instance_variable_defined?(key) && (ref = instance_variable_get(key)) && ref.weakref_alive? + begin + object = ref.__getobj__ + rescue => e + unless (defined?(::WeakRef::RefError) && e.is_a?(::WeakRef::RefError)) || (defined?(RefError) && e.is_a?(RefError)) + raise e + end + end + end + + unless object + object = yield + ObjectSpace.define_finalizer(object, instance_cache_finalizer(key)) + instance_variable_set(key, WeakRef.new(object)) + end + + object + end + + def instance_cache_finalizer(key) + proc do |object_id| + if instance_variable_defined?(key) && (ref = instance_variable_get(key)) && (!ref.weakref_alive? || ref.__getobj__.object_id == object_id) + remove_instance_variable(key) + end + end + end + end + end +end diff --git a/lib/roo/open_office.rb b/lib/roo/open_office.rb index 6ccbe85..f172363 100644 --- a/lib/roo/open_office.rb +++ b/lib/roo/open_office.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'date' require 'nokogiri' require 'cgi' @@ -11,9 +13,9 @@ module Roo class OpenOffice < Roo::Base extend Roo::Tempdir - ERROR_MISSING_CONTENT_XML = 'file missing required content.xml'.freeze - XPATH_FIND_TABLE_STYLES = "//*[local-name()='automatic-styles']".freeze - XPATH_LOCAL_NAME_TABLE = "//*[local-name()='table']".freeze + ERROR_MISSING_CONTENT_XML = 'file missing required content.xml' + XPATH_FIND_TABLE_STYLES = "//*[local-name()='automatic-styles']" + XPATH_LOCAL_NAME_TABLE = "//*[local-name()='table']" # initialization and opening of a spreadsheet file # values for packed: :zip @@ -561,7 +563,7 @@ module Roo end def read_labels - @label ||= Hash[doc.xpath('//table:named-range').map do |ne| + @label ||= doc.xpath('//table:named-range').each_with_object({}) do |ne, hash| #- # $Sheet1.$C$5 #+ @@ -569,8 +571,8 @@ module Roo sheetname, coords = attribute(ne, 'cell-range-address').to_s.split('.$') col, row = coords.split('$') sheetname = sheetname[1..-1] if sheetname[0, 1] == '$' - [name, [sheetname, row, col]] - end] + hash[name] = [sheetname, row, col] + end end def read_styles(style_elements) diff --git a/lib/roo/spreadsheet.rb b/lib/roo/spreadsheet.rb index 1eef58d..cdc93f0 100644 --- a/lib/roo/spreadsheet.rb +++ b/lib/roo/spreadsheet.rb @@ -24,7 +24,7 @@ module Roo options[:file_warning] = :ignore extension.tr('.', '').downcase.to_sym else - res = ::File.extname((path =~ /\A#{::URI.regexp}\z/) ? ::URI.parse(::URI.encode(path)).path : path) + res = ::File.extname((path =~ /\A#{::URI::DEFAULT_PARSER.make_regexp}\z/) ? ::URI.parse(::URI.encode(path)).path : path) res.tr('.', '').downcase.to_sym end end diff --git a/lib/roo/utils.rb b/lib/roo/utils.rb index 17b672d..dcaedde 100644 --- a/lib/roo/utils.rb +++ b/lib/roo/utils.rb @@ -4,27 +4,39 @@ module Roo LETTERS = ('A'..'Z').to_a - def split_coordinate(str) - @split_coordinate ||= {} + def extract_coordinate(s) + num = letter_num = 0 + num_only = false - @split_coordinate[str] ||= begin - letter, number = split_coord(str) - x = letter_to_number(letter) - y = number - [y, x] + s.each_byte do |b| + if !num_only && (index = char_index(b)) + letter_num *= 26 + letter_num += index + elsif index = num_index(b) + num_only = true + num *= 10 + num += index + else + fail ArgumentError + end end + fail ArgumentError if letter_num == 0 || !num_only + + Excelx::Coordinate.new(num, letter_num) end - alias_method :ref_to_key, :split_coordinate + alias_method :ref_to_key, :extract_coordinate - def split_coord(s) - if s =~ /([a-zA-Z]+)([0-9]+)/ - letter = Regexp.last_match[1] - number = Regexp.last_match[2].to_i - else - fail ArgumentError - end - [letter, number] + def split_coordinate(str) + warn "[DEPRECATION] `Roo::Utils.split_coordinate` is deprecated. Please use `Roo::Utils.extract_coordinate` instead." + extract_coordinate(str) + end + + + + def split_coord(str) + coord = extract_coordinate(str) + [number_to_letter(coord.column), coord.row] end # convert a number to something like 'AB' (1 => 'A', 2 => 'B', ...) @@ -56,8 +68,8 @@ module Roo cells = str.split(':') return 1 if cells.count == 1 raise ArgumentError.new("invalid range string: #{str}. Supported range format 'A1:B2'") if cells.count != 2 - x1, y1 = split_coordinate(cells[0]) - x2, y2 = split_coordinate(cells[1]) + x1, y1 = extract_coordinate(cells[0]) + x2, y2 = extract_coordinate(cells[1]) (x2 - (x1 - 1)) * (y2 - (y1 - 1)) end @@ -69,10 +81,27 @@ module Roo # Yield each element of a given type ('row', 'c', etc.) to caller def each_element(path, elements) + elements = Array(elements) Nokogiri::XML::Reader(::File.open(path, 'rb'), nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).each do |node| - next unless node.node_type == Nokogiri::XML::Reader::TYPE_ELEMENT && Array(elements).include?(node.name) + next unless node.node_type == Nokogiri::XML::Reader::TYPE_ELEMENT && elements.include?(node.name) yield Nokogiri::XML(node.outer_xml).root if block_given? end end + + private + + def char_index(byte) + if byte >= 65 && byte <= 90 + byte - 64 + elsif byte >= 97 && byte <= 122 + byte - 96 + end + end + + def num_index(byte) + if byte >= 48 && byte <= 57 + byte - 48 + end + end end end diff --git a/lib/roo/version.rb b/lib/roo/version.rb index bdd16dc..7fd96d7 100644 --- a/lib/roo/version.rb +++ b/lib/roo/version.rb @@ -1,3 +1,3 @@ module Roo - VERSION = "2.7.1" + VERSION = "2.8.0" end diff --git a/roo.gemspec b/roo.gemspec index a34accb..c674025 100644 --- a/roo.gemspec +++ b/roo.gemspec @@ -4,21 +4,23 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'roo/version' Gem::Specification.new do |spec| - spec.name = 'roo' - spec.version = Roo::VERSION - spec.authors = ['Thomas Preymesser', 'Hugh McGowan', 'Ben Woosley', 'Oleksandr Simonov', 'Steven Daniels'] - spec.email = ['ruby.ruby.ruby.roo@gmail.com', 'oleksandr@simonov.me'] - spec.summary = 'Roo can access the contents of various spreadsheet files.' - spec.description = "Roo can access the contents of various spreadsheet files. It can handle\n* OpenOffice\n* Excelx\n* LibreOffice\n* CSV" - spec.homepage = 'http://github.com/roo-rb/roo' - spec.license = 'MIT' + spec.name = 'roo' + spec.version = Roo::VERSION + spec.authors = ['Thomas Preymesser', 'Hugh McGowan', 'Ben Woosley', 'Oleksandr Simonov', 'Steven Daniels', 'Anmol Chopra'] + spec.email = ['ruby.ruby.ruby.roo@gmail.com', 'oleksandr@simonov.me'] + spec.summary = 'Roo can access the contents of various spreadsheet files.' + spec.description = "Roo can access the contents of various spreadsheet files. It can handle\n* OpenOffice\n* Excelx\n* LibreOffice\n* CSV" + spec.homepage = 'https://github.com/roo-rb/roo' + spec.license = 'MIT' - spec.files = `git ls-files -z`.split("\x0") + spec.files = `git ls-files -z`.split("\x0") spec.files.reject! { |fn| fn.include?('test/files') } - spec.require_paths = ['lib'] + spec.require_paths = ['lib'] + + spec.required_ruby_version = ">= 2.3.0" spec.add_dependency 'nokogiri', '~> 1' - spec.add_dependency 'rubyzip', '~> 1.1', '< 2.0.0' + spec.add_dependency 'rubyzip', '>= 1.2.1', '< 2.0.0' spec.add_development_dependency 'rake', '~> 10.1' spec.add_development_dependency 'minitest', '~> 5.4', '>= 5.4.3' diff --git a/spec/lib/roo/base_spec.rb b/spec/lib/roo/base_spec.rb index d00025d..76cefcc 100644 --- a/spec/lib/roo/base_spec.rb +++ b/spec/lib/roo/base_spec.rb @@ -127,10 +127,22 @@ describe Roo::Base do end end - describe '#row' do - it 'should return the specified row' do + describe "#row" do + it "should return the specified row" do expect(spreadsheet.row(12)).to eq([41.0, 42.0, 43.0, 44.0, 45.0, nil, nil]) - expect(spreadsheet.row(16)).to eq([nil, '"Hello world!"', 'forty-three', 'forty-four', 'forty-five', nil, nil]) + expect(spreadsheet.row(16)).to eq([nil, '"Hello world!"', "forty-three", "forty-four", "forty-five", nil, nil]) + end + + it "should return the specified row if default_sheet is set by a string" do + spreadsheet.default_sheet = "my_sheet" + expect(spreadsheet.row(12)).to eq([41.0, 42.0, 43.0, 44.0, 45.0, nil, nil]) + expect(spreadsheet.row(16)).to eq([nil, '"Hello world!"', "forty-three", "forty-four", "forty-five", nil, nil]) + end + + it "should return the specified row if default_sheet is set by an integer" do + spreadsheet.default_sheet = 0 + expect(spreadsheet.row(12)).to eq([41.0, 42.0, 43.0, 44.0, 45.0, nil, nil]) + expect(spreadsheet.row(16)).to eq([nil, '"Hello world!"', "forty-three", "forty-four", "forty-five", nil, nil]) end end @@ -146,6 +158,11 @@ describe Roo::Base do expect { spreadsheet.row_with([/Missing Header/]) }.to \ raise_error(Roo::HeaderRowNotFoundError) end + + it 'returns missing headers' do + expect { spreadsheet.row_with([/Header/, /Missing Header 1/, /Missing Header 2/]) }.to \ + raise_error(Roo::HeaderRowNotFoundError, '[/Missing Header 1/, /Missing Header 2/]') + end end end @@ -173,6 +190,31 @@ describe Roo::Base do end end + describe "#default_sheet=" do + it "should correctly set the default sheet if passed a string" do + spreadsheet.default_sheet = "my_sheet" + expect(spreadsheet.default_sheet).to eq("my_sheet") + end + + it "should correctly set the default sheet if passed an integer" do + spreadsheet.default_sheet = 0 + expect(spreadsheet.default_sheet).to eq("my_sheet") + end + + it "should correctly set the default sheet if passed an integer for the second sheet" do + spreadsheet.default_sheet = 1 + expect(spreadsheet.default_sheet).to eq("blank sheet") + end + + it "should raise an error if passed a sheet that does not exist as an integer" do + expect { spreadsheet.default_sheet = 10 }.to raise_error RangeError + end + + it "should raise an error if passed a sheet that does not exist as a string" do + expect { spreadsheet.default_sheet = "does_not_exist" }.to raise_error RangeError + end + end + describe '#to_yaml' do it 'should convert the spreadsheet to yaml' do expect(spreadsheet.to_yaml({}, 5, 1, 5, 1)).to eq("--- \n" + yaml_entry(5, 1, 'date', '1961-11-21')) diff --git a/spec/lib/roo/excelx_spec.rb b/spec/lib/roo/excelx_spec.rb index 0ef0f19..6c2289f 100755 --- a/spec/lib/roo/excelx_spec.rb +++ b/spec/lib/roo/excelx_spec.rb @@ -151,6 +151,22 @@ describe Roo::Excelx do it 'returns the expected result' do expect(subject.sheet_for("Tabelle1").instance_variable_get("@name")).to eq "Tabelle1" end + + it 'returns the expected result when passed a number' do + expect(subject.sheet_for(0).instance_variable_get("@name")).to eq "Tabelle1" + end + + it 'returns the expected result when passed a number that is not the first sheet' do + expect(subject.sheet_for(1).instance_variable_get("@name")).to eq "Name of Sheet 2" + end + + it "should raise an error if passed a sheet that does not exist as an integer" do + expect { subject.sheet_for(10) }.to raise_error RangeError + end + + it "should raise an error if passed a sheet that does not exist as a string" do + expect { subject.sheet_for("does_not_exist") }.to raise_error RangeError + end end describe '#row' do @@ -304,6 +320,18 @@ describe Roo::Excelx do end end + describe '#row' do + context 'integers with leading zero' + let(:path) { 'test/files/number_with_zero_prefix.xlsx' } + + it 'returns base 10 integer' do + (1..50).each do |row_index| + range_start = (row_index - 1) * 20 + 1 + expect(subject.row(row_index)).to eq (range_start..(range_start+19)).to_a + end + end + end + describe '#excelx_format' do let(:path) { 'test/files/style.xlsx' } @@ -354,11 +382,22 @@ describe Roo::Excelx do end describe '#hyperlink' do - let(:path) { 'test/files/link.xlsx' } + context 'without location' do + let(:path) { 'test/files/link.xlsx' } - it 'returns the expected result' do - expect(subject.hyperlink(1, 1)).to eq "http://www.google.com" - expect(subject.hyperlink(1, 2)).to eq nil + it 'returns the expected result' do + expect(subject.hyperlink(1, 1)).to eq "http://www.google.com" + expect(subject.hyperlink(1, 2)).to eq nil + end + end + + context 'with location' do + let(:path) { 'test/files/link_with_location.xlsx' } + + it 'returns the expected result' do + expect(subject.hyperlink(1, 1)).to eq "http://www.google.com/#hey" + expect(subject.hyperlink(1, 2)).to eq nil + end end end @@ -480,34 +519,36 @@ describe Roo::Excelx do end describe '#html_strings' do - let(:path) { 'test/files/html_strings_formatting.xlsx' } + describe "HTML Parsing Enabling" do + let(:path) { 'test/files/html_strings_formatting.xlsx' } - it 'returns the expected result' do - expect(subject.excelx_value(1, 1, "Sheet1")).to eq "This has no formatting." - expect(subject.excelx_value(2, 1, "Sheet1")).to eq "<html>This has<b> bold </b>formatting.</html>" - expect(subject.excelx_value(2, 2, "Sheet1")).to eq "<html>This has <i>italics</i> formatting.</html>" - expect(subject.excelx_value(2, 3, "Sheet1")).to eq "<html>This has <u>underline</u> format.</html>" - expect(subject.excelx_value(2, 4, "Sheet1")).to eq "<html>Superscript. x<sup>123</sup></html>" - expect(subject.excelx_value(2, 5, "Sheet1")).to eq "<html>SubScript. T<sub>j</sub></html>" - - expect(subject.excelx_value(3, 1, "Sheet1")).to eq "<html>Bold, italics <b><i>together</i></b>.</html>" - expect(subject.excelx_value(3, 2, "Sheet1")).to eq "<html>Bold, Underline <b><u>together</u></b>.</html>" - expect(subject.excelx_value(3, 3, "Sheet1")).to eq "<html>Bold, Superscript. <b>x</b><sup><b>N</b></sup></html>" - expect(subject.excelx_value(3, 4, "Sheet1")).to eq "<html>Bold, Subscript. <b>T</b><sub><b>abc</b></sub></html>" - expect(subject.excelx_value(3, 5, "Sheet1")).to eq "<html>Italics, Underline <i><u>together</u></i>.</html>" - expect(subject.excelx_value(3, 6, "Sheet1")).to eq "<html>Italics, Superscript. <i>X</i><sup><i>abc</i></sup></html>" - expect(subject.excelx_value(3, 7, "Sheet1")).to eq "<html>Italics, Subscript. <i>B</i><sub><i>efg</i></sub></html>" - expect(subject.excelx_value(4, 1, "Sheet1")).to eq "<html>Bold, italics underline,<b><i><u> together</u></i></b>.</html>" - expect(subject.excelx_value(4, 2, "Sheet1")).to eq "<html>Bold, italics, superscript. <b>X</b><sup><b><i>abc</i></b></sup><b><i>123</i></b></html>" - expect(subject.excelx_value(4, 3, "Sheet1")).to eq "<html>Bold, Italics, subscript. <b><i>Mg</i></b><sub><b><i>ha</i></b></sub><b><i>2</i></b></html>" - expect(subject.excelx_value(4, 4, "Sheet1")).to eq "<html>Bold, Underline, superscript. <b><u>AB</u></b><sup><b><u>C12</u></b></sup><b><u>3</u></b></html>" - expect(subject.excelx_value(4, 5, "Sheet1")).to eq "<html>Bold, Underline, subscript. <b><u>Good</u></b><sub><b><u>XYZ</u></b></sub></html>" - expect(subject.excelx_value(4, 6, "Sheet1")).to eq "<html>Italics, Underline, superscript. <i><u>Up</u></i><sup><i><u>swing</u></i></sup></html>" - expect(subject.excelx_value(4, 7, "Sheet1")).to eq "<html>Italics, Underline, subscript. <i><u>T</u></i><sub><i><u>swing</u></i></sub></html>" - expect(subject.excelx_value(5, 1, "Sheet1")).to eq "<html>Bold, italics, underline, superscript. <b><i><u>GHJK</u></i></b><sup><b><i><u>190</u></i></b></sup><b><i><u>4</u></i></b></html>" - expect(subject.excelx_value(5, 2, "Sheet1")).to eq "<html>Bold, italics, underline, subscript. <b><i><u>Mike</u></i></b><sub><b><i><u>drop</u></i></b></sub></html>" - expect(subject.excelx_value(6, 1, "Sheet1")).to eq "See that regular html tags do not create html tags.\n<ol>\n <li> Denver Broncos </li>\n <li> Carolina Panthers </li>\n <li> New England Patriots</li>\n <li>Arizona Panthers</li>\n</ol>" - expect(subject.excelx_value(7, 1, "Sheet1")).to eq "<html>Does create html tags when formatting is used..\n<ol>\n <li> <b>Denver Broncos</b> </li>\n <li> <i>Carolina Panthers </i></li>\n <li> <u>New England Patriots</u></li>\n <li>Arizona Panthers</li>\n</ol></html>" + it 'returns the expected result' do + expect(subject.excelx_value(1, 1, "Sheet1")).to eq("This has no formatting.") + expect(subject.excelx_value(2, 1, "Sheet1")).to eq("<html>This has<b> bold </b>formatting.</html>") + expect(subject.excelx_value(2, 2, "Sheet1")).to eq("<html>This has <i>italics</i> formatting.</html>") + expect(subject.excelx_value(2, 3, "Sheet1")).to eq("<html>This has <u>underline</u> format.</html>") + expect(subject.excelx_value(2, 4, "Sheet1")).to eq("<html>Superscript. x<sup>123</sup></html>") + expect(subject.excelx_value(2, 5, "Sheet1")).to eq("<html>SubScript. T<sub>j</sub></html>") + + expect(subject.excelx_value(3, 1, "Sheet1")).to eq("<html>Bold, italics <b><i>together</i></b>.</html>") + expect(subject.excelx_value(3, 2, "Sheet1")).to eq("<html>Bold, Underline <b><u>together</u></b>.</html>") + expect(subject.excelx_value(3, 3, "Sheet1")).to eq("<html>Bold, Superscript. <b>x</b><sup><b>N</b></sup></html>") + expect(subject.excelx_value(3, 4, "Sheet1")).to eq("<html>Bold, Subscript. <b>T</b><sub><b>abc</b></sub></html>") + expect(subject.excelx_value(3, 5, "Sheet1")).to eq("<html>Italics, Underline <i><u>together</u></i>.</html>") + expect(subject.excelx_value(3, 6, "Sheet1")).to eq("<html>Italics, Superscript. <i>X</i><sup><i>abc</i></sup></html>") + expect(subject.excelx_value(3, 7, "Sheet1")).to eq("<html>Italics, Subscript. <i>B</i><sub><i>efg</i></sub></html>") + expect(subject.excelx_value(4, 1, "Sheet1")).to eq("<html>Bold, italics underline,<b><i><u> together</u></i></b>.</html>") + expect(subject.excelx_value(4, 2, "Sheet1")).to eq("<html>Bold, italics, superscript. <b>X</b><sup><b><i>abc</i></b></sup><b><i>123</i></b></html>") + expect(subject.excelx_value(4, 3, "Sheet1")).to eq("<html>Bold, Italics, subscript. <b><i>Mg</i></b><sub><b><i>ha</i></b></sub><b><i>2</i></b></html>") + expect(subject.excelx_value(4, 4, "Sheet1")).to eq("<html>Bold, Underline, superscript. <b><u>AB</u></b><sup><b><u>C12</u></b></sup><b><u>3</u></b></html>") + expect(subject.excelx_value(4, 5, "Sheet1")).to eq("<html>Bold, Underline, subscript. <b><u>Good</u></b><sub><b><u>XYZ</u></b></sub></html>") + expect(subject.excelx_value(4, 6, "Sheet1")).to eq("<html>Italics, Underline, superscript. <i><u>Up</u></i><sup><i><u>swing</u></i></sup></html>") + expect(subject.excelx_value(4, 7, "Sheet1")).to eq("<html>Italics, Underline, subscript. <i><u>T</u></i><sub><i><u>swing</u></i></sub></html>") + expect(subject.excelx_value(5, 1, "Sheet1")).to eq("<html>Bold, italics, underline, superscript. <b><i><u>GHJK</u></i></b><sup><b><i><u>190</u></i></b></sup><b><i><u>4</u></i></b></html>") + expect(subject.excelx_value(5, 2, "Sheet1")).to eq("<html>Bold, italics, underline, subscript. <b><i><u>Mike</u></i></b><sub><b><i><u>drop</u></i></b></sub></html>") + expect(subject.excelx_value(6, 1, "Sheet1")).to eq("See that regular html tags do not create html tags.\n<ol>\n <li> Denver Broncos </li>\n <li> Carolina Panthers </li>\n <li> New England Patriots</li>\n <li>Arizona Panthers</li>\n</ol>") + expect(subject.excelx_value(7, 1, "Sheet1")).to eq("<html>Does create html tags when formatting is used..\n<ol>\n <li> <b>Denver Broncos</b> </li>\n <li> <i>Carolina Panthers </i></li>\n <li> <u>New England Patriots</u></li>\n <li>Arizona Panthers</li>\n</ol></html>") + end end end @@ -534,4 +575,57 @@ describe Roo::Excelx do expect(subject.sheet(0).excelx_format(2,1)).to eq 'm/d/yyyy" "h:mm:ss" "AM/PM' end end + + describe 'images' do + let(:path) { 'test/files/images.xlsx' } + + it 'returns array of images from default sheet' do + expect(subject.images).to be_kind_of(Array) + expect(subject.images.size).to eql(19) + end + + it 'returns empty array if there is no images on the sheet' do + expect(subject.images("Sheet2")).to eql([]) + end + end end + +describe 'Roo::Excelx with options set' do + subject(:xlsx) do + Roo::Excelx.new(path, disable_html_wrapper: true) + end + + describe '#html_strings' do + describe "HTML Parsing Disabled" do + let(:path) { 'test/files/html_strings_formatting.xlsx' } + + it 'returns the expected result' do + expect(subject.excelx_value(1, 1, "Sheet1")).to eq("This has no formatting.") + expect(subject.excelx_value(2, 1, "Sheet1")).to eq("This has bold formatting.") + expect(subject.excelx_value(2, 2, "Sheet1")).to eq("This has italics formatting.") + expect(subject.excelx_value(2, 3, "Sheet1")).to eq("This has underline format.") + expect(subject.excelx_value(2, 4, "Sheet1")).to eq("Superscript. x123") + expect(subject.excelx_value(2, 5, "Sheet1")).to eq("SubScript. Tj") + + expect(subject.excelx_value(3, 1, "Sheet1")).to eq("Bold, italics together.") + expect(subject.excelx_value(3, 2, "Sheet1")).to eq("Bold, Underline together.") + expect(subject.excelx_value(3, 3, "Sheet1")).to eq("Bold, Superscript. xN") + expect(subject.excelx_value(3, 4, "Sheet1")).to eq("Bold, Subscript. Tabc") + expect(subject.excelx_value(3, 5, "Sheet1")).to eq("Italics, Underline together.") + expect(subject.excelx_value(3, 6, "Sheet1")).to eq("Italics, Superscript. Xabc") + expect(subject.excelx_value(3, 7, "Sheet1")).to eq("Italics, Subscript. Befg") + expect(subject.excelx_value(4, 1, "Sheet1")).to eq("Bold, italics underline, together.") + expect(subject.excelx_value(4, 2, "Sheet1")).to eq("Bold, italics, superscript. Xabc123") + expect(subject.excelx_value(4, 3, "Sheet1")).to eq("Bold, Italics, subscript. Mgha2") + expect(subject.excelx_value(4, 4, "Sheet1")).to eq("Bold, Underline, superscript. ABC123") + expect(subject.excelx_value(4, 5, "Sheet1")).to eq("Bold, Underline, subscript. GoodXYZ") + expect(subject.excelx_value(4, 6, "Sheet1")).to eq("Italics, Underline, superscript. Upswing") + expect(subject.excelx_value(4, 7, "Sheet1")).to eq("Italics, Underline, subscript. Tswing") + expect(subject.excelx_value(5, 1, "Sheet1")).to eq("Bold, italics, underline, superscript. GHJK1904") + expect(subject.excelx_value(5, 2, "Sheet1")).to eq("Bold, italics, underline, subscript. Mikedrop") + expect(subject.excelx_value(6, 1, "Sheet1")).to eq("See that regular html tags do not create html tags.\n<ol>\n <li> Denver Broncos </li>\n <li> Carolina Panthers </li>\n <li> New England Patriots</li>\n <li>Arizona Panthers</li>\n</ol>") + expect(subject.excelx_value(7, 1, "Sheet1")).to eq("Does create html tags when formatting is used..\n<ol>\n <li> Denver Broncos </li>\n <li> Carolina Panthers </li>\n <li> New England Patriots</li>\n <li>Arizona Panthers</li>\n</ol>") + end + end + end +end
\ No newline at end of file diff --git a/spec/lib/roo/strict_spec.rb b/spec/lib/roo/strict_spec.rb new file mode 100644 index 0000000..811ee51 --- /dev/null +++ b/spec/lib/roo/strict_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe Roo::Excelx do + subject { Roo::Excelx.new('test/files/strict.xlsx') } + + example '#sheets' do + expect(subject.sheets).to eq %w(Sheet1 Sheet2) + end + + example '#sheet' do + expect(subject.sheet('Sheet1')).to be_a(Roo::Excelx) + end + + example '#cell' do + expect(subject.cell(1, 1)).to eq 'Sheet 1' + expect(subject.cell(1, 1, 'Sheet2')).to eq 'Sheet 2' + end + + example '#row' do + expect(subject.row(1)).to eq ['Sheet 1'] + expect(subject.row(1, 'Sheet2')).to eq ['Sheet 2'] + end + + example '#first_row' do + expect(subject.first_row).to eq 1 + expect(subject.first_row('Sheet2')).to eq 1 + end + + example '#last_row' do + expect(subject.last_row).to eq 1 + expect(subject.last_row('Sheet2')).to eq 1 + end + + example '#first_column' do + expect(subject.first_column).to eq 1 + expect(subject.first_column('Sheet2')).to eq 1 + end + + example '#last_column' do + expect(subject.last_column).to eq 1 + expect(subject.last_column('Sheet2')).to eq 1 + end +end diff --git a/spec/lib/roo/utils_spec.rb b/spec/lib/roo/utils_spec.rb index ffe93d4..8f322d4 100644 --- a/spec/lib/roo/utils_spec.rb +++ b/spec/lib/roo/utils_spec.rb @@ -52,6 +52,15 @@ RSpec.describe ::Roo::Utils do end end + context '.extract_coordinate' do + it "returns the expected result" do + expect(described_class.extract_coordinate('A1')).to eq [1, 1] + expect(described_class.extract_coordinate('B2')).to eq [2, 2] + expect(described_class.extract_coordinate('R2')).to eq [2, 18] + expect(described_class.extract_coordinate('AR31')).to eq [31, 18 + 26] + end + end + context '.split_coord' do it "returns the expected result" do expect(described_class.split_coord('A1')).to eq ["A", 1] @@ -86,21 +95,21 @@ RSpec.describe ::Roo::Utils do expect(described_class.load_xml('test/files/sheet1.xml')).to be_a(Nokogiri::XML::Document) expect(described_class.load_xml('test/files/sheet1.xml'). remove_namespaces!.xpath("/worksheet/dimension").map do |dim| - dim.attributes["ref"].value end.first).to eq "A1:B11" + dim["ref"] end.first).to eq "A1:B11" end end context '.each_element' do it 'returns the expected result' do described_class.each_element('test/files/sheet1.xml', 'dimension') do |dim| - expect(dim.attributes["ref"].value).to eq "A1:B11" + expect(dim["ref"]).to eq "A1:B11" end rows = [] described_class.each_element('test/files/sheet1.xml', 'row') do |row| rows << row end expect(rows.size).to eq 11 - expect(rows[2].attributes["r"].value).to eq "3" + expect(rows[2]["r"]).to eq "3" end end end diff --git a/spec/lib/roo/weak_instance_cache_spec.rb b/spec/lib/roo/weak_instance_cache_spec.rb new file mode 100644 index 0000000..c2ad9b4 --- /dev/null +++ b/spec/lib/roo/weak_instance_cache_spec.rb @@ -0,0 +1,92 @@ +require 'spec_helper' + +if RUBY_PLATFORM == "java" + require 'java' + java_import 'java.lang.System' +end + +describe Roo::Helpers::WeakInstanceCache do + let(:klass) do + Class.new do + include Roo::Helpers::WeakInstanceCache + + def memoized_data + instance_cache(:@memoized_data) do + "Some Costly Operation #{rand(1000)}" * 1_000 + end + end + end + end + + subject do + klass.new + end + + it 'should be lazy' do + expect(subject.instance_variables).to_not include(:@memoized_data) + data = subject.memoized_data + expect(subject.instance_variables).to include(:@memoized_data) + end + + + it 'should be memoized' do + data = subject.memoized_data + expect(subject.memoized_data).to equal(data) + end + + it 'should recalculate after GC' do + expect(subject.instance_variables).to_not include(:@memoized_data) + GC.disable + subject.memoized_data && nil + expect(subject.instance_variables).to include(:@memoized_data) + + force_gc + expect(subject.instance_variables).to_not include(:@memoized_data) + GC.disable + subject.memoized_data && nil + expect(subject.instance_variables).to include(:@memoized_data) + end + + it 'must remove instance variable' do + expect(subject.instance_variables).to_not include(:@memoized_data) + GC.disable + subject.memoized_data && nil + expect(subject.instance_variables).to include(:@memoized_data) + + force_gc + expect(subject.instance_variables).to_not include(:@memoized_data) + end + + context '#inspect must not raise' do + it 'before calculation' do + expect{subject.inspect}.to_not raise_error + end + it 'after calculation' do + GC.disable + subject.memoized_data && nil + expect{subject.inspect}.to_not raise_error + expect(subject.inspect).to include("Some Costly Operation") + force_gc + end + it 'after GC' do + subject.memoized_data && nil + force_gc + expect(subject.instance_variables).to_not include(:@memoized_data) + expect{subject.inspect}.to_not raise_error + expect(subject.inspect).to_not include("Some Costly Operation") + end + end + + if RUBY_PLATFORM == "java" + def force_gc + System.gc + sleep(0.1) + end + else + def force_gc + GC.start(full_mark: true, immediate_sweep: true) + sleep(0.1) + GC.start(full_mark: true, immediate_sweep: true) + end + end +end
\ No newline at end of file diff --git a/spec/lib/roo_spec.rb b/spec/lib/roo_spec.rb new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/spec/lib/roo_spec.rb diff --git a/test/excelx/cell/test_attr_reader_default.rb b/test/excelx/cell/test_attr_reader_default.rb new file mode 100644 index 0000000..c1ae277 --- /dev/null +++ b/test/excelx/cell/test_attr_reader_default.rb @@ -0,0 +1,72 @@ +require "test_helper" + +class TestAttrReaderDefault < Minitest::Test + def base + Roo::Excelx::Cell::Base + end + + def boolean + Roo::Excelx::Cell::Boolean + end + + def class_date + Roo::Excelx::Cell::Date + end + + def datetime + Roo::Excelx::Cell::DateTime + end + + def empty + Roo::Excelx::Cell::Empty + end + + def number + Roo::Excelx::Cell::Number + end + + def string + Roo::Excelx::Cell::String + end + + def base_date + ::Date.new(1899, 12, 30) + end + + def base_timestamp + ::Date.new(1899, 12, 30).to_datetime.to_time.to_i + end + + def class_time + Roo::Excelx::Cell::Time + end + + def test_cell_default_values + assert_values base.new(nil, nil, [], 1, nil, nil), default_type: :base, :@default_type => nil, style: 1, :@style => nil + assert_values boolean.new("1", nil, nil, nil, nil), default_type: :boolean, :@default_type => nil, cell_type: :boolean, :@cell_type => nil + assert_values class_date.new("41791", nil, [:numeric_or_formula, "mm-dd-yy"], 6, nil, base_date, nil), default_type: :date, :@default_type => nil + assert_values class_time.new("0.521", nil, [:numeric_or_formula, "hh:mm"], 6, nil, base_timestamp, nil), default_type: :time, :@default_type => nil + assert_values datetime.new("41791.521", nil, [:numeric_or_formula, "mm-dd-yy hh:mm"], 6, nil, base_timestamp, nil), default_type: :datetime, :@default_type => nil + assert_values empty.new(nil), default_type: nil, :@default_type => nil, style: nil, :@style => nil + assert_values number.new("42", nil, ["0"], nil, nil, nil), default_type: :float, :@default_type => nil + assert_values string.new("1", nil, nil, nil, nil), default_type: :string, :@default_type => nil, cell_type: :string, :@cell_type => nil + + assert_values base.new(nil, nil, [], 2, nil, nil), style: 2, :@style => 2 + end + + def assert_values(object, value_hash) + value_hash.each do |attr_name, expected_value| + value = if attr_name.to_s.include?("@") + object.instance_variable_defined?(attr_name) ? object.instance_variable_get(attr_name) : nil + else + object.public_send(attr_name) + end + + if expected_value + assert_equal expected_value, value + else + assert_nil value + end + end + end +end diff --git a/test/excelx/cell/test_base.rb b/test/excelx/cell/test_base.rb index 17c83be..e3d0c7c 100644 --- a/test/excelx/cell/test_base.rb +++ b/test/excelx/cell/test_base.rb @@ -25,6 +25,11 @@ class TestRooExcelxCellBase < Minitest::Test refute cell.empty? end + def test_presence + cell = base.new(value, nil, [], nil, nil, nil) + assert_equal cell, cell.presence + end + def test_cell_type_is_formula formula = true cell = base.new(value, formula, [], nil, nil, nil) diff --git a/test/excelx/cell/test_datetime.rb b/test/excelx/cell/test_datetime.rb index 425830b..4ab1e18 100644 --- a/test/excelx/cell/test_datetime.rb +++ b/test/excelx/cell/test_datetime.rb @@ -2,12 +2,12 @@ require 'test_helper' class TestRooExcelxCellDateTime < Minitest::Test def test_cell_value_is_datetime - cell = datetime.new('30000.323212', nil, ['mm-dd-yy'], nil, nil, base_date, nil) + cell = datetime.new('30000.323212', nil, ['mm-dd-yy'], nil, nil, base_timestamp, nil) assert_kind_of ::DateTime, cell.value end def test_cell_type_is_datetime - cell = datetime.new('30000.323212', nil, [], nil, nil, base_date, nil) + cell = datetime.new('30000.323212', nil, [], nil, nil, base_timestamp, nil) assert_equal :datetime, cell.type end @@ -19,7 +19,7 @@ class TestRooExcelxCellDateTime < Minitest::Test ['mmm-yy', 'JAN-15'], ['m/d/yy h:mm', '1/25/15 8:15'] ].each do |format, formatted_value| - cell = datetime.new '42029.34375', nil, [format], nil, nil, base_date, nil + cell = datetime.new '42029.34375', nil, [format], nil, nil, base_timestamp, nil assert_equal formatted_value, cell.formatted_value end end @@ -30,7 +30,7 @@ class TestRooExcelxCellDateTime < Minitest::Test ['h:mm:ss000 mm/yy', '8:15:00000 01/15'], ['mmm yyy', '2015-01-25 08:15:00'] ].each do |format, formatted_value| - cell = datetime.new '42029.34375', nil, [format], nil, nil, base_date, nil + cell = datetime.new '42029.34375', nil, [format], nil, nil, base_timestamp, nil assert_equal formatted_value, cell.formatted_value end end @@ -39,7 +39,7 @@ class TestRooExcelxCellDateTime < Minitest::Test Roo::Excelx::Cell::DateTime end - def base_date - Date.new(1899, 12, 30) + def base_timestamp + DateTime.new(1899, 12, 30).to_time.to_i end end diff --git a/test/excelx/cell/test_empty.rb b/test/excelx/cell/test_empty.rb index 8263714..e502a1d 100644 --- a/test/excelx/cell/test_empty.rb +++ b/test/excelx/cell/test_empty.rb @@ -4,4 +4,15 @@ class TestRooExcelxCellEmpty < Minitest::Test def empty Roo::Excelx::Cell::Empty end + + def test_empty? + cell = empty.new(nil) + assert_same true, cell.empty? + end + + def test_nil_presence + cell = empty.new(nil) + assert_nil cell.presence + end + end diff --git a/test/excelx/cell/test_number.rb b/test/excelx/cell/test_number.rb index 45db819..5c8d726 100644 --- a/test/excelx/cell/test_number.rb +++ b/test/excelx/cell/test_number.rb @@ -25,6 +25,11 @@ class TestRooExcelxCellNumber < Minitest::Test assert_kind_of(Float, cell.value) end + def test_very_simple_scientific_notation + cell = Roo::Excelx::Cell::Number.new '1e6', nil, ['0'], nil, nil, nil + assert_kind_of(Float, cell.value) + end + def test_percent cell = Roo::Excelx::Cell::Number.new '42.1', nil, ['0.00%'], nil, nil, nil assert_kind_of(Float, cell.value) @@ -53,8 +58,12 @@ class TestRooExcelxCellNumber < Minitest::Test def test_formats [ ['General', '1042'], + ['GENERAL', '1042'], ['0', '1042'], + ['000000', '001042'], ['0.00', '1042.00'], + ['0.0000', '1042.0000'], + ['0.000000000', '1042.000000000'], ['#,##0', '1,042'], ['#,##0.00', '1,042.00'], ['0%', '104200%'], diff --git a/test/excelx/cell/test_string.rb b/test/excelx/cell/test_string.rb index f1c848f..b7ecaa7 100644 --- a/test/excelx/cell/test_string.rb +++ b/test/excelx/cell/test_string.rb @@ -25,4 +25,24 @@ class TestRooExcelxCellString < Minitest::Test cell = string.new '0', nil, nil, nil, nil assert_equal '0', cell.value end + + def test_not_empty? + cell = string.new '1', nil, nil, nil, nil + assert_equal false, cell.empty? + end + + def test_empty? + cell = string.new '', nil, nil, nil, nil + assert_equal true, cell.empty? + end + + def test_presence + cell = string.new '1', nil, nil, nil, nil + assert_equal cell, cell.presence + end + + def test_nil_presence + cell = string.new '', nil, nil, nil, nil + assert_nil cell.presence + end end diff --git a/test/excelx/cell/test_time.rb b/test/excelx/cell/test_time.rb index 7619b3a..25d15f9 100644 --- a/test/excelx/cell/test_time.rb +++ b/test/excelx/cell/test_time.rb @@ -5,8 +5,8 @@ class TestRooExcelxCellTime < Minitest::Test Roo::Excelx::Cell::Time end - def base_date - Date.new(1899, 12, 30) + def base_timestamp + DateTime.new(1899, 12, 30).to_time.to_i end def test_formatted_value @@ -18,13 +18,13 @@ class TestRooExcelxCellTime < Minitest::Test ['[h]:mm:ss', '[1]:48:09'], ['mmss.0', '4809.0'] # Cell::Time always get rounded to the nearest second. ].each do |style_format, result| - cell = roo_time.new(value, nil, [:numeric_or_formula, style_format], 6, nil, base_date, nil) + cell = roo_time.new(value, nil, [:numeric_or_formula, style_format], 6, nil, base_timestamp, nil) assert_equal result, cell.formatted_value, "Style=#{style_format} is not properly formatted" end end def test_value - cell = roo_time.new('0.0751', nil, [:numeric_or_formula, 'h:mm'], 6, nil, base_date, nil) + cell = roo_time.new('0.0751', nil, [:numeric_or_formula, 'h:mm'], 6, nil, base_timestamp, nil) assert_kind_of Integer, cell.value end end diff --git a/test/excelx/test_coordinate.rb b/test/excelx/test_coordinate.rb new file mode 100644 index 0000000..5cddcc4 --- /dev/null +++ b/test/excelx/test_coordinate.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require "test_helper" + +class TestRooExcelxCoordinate < Minitest::Test + def row + 10 + end + + def column + 20 + end + + def coordinate + Roo::Excelx::Coordinate.new(row, column) + end + + def array + [row, column] + end + + def test_row + assert_same row, coordinate.row + end + + def test_column + assert_same column, coordinate.column + end + + def test_frozen? + assert coordinate.frozen? + end + + def test_equality + hash = {} + hash[coordinate] = true + assert hash.key?(coordinate) + assert hash.key?(array) + end + + def test_expand + r, c = coordinate + assert_same row, r + assert_same column, c + end + + def test_aref + assert_same row, coordinate[0] + assert_same column, coordinate[1] + end +end diff --git a/test/files/datetime_timezone_ist_offset_change.ods b/test/files/datetime_timezone_ist_offset_change.ods Binary files differnew file mode 100644 index 0000000..ebe469b --- /dev/null +++ b/test/files/datetime_timezone_ist_offset_change.ods diff --git a/test/files/datetime_timezone_ist_offset_change.xlsx b/test/files/datetime_timezone_ist_offset_change.xlsx Binary files differnew file mode 100644 index 0000000..62240e9 --- /dev/null +++ b/test/files/datetime_timezone_ist_offset_change.xlsx diff --git a/test/files/images.xlsx b/test/files/images.xlsx Binary files differnew file mode 100644 index 0000000..669f371 --- /dev/null +++ b/test/files/images.xlsx diff --git a/test/files/link_with_location.xlsx b/test/files/link_with_location.xlsx Binary files differnew file mode 100644 index 0000000..393f5ac --- /dev/null +++ b/test/files/link_with_location.xlsx diff --git a/test/files/number_with_zero_prefix.xlsx b/test/files/number_with_zero_prefix.xlsx Binary files differnew file mode 100644 index 0000000..104e07f --- /dev/null +++ b/test/files/number_with_zero_prefix.xlsx diff --git a/test/files/simple_spreadsheet.csv b/test/files/simple_spreadsheet.csv new file mode 100644 index 0000000..fc4ab6b --- /dev/null +++ b/test/files/simple_spreadsheet.csv @@ -0,0 +1,13 @@ +,,,,, +,,,,, +Date,Start time,End time,Pause,Sum,Comment +2007-05-07,9.25,10.25,0,1,Task 1 +2007-05-07,10.75,12.50,0,1.75,Task 1 +2007-05-07,18.00,19.00,0,1,Task 2 +2007-05-08,9.25,10.25,0,1,Task 2 +2007-05-08,14.50,15.50,0,1,Task 3 +2007-05-08,8.75,9.25,0,0.5,Task 3 +2007-05-14,21.75,22.25,0,0.5,Task 3 +2007-05-14,22.50,23.00,0,0.5,Task 3 +2007-05-15,11.75,12.75,0,1,Task 3 +2007-05-07,10.75,10.75,0,0,Task 1 diff --git a/test/files/so_datetime_timezone_ist_offset_change.csv b/test/files/so_datetime_timezone_ist_offset_change.csv new file mode 100644 index 0000000..6f602aa --- /dev/null +++ b/test/files/so_datetime_timezone_ist_offset_change.csv @@ -0,0 +1,864 @@ +1941-09-30,1942-08-31,1905-12-31
+1941-09-30T00:05:00+00:00,1942-08-31T00:05:00+00:00,1905-12-31T00:05:00+00:00
+1941-09-30T00:10:00+00:00,1942-08-31T00:10:00+00:00,1905-12-31T00:10:00+00:00
+1941-09-30T00:15:00+00:00,1942-08-31T00:15:00+00:00,1905-12-31T00:15:00+00:00
+1941-09-30T00:20:00+00:00,1942-08-31T00:20:00+00:00,1905-12-31T00:20:00+00:00
+1941-09-30T00:25:00+00:00,1942-08-31T00:25:00+00:00,1905-12-31T00:25:00+00:00
+1941-09-30T00:30:00+00:00,1942-08-31T00:30:00+00:00,1905-12-31T00:30:00+00:00
+1941-09-30T00:35:00+00:00,1942-08-31T00:35:00+00:00,1905-12-31T00:35:00+00:00
+1941-09-30T00:40:00+00:00,1942-08-31T00:40:00+00:00,1905-12-31T00:40:00+00:00
+1941-09-30T00:45:00+00:00,1942-08-31T00:45:00+00:00,1905-12-31T00:45:00+00:00
+1941-09-30T00:50:00+00:00,1942-08-31T00:50:00+00:00,1905-12-31T00:50:00+00:00
+1941-09-30T00:55:00+00:00,1942-08-31T00:55:00+00:00,1905-12-31T00:55:00+00:00
+1941-09-30T01:00:00+00:00,1942-08-31T01:00:00+00:00,1905-12-31T01:00:00+00:00
+1941-09-30T01:05:00+00:00,1942-08-31T01:05:00+00:00,1905-12-31T01:05:00+00:00
+1941-09-30T01:10:00+00:00,1942-08-31T01:10:00+00:00,1905-12-31T01:10:00+00:00
+1941-09-30T01:15:00+00:00,1942-08-31T01:15:00+00:00,1905-12-31T01:15:00+00:00
+1941-09-30T01:20:00+00:00,1942-08-31T01:20:00+00:00,1905-12-31T01:20:00+00:00
+1941-09-30T01:25:00+00:00,1942-08-31T01:25:00+00:00,1905-12-31T01:25:00+00:00
+1941-09-30T01:30:00+00:00,1942-08-31T01:30:00+00:00,1905-12-31T01:30:00+00:00
+1941-09-30T01:35:00+00:00,1942-08-31T01:35:00+00:00,1905-12-31T01:35:00+00:00
+1941-09-30T01:40:00+00:00,1942-08-31T01:40:00+00:00,1905-12-31T01:40:00+00:00
+1941-09-30T01:45:00+00:00,1942-08-31T01:45:00+00:00,1905-12-31T01:45:00+00:00
+1941-09-30T01:50:00+00:00,1942-08-31T01:50:00+00:00,1905-12-31T01:50:00+00:00
+1941-09-30T01:55:00+00:00,1942-08-31T01:55:00+00:00,1905-12-31T01:55:00+00:00
+1941-09-30T02:00:00+00:00,1942-08-31T02:00:00+00:00,1905-12-31T02:00:00+00:00
+1941-09-30T02:05:00+00:00,1942-08-31T02:05:00+00:00,1905-12-31T02:05:00+00:00
+1941-09-30T02:10:00+00:00,1942-08-31T02:10:00+00:00,1905-12-31T02:10:00+00:00
+1941-09-30T02:15:00+00:00,1942-08-31T02:15:00+00:00,1905-12-31T02:15:00+00:00
+1941-09-30T02:20:00+00:00,1942-08-31T02:20:00+00:00,1905-12-31T02:20:00+00:00
+1941-09-30T02:25:00+00:00,1942-08-31T02:25:00+00:00,1905-12-31T02:25:00+00:00
+1941-09-30T02:30:00+00:00,1942-08-31T02:30:00+00:00,1905-12-31T02:30:00+00:00
+1941-09-30T02:35:00+00:00,1942-08-31T02:35:00+00:00,1905-12-31T02:35:00+00:00
+1941-09-30T02:40:00+00:00,1942-08-31T02:40:00+00:00,1905-12-31T02:40:00+00:00
+1941-09-30T02:45:00+00:00,1942-08-31T02:45:00+00:00,1905-12-31T02:45:00+00:00
+1941-09-30T02:50:00+00:00,1942-08-31T02:50:00+00:00,1905-12-31T02:50:00+00:00
+1941-09-30T02:55:00+00:00,1942-08-31T02:55:00+00:00,1905-12-31T02:55:00+00:00
+1941-09-30T03:00:00+00:00,1942-08-31T03:00:00+00:00,1905-12-31T03:00:00+00:00
+1941-09-30T03:05:00+00:00,1942-08-31T03:05:00+00:00,1905-12-31T03:05:00+00:00
+1941-09-30T03:10:00+00:00,1942-08-31T03:10:00+00:00,1905-12-31T03:10:00+00:00
+1941-09-30T03:15:00+00:00,1942-08-31T03:15:00+00:00,1905-12-31T03:15:00+00:00
+1941-09-30T03:20:00+00:00,1942-08-31T03:20:00+00:00,1905-12-31T03:20:00+00:00
+1941-09-30T03:25:00+00:00,1942-08-31T03:25:00+00:00,1905-12-31T03:25:00+00:00
+1941-09-30T03:30:00+00:00,1942-08-31T03:30:00+00:00,1905-12-31T03:30:00+00:00
+1941-09-30T03:35:00+00:00,1942-08-31T03:35:00+00:00,1905-12-31T03:35:00+00:00
+1941-09-30T03:40:00+00:00,1942-08-31T03:40:00+00:00,1905-12-31T03:40:00+00:00
+1941-09-30T03:45:00+00:00,1942-08-31T03:45:00+00:00,1905-12-31T03:45:00+00:00
+1941-09-30T03:50:00+00:00,1942-08-31T03:50:00+00:00,1905-12-31T03:50:00+00:00
+1941-09-30T03:55:00+00:00,1942-08-31T03:55:00+00:00,1905-12-31T03:55:00+00:00
+1941-09-30T04:00:00+00:00,1942-08-31T04:00:00+00:00,1905-12-31T04:00:00+00:00
+1941-09-30T04:05:00+00:00,1942-08-31T04:05:00+00:00,1905-12-31T04:05:00+00:00
+1941-09-30T04:10:00+00:00,1942-08-31T04:10:00+00:00,1905-12-31T04:10:00+00:00
+1941-09-30T04:15:00+00:00,1942-08-31T04:15:00+00:00,1905-12-31T04:15:00+00:00
+1941-09-30T04:20:00+00:00,1942-08-31T04:20:00+00:00,1905-12-31T04:20:00+00:00
+1941-09-30T04:25:00+00:00,1942-08-31T04:25:00+00:00,1905-12-31T04:25:00+00:00
+1941-09-30T04:30:00+00:00,1942-08-31T04:30:00+00:00,1905-12-31T04:30:00+00:00
+1941-09-30T04:35:00+00:00,1942-08-31T04:35:00+00:00,1905-12-31T04:35:00+00:00
+1941-09-30T04:40:00+00:00,1942-08-31T04:40:00+00:00,1905-12-31T04:40:00+00:00
+1941-09-30T04:45:00+00:00,1942-08-31T04:45:00+00:00,1905-12-31T04:45:00+00:00
+1941-09-30T04:50:00+00:00,1942-08-31T04:50:00+00:00,1905-12-31T04:50:00+00:00
+1941-09-30T04:55:00+00:00,1942-08-31T04:55:00+00:00,1905-12-31T04:55:00+00:00
+1941-09-30T05:00:00+00:00,1942-08-31T05:00:00+00:00,1905-12-31T05:00:00+00:00
+1941-09-30T05:05:00+00:00,1942-08-31T05:05:00+00:00,1905-12-31T05:05:00+00:00
+1941-09-30T05:10:00+00:00,1942-08-31T05:10:00+00:00,1905-12-31T05:10:00+00:00
+1941-09-30T05:15:00+00:00,1942-08-31T05:15:00+00:00,1905-12-31T05:15:00+00:00
+1941-09-30T05:20:00+00:00,1942-08-31T05:20:00+00:00,1905-12-31T05:20:00+00:00
+1941-09-30T05:25:00+00:00,1942-08-31T05:25:00+00:00,1905-12-31T05:25:00+00:00
+1941-09-30T05:30:00+00:00,1942-08-31T05:30:00+00:00,1905-12-31T05:30:00+00:00
+1941-09-30T05:35:00+00:00,1942-08-31T05:35:00+00:00,1905-12-31T05:35:00+00:00
+1941-09-30T05:40:00+00:00,1942-08-31T05:40:00+00:00,1905-12-31T05:40:00+00:00
+1941-09-30T05:45:00+00:00,1942-08-31T05:45:00+00:00,1905-12-31T05:45:00+00:00
+1941-09-30T05:50:00+00:00,1942-08-31T05:50:00+00:00,1905-12-31T05:50:00+00:00
+1941-09-30T05:55:00+00:00,1942-08-31T05:55:00+00:00,1905-12-31T05:55:00+00:00
+1941-09-30T06:00:00+00:00,1942-08-31T06:00:00+00:00,1905-12-31T06:00:00+00:00
+1941-09-30T06:05:00+00:00,1942-08-31T06:05:00+00:00,1905-12-31T06:05:00+00:00
+1941-09-30T06:10:00+00:00,1942-08-31T06:10:00+00:00,1905-12-31T06:10:00+00:00
+1941-09-30T06:15:00+00:00,1942-08-31T06:15:00+00:00,1905-12-31T06:15:00+00:00
+1941-09-30T06:20:00+00:00,1942-08-31T06:20:00+00:00,1905-12-31T06:20:00+00:00
+1941-09-30T06:25:00+00:00,1942-08-31T06:25:00+00:00,1905-12-31T06:25:00+00:00
+1941-09-30T06:30:00+00:00,1942-08-31T06:30:00+00:00,1905-12-31T06:30:00+00:00
+1941-09-30T06:35:00+00:00,1942-08-31T06:35:00+00:00,1905-12-31T06:35:00+00:00
+1941-09-30T06:40:00+00:00,1942-08-31T06:40:00+00:00,1905-12-31T06:40:00+00:00
+1941-09-30T06:45:00+00:00,1942-08-31T06:45:00+00:00,1905-12-31T06:45:00+00:00
+1941-09-30T06:50:00+00:00,1942-08-31T06:50:00+00:00,1905-12-31T06:50:00+00:00
+1941-09-30T06:55:00+00:00,1942-08-31T06:55:00+00:00,1905-12-31T06:55:00+00:00
+1941-09-30T07:00:00+00:00,1942-08-31T07:00:00+00:00,1905-12-31T07:00:00+00:00
+1941-09-30T07:05:00+00:00,1942-08-31T07:05:00+00:00,1905-12-31T07:05:00+00:00
+1941-09-30T07:10:00+00:00,1942-08-31T07:10:00+00:00,1905-12-31T07:10:00+00:00
+1941-09-30T07:15:00+00:00,1942-08-31T07:15:00+00:00,1905-12-31T07:15:00+00:00
+1941-09-30T07:20:00+00:00,1942-08-31T07:20:00+00:00,1905-12-31T07:20:00+00:00
+1941-09-30T07:25:00+00:00,1942-08-31T07:25:00+00:00,1905-12-31T07:25:00+00:00
+1941-09-30T07:30:00+00:00,1942-08-31T07:30:00+00:00,1905-12-31T07:30:00+00:00
+1941-09-30T07:35:00+00:00,1942-08-31T07:35:00+00:00,1905-12-31T07:35:00+00:00
+1941-09-30T07:40:00+00:00,1942-08-31T07:40:00+00:00,1905-12-31T07:40:00+00:00
+1941-09-30T07:45:00+00:00,1942-08-31T07:45:00+00:00,1905-12-31T07:45:00+00:00
+1941-09-30T07:50:00+00:00,1942-08-31T07:50:00+00:00,1905-12-31T07:50:00+00:00
+1941-09-30T07:55:00+00:00,1942-08-31T07:55:00+00:00,1905-12-31T07:55:00+00:00
+1941-09-30T08:00:00+00:00,1942-08-31T08:00:00+00:00,1905-12-31T08:00:00+00:00
+1941-09-30T08:05:00+00:00,1942-08-31T08:05:00+00:00,1905-12-31T08:05:00+00:00
+1941-09-30T08:10:00+00:00,1942-08-31T08:10:00+00:00,1905-12-31T08:10:00+00:00
+1941-09-30T08:15:00+00:00,1942-08-31T08:15:00+00:00,1905-12-31T08:15:00+00:00
+1941-09-30T08:20:00+00:00,1942-08-31T08:20:00+00:00,1905-12-31T08:20:00+00:00
+1941-09-30T08:25:00+00:00,1942-08-31T08:25:00+00:00,1905-12-31T08:25:00+00:00
+1941-09-30T08:30:00+00:00,1942-08-31T08:30:00+00:00,1905-12-31T08:30:00+00:00
+1941-09-30T08:35:00+00:00,1942-08-31T08:35:00+00:00,1905-12-31T08:35:00+00:00
+1941-09-30T08:40:00+00:00,1942-08-31T08:40:00+00:00,1905-12-31T08:40:00+00:00
+1941-09-30T08:45:00+00:00,1942-08-31T08:45:00+00:00,1905-12-31T08:45:00+00:00
+1941-09-30T08:50:00+00:00,1942-08-31T08:50:00+00:00,1905-12-31T08:50:00+00:00
+1941-09-30T08:55:00+00:00,1942-08-31T08:55:00+00:00,1905-12-31T08:55:00+00:00
+1941-09-30T09:00:00+00:00,1942-08-31T09:00:00+00:00,1905-12-31T09:00:00+00:00
+1941-09-30T09:05:00+00:00,1942-08-31T09:05:00+00:00,1905-12-31T09:05:00+00:00
+1941-09-30T09:10:00+00:00,1942-08-31T09:10:00+00:00,1905-12-31T09:10:00+00:00
+1941-09-30T09:15:00+00:00,1942-08-31T09:15:00+00:00,1905-12-31T09:15:00+00:00
+1941-09-30T09:20:00+00:00,1942-08-31T09:20:00+00:00,1905-12-31T09:20:00+00:00
+1941-09-30T09:25:00+00:00,1942-08-31T09:25:00+00:00,1905-12-31T09:25:00+00:00
+1941-09-30T09:30:00+00:00,1942-08-31T09:30:00+00:00,1905-12-31T09:30:00+00:00
+1941-09-30T09:35:00+00:00,1942-08-31T09:35:00+00:00,1905-12-31T09:35:00+00:00
+1941-09-30T09:40:00+00:00,1942-08-31T09:40:00+00:00,1905-12-31T09:40:00+00:00
+1941-09-30T09:45:00+00:00,1942-08-31T09:45:00+00:00,1905-12-31T09:45:00+00:00
+1941-09-30T09:50:00+00:00,1942-08-31T09:50:00+00:00,1905-12-31T09:50:00+00:00
+1941-09-30T09:55:00+00:00,1942-08-31T09:55:00+00:00,1905-12-31T09:55:00+00:00
+1941-09-30T10:00:00+00:00,1942-08-31T10:00:00+00:00,1905-12-31T10:00:00+00:00
+1941-09-30T10:05:00+00:00,1942-08-31T10:05:00+00:00,1905-12-31T10:05:00+00:00
+1941-09-30T10:10:00+00:00,1942-08-31T10:10:00+00:00,1905-12-31T10:10:00+00:00
+1941-09-30T10:15:00+00:00,1942-08-31T10:15:00+00:00,1905-12-31T10:15:00+00:00
+1941-09-30T10:20:00+00:00,1942-08-31T10:20:00+00:00,1905-12-31T10:20:00+00:00
+1941-09-30T10:25:00+00:00,1942-08-31T10:25:00+00:00,1905-12-31T10:25:00+00:00
+1941-09-30T10:30:00+00:00,1942-08-31T10:30:00+00:00,1905-12-31T10:30:00+00:00
+1941-09-30T10:35:00+00:00,1942-08-31T10:35:00+00:00,1905-12-31T10:35:00+00:00
+1941-09-30T10:40:00+00:00,1942-08-31T10:40:00+00:00,1905-12-31T10:40:00+00:00
+1941-09-30T10:45:00+00:00,1942-08-31T10:45:00+00:00,1905-12-31T10:45:00+00:00
+1941-09-30T10:50:00+00:00,1942-08-31T10:50:00+00:00,1905-12-31T10:50:00+00:00
+1941-09-30T10:55:00+00:00,1942-08-31T10:55:00+00:00,1905-12-31T10:55:00+00:00
+1941-09-30T11:00:00+00:00,1942-08-31T11:00:00+00:00,1905-12-31T11:00:00+00:00
+1941-09-30T11:05:00+00:00,1942-08-31T11:05:00+00:00,1905-12-31T11:05:00+00:00
+1941-09-30T11:10:00+00:00,1942-08-31T11:10:00+00:00,1905-12-31T11:10:00+00:00
+1941-09-30T11:15:00+00:00,1942-08-31T11:15:00+00:00,1905-12-31T11:15:00+00:00
+1941-09-30T11:20:00+00:00,1942-08-31T11:20:00+00:00,1905-12-31T11:20:00+00:00
+1941-09-30T11:25:00+00:00,1942-08-31T11:25:00+00:00,1905-12-31T11:25:00+00:00
+1941-09-30T11:30:00+00:00,1942-08-31T11:30:00+00:00,1905-12-31T11:30:00+00:00
+1941-09-30T11:35:00+00:00,1942-08-31T11:35:00+00:00,1905-12-31T11:35:00+00:00
+1941-09-30T11:40:00+00:00,1942-08-31T11:40:00+00:00,1905-12-31T11:40:00+00:00
+1941-09-30T11:45:00+00:00,1942-08-31T11:45:00+00:00,1905-12-31T11:45:00+00:00
+1941-09-30T11:50:00+00:00,1942-08-31T11:50:00+00:00,1905-12-31T11:50:00+00:00
+1941-09-30T11:55:00+00:00,1942-08-31T11:55:00+00:00,1905-12-31T11:55:00+00:00
+1941-09-30T12:00:00+00:00,1942-08-31T12:00:00+00:00,1905-12-31T12:00:00+00:00
+1941-09-30T12:05:00+00:00,1942-08-31T12:05:00+00:00,1905-12-31T12:05:00+00:00
+1941-09-30T12:10:00+00:00,1942-08-31T12:10:00+00:00,1905-12-31T12:10:00+00:00
+1941-09-30T12:15:00+00:00,1942-08-31T12:15:00+00:00,1905-12-31T12:15:00+00:00
+1941-09-30T12:20:00+00:00,1942-08-31T12:20:00+00:00,1905-12-31T12:20:00+00:00
+1941-09-30T12:25:00+00:00,1942-08-31T12:25:00+00:00,1905-12-31T12:25:00+00:00
+1941-09-30T12:30:00+00:00,1942-08-31T12:30:00+00:00,1905-12-31T12:30:00+00:00
+1941-09-30T12:35:00+00:00,1942-08-31T12:35:00+00:00,1905-12-31T12:35:00+00:00
+1941-09-30T12:40:00+00:00,1942-08-31T12:40:00+00:00,1905-12-31T12:40:00+00:00
+1941-09-30T12:45:00+00:00,1942-08-31T12:45:00+00:00,1905-12-31T12:45:00+00:00
+1941-09-30T12:50:00+00:00,1942-08-31T12:50:00+00:00,1905-12-31T12:50:00+00:00
+1941-09-30T12:55:00+00:00,1942-08-31T12:55:00+00:00,1905-12-31T12:55:00+00:00
+1941-09-30T13:00:00+00:00,1942-08-31T13:00:00+00:00,1905-12-31T13:00:00+00:00
+1941-09-30T13:05:00+00:00,1942-08-31T13:05:00+00:00,1905-12-31T13:05:00+00:00
+1941-09-30T13:10:00+00:00,1942-08-31T13:10:00+00:00,1905-12-31T13:10:00+00:00
+1941-09-30T13:15:00+00:00,1942-08-31T13:15:00+00:00,1905-12-31T13:15:00+00:00
+1941-09-30T13:20:00+00:00,1942-08-31T13:20:00+00:00,1905-12-31T13:20:00+00:00
+1941-09-30T13:25:00+00:00,1942-08-31T13:25:00+00:00,1905-12-31T13:25:00+00:00
+1941-09-30T13:30:00+00:00,1942-08-31T13:30:00+00:00,1905-12-31T13:30:00+00:00
+1941-09-30T13:35:00+00:00,1942-08-31T13:35:00+00:00,1905-12-31T13:35:00+00:00
+1941-09-30T13:40:00+00:00,1942-08-31T13:40:00+00:00,1905-12-31T13:40:00+00:00
+1941-09-30T13:45:00+00:00,1942-08-31T13:45:00+00:00,1905-12-31T13:45:00+00:00
+1941-09-30T13:50:00+00:00,1942-08-31T13:50:00+00:00,1905-12-31T13:50:00+00:00
+1941-09-30T13:55:00+00:00,1942-08-31T13:55:00+00:00,1905-12-31T13:55:00+00:00
+1941-09-30T14:00:00+00:00,1942-08-31T14:00:00+00:00,1905-12-31T14:00:00+00:00
+1941-09-30T14:05:00+00:00,1942-08-31T14:05:00+00:00,1905-12-31T14:05:00+00:00
+1941-09-30T14:10:00+00:00,1942-08-31T14:10:00+00:00,1905-12-31T14:10:00+00:00
+1941-09-30T14:15:00+00:00,1942-08-31T14:15:00+00:00,1905-12-31T14:15:00+00:00
+1941-09-30T14:20:00+00:00,1942-08-31T14:20:00+00:00,1905-12-31T14:20:00+00:00
+1941-09-30T14:25:00+00:00,1942-08-31T14:25:00+00:00,1905-12-31T14:25:00+00:00
+1941-09-30T14:30:00+00:00,1942-08-31T14:30:00+00:00,1905-12-31T14:30:00+00:00
+1941-09-30T14:35:00+00:00,1942-08-31T14:35:00+00:00,1905-12-31T14:35:00+00:00
+1941-09-30T14:40:00+00:00,1942-08-31T14:40:00+00:00,1905-12-31T14:40:00+00:00
+1941-09-30T14:45:00+00:00,1942-08-31T14:45:00+00:00,1905-12-31T14:45:00+00:00
+1941-09-30T14:50:00+00:00,1942-08-31T14:50:00+00:00,1905-12-31T14:50:00+00:00
+1941-09-30T14:55:00+00:00,1942-08-31T14:55:00+00:00,1905-12-31T14:55:00+00:00
+1941-09-30T15:00:00+00:00,1942-08-31T15:00:00+00:00,1905-12-31T15:00:00+00:00
+1941-09-30T15:05:00+00:00,1942-08-31T15:05:00+00:00,1905-12-31T15:05:00+00:00
+1941-09-30T15:10:00+00:00,1942-08-31T15:10:00+00:00,1905-12-31T15:10:00+00:00
+1941-09-30T15:15:00+00:00,1942-08-31T15:15:00+00:00,1905-12-31T15:15:00+00:00
+1941-09-30T15:20:00+00:00,1942-08-31T15:20:00+00:00,1905-12-31T15:20:00+00:00
+1941-09-30T15:25:00+00:00,1942-08-31T15:25:00+00:00,1905-12-31T15:25:00+00:00
+1941-09-30T15:30:00+00:00,1942-08-31T15:30:00+00:00,1905-12-31T15:30:00+00:00
+1941-09-30T15:35:00+00:00,1942-08-31T15:35:00+00:00,1905-12-31T15:35:00+00:00
+1941-09-30T15:40:00+00:00,1942-08-31T15:40:00+00:00,1905-12-31T15:40:00+00:00
+1941-09-30T15:45:00+00:00,1942-08-31T15:45:00+00:00,1905-12-31T15:45:00+00:00
+1941-09-30T15:50:00+00:00,1942-08-31T15:50:00+00:00,1905-12-31T15:50:00+00:00
+1941-09-30T15:55:00+00:00,1942-08-31T15:55:00+00:00,1905-12-31T15:55:00+00:00
+1941-09-30T16:00:00+00:00,1942-08-31T16:00:00+00:00,1905-12-31T16:00:00+00:00
+1941-09-30T16:05:00+00:00,1942-08-31T16:05:00+00:00,1905-12-31T16:05:00+00:00
+1941-09-30T16:10:00+00:00,1942-08-31T16:10:00+00:00,1905-12-31T16:10:00+00:00
+1941-09-30T16:15:00+00:00,1942-08-31T16:15:00+00:00,1905-12-31T16:15:00+00:00
+1941-09-30T16:20:00+00:00,1942-08-31T16:20:00+00:00,1905-12-31T16:20:00+00:00
+1941-09-30T16:25:00+00:00,1942-08-31T16:25:00+00:00,1905-12-31T16:25:00+00:00
+1941-09-30T16:30:00+00:00,1942-08-31T16:30:00+00:00,1905-12-31T16:30:00+00:00
+1941-09-30T16:35:00+00:00,1942-08-31T16:35:00+00:00,1905-12-31T16:35:00+00:00
+1941-09-30T16:40:00+00:00,1942-08-31T16:40:00+00:00,1905-12-31T16:40:00+00:00
+1941-09-30T16:45:00+00:00,1942-08-31T16:45:00+00:00,1905-12-31T16:45:00+00:00
+1941-09-30T16:50:00+00:00,1942-08-31T16:50:00+00:00,1905-12-31T16:50:00+00:00
+1941-09-30T16:55:00+00:00,1942-08-31T16:55:00+00:00,1905-12-31T16:55:00+00:00
+1941-09-30T17:00:00+00:00,1942-08-31T17:00:00+00:00,1905-12-31T17:00:00+00:00
+1941-09-30T17:05:00+00:00,1942-08-31T17:05:00+00:00,1905-12-31T17:05:00+00:00
+1941-09-30T17:10:00+00:00,1942-08-31T17:10:00+00:00,1905-12-31T17:10:00+00:00
+1941-09-30T17:15:00+00:00,1942-08-31T17:15:00+00:00,1905-12-31T17:15:00+00:00
+1941-09-30T17:20:00+00:00,1942-08-31T17:20:00+00:00,1905-12-31T17:20:00+00:00
+1941-09-30T17:25:00+00:00,1942-08-31T17:25:00+00:00,1905-12-31T17:25:00+00:00
+1941-09-30T17:30:00+00:00,1942-08-31T17:30:00+00:00,1905-12-31T17:30:00+00:00
+1941-09-30T17:35:00+00:00,1942-08-31T17:35:00+00:00,1905-12-31T17:35:00+00:00
+1941-09-30T17:40:00+00:00,1942-08-31T17:40:00+00:00,1905-12-31T17:40:00+00:00
+1941-09-30T17:45:00+00:00,1942-08-31T17:45:00+00:00,1905-12-31T17:45:00+00:00
+1941-09-30T17:50:00+00:00,1942-08-31T17:50:00+00:00,1905-12-31T17:50:00+00:00
+1941-09-30T17:55:00+00:00,1942-08-31T17:55:00+00:00,1905-12-31T17:55:00+00:00
+1941-09-30T18:00:00+00:00,1942-08-31T18:00:00+00:00,1905-12-31T18:00:00+00:00
+1941-09-30T18:05:00+00:00,1942-08-31T18:05:00+00:00,1905-12-31T18:05:00+00:00
+1941-09-30T18:10:00+00:00,1942-08-31T18:10:00+00:00,1905-12-31T18:10:00+00:00
+1941-09-30T18:15:00+00:00,1942-08-31T18:15:00+00:00,1905-12-31T18:15:00+00:00
+1941-09-30T18:20:00+00:00,1942-08-31T18:20:00+00:00,1905-12-31T18:20:00+00:00
+1941-09-30T18:25:00+00:00,1942-08-31T18:25:00+00:00,1905-12-31T18:25:00+00:00
+1941-09-30T18:30:00+00:00,1942-08-31T18:30:00+00:00,1905-12-31T18:30:00+00:00
+1941-09-30T18:35:00+00:00,1942-08-31T18:35:00+00:00,1905-12-31T18:35:00+00:00
+1941-09-30T18:40:00+00:00,1942-08-31T18:40:00+00:00,1905-12-31T18:40:00+00:00
+1941-09-30T18:45:00+00:00,1942-08-31T18:45:00+00:00,1905-12-31T18:45:00+00:00
+1941-09-30T18:50:00+00:00,1942-08-31T18:50:00+00:00,1905-12-31T18:50:00+00:00
+1941-09-30T18:55:00+00:00,1942-08-31T18:55:00+00:00,1905-12-31T18:55:00+00:00
+1941-09-30T19:00:00+00:00,1942-08-31T19:00:00+00:00,1905-12-31T19:00:00+00:00
+1941-09-30T19:05:00+00:00,1942-08-31T19:05:00+00:00,1905-12-31T19:05:00+00:00
+1941-09-30T19:10:00+00:00,1942-08-31T19:10:00+00:00,1905-12-31T19:10:00+00:00
+1941-09-30T19:15:00+00:00,1942-08-31T19:15:00+00:00,1905-12-31T19:15:00+00:00
+1941-09-30T19:20:00+00:00,1942-08-31T19:20:00+00:00,1905-12-31T19:20:00+00:00
+1941-09-30T19:25:00+00:00,1942-08-31T19:25:00+00:00,1905-12-31T19:25:00+00:00
+1941-09-30T19:30:00+00:00,1942-08-31T19:30:00+00:00,1905-12-31T19:30:00+00:00
+1941-09-30T19:35:00+00:00,1942-08-31T19:35:00+00:00,1905-12-31T19:35:00+00:00
+1941-09-30T19:40:00+00:00,1942-08-31T19:40:00+00:00,1905-12-31T19:40:00+00:00
+1941-09-30T19:45:00+00:00,1942-08-31T19:45:00+00:00,1905-12-31T19:45:00+00:00
+1941-09-30T19:50:00+00:00,1942-08-31T19:50:00+00:00,1905-12-31T19:50:00+00:00
+1941-09-30T19:55:00+00:00,1942-08-31T19:55:00+00:00,1905-12-31T19:55:00+00:00
+1941-09-30T20:00:00+00:00,1942-08-31T20:00:00+00:00,1905-12-31T20:00:00+00:00
+1941-09-30T20:05:00+00:00,1942-08-31T20:05:00+00:00,1905-12-31T20:05:00+00:00
+1941-09-30T20:10:00+00:00,1942-08-31T20:10:00+00:00,1905-12-31T20:10:00+00:00
+1941-09-30T20:15:00+00:00,1942-08-31T20:15:00+00:00,1905-12-31T20:15:00+00:00
+1941-09-30T20:20:00+00:00,1942-08-31T20:20:00+00:00,1905-12-31T20:20:00+00:00
+1941-09-30T20:25:00+00:00,1942-08-31T20:25:00+00:00,1905-12-31T20:25:00+00:00
+1941-09-30T20:30:00+00:00,1942-08-31T20:30:00+00:00,1905-12-31T20:30:00+00:00
+1941-09-30T20:35:00+00:00,1942-08-31T20:35:00+00:00,1905-12-31T20:35:00+00:00
+1941-09-30T20:40:00+00:00,1942-08-31T20:40:00+00:00,1905-12-31T20:40:00+00:00
+1941-09-30T20:45:00+00:00,1942-08-31T20:45:00+00:00,1905-12-31T20:45:00+00:00
+1941-09-30T20:50:00+00:00,1942-08-31T20:50:00+00:00,1905-12-31T20:50:00+00:00
+1941-09-30T20:55:00+00:00,1942-08-31T20:55:00+00:00,1905-12-31T20:55:00+00:00
+1941-09-30T21:00:00+00:00,1942-08-31T21:00:00+00:00,1905-12-31T21:00:00+00:00
+1941-09-30T21:05:00+00:00,1942-08-31T21:05:00+00:00,1905-12-31T21:05:00+00:00
+1941-09-30T21:10:00+00:00,1942-08-31T21:10:00+00:00,1905-12-31T21:10:00+00:00
+1941-09-30T21:15:00+00:00,1942-08-31T21:15:00+00:00,1905-12-31T21:15:00+00:00
+1941-09-30T21:20:00+00:00,1942-08-31T21:20:00+00:00,1905-12-31T21:20:00+00:00
+1941-09-30T21:25:00+00:00,1942-08-31T21:25:00+00:00,1905-12-31T21:25:00+00:00
+1941-09-30T21:30:00+00:00,1942-08-31T21:30:00+00:00,1905-12-31T21:30:00+00:00
+1941-09-30T21:35:00+00:00,1942-08-31T21:35:00+00:00,1905-12-31T21:35:00+00:00
+1941-09-30T21:40:00+00:00,1942-08-31T21:40:00+00:00,1905-12-31T21:40:00+00:00
+1941-09-30T21:45:00+00:00,1942-08-31T21:45:00+00:00,1905-12-31T21:45:00+00:00
+1941-09-30T21:50:00+00:00,1942-08-31T21:50:00+00:00,1905-12-31T21:50:00+00:00
+1941-09-30T21:55:00+00:00,1942-08-31T21:55:00+00:00,1905-12-31T21:55:00+00:00
+1941-09-30T22:00:00+00:00,1942-08-31T22:00:00+00:00,1905-12-31T22:00:00+00:00
+1941-09-30T22:05:00+00:00,1942-08-31T22:05:00+00:00,1905-12-31T22:05:00+00:00
+1941-09-30T22:10:00+00:00,1942-08-31T22:10:00+00:00,1905-12-31T22:10:00+00:00
+1941-09-30T22:15:00+00:00,1942-08-31T22:15:00+00:00,1905-12-31T22:15:00+00:00
+1941-09-30T22:20:00+00:00,1942-08-31T22:20:00+00:00,1905-12-31T22:20:00+00:00
+1941-09-30T22:25:00+00:00,1942-08-31T22:25:00+00:00,1905-12-31T22:25:00+00:00
+1941-09-30T22:30:00+00:00,1942-08-31T22:30:00+00:00,1905-12-31T22:30:00+00:00
+1941-09-30T22:35:00+00:00,1942-08-31T22:35:00+00:00,1905-12-31T22:35:00+00:00
+1941-09-30T22:40:00+00:00,1942-08-31T22:40:00+00:00,1905-12-31T22:40:00+00:00
+1941-09-30T22:45:00+00:00,1942-08-31T22:45:00+00:00,1905-12-31T22:45:00+00:00
+1941-09-30T22:50:00+00:00,1942-08-31T22:50:00+00:00,1905-12-31T22:50:00+00:00
+1941-09-30T22:55:00+00:00,1942-08-31T22:55:00+00:00,1905-12-31T22:55:00+00:00
+1941-09-30T23:00:00+00:00,1942-08-31T23:00:00+00:00,1905-12-31T23:00:00+00:00
+1941-09-30T23:05:00+00:00,1942-08-31T23:05:00+00:00,1905-12-31T23:05:00+00:00
+1941-09-30T23:10:00+00:00,1942-08-31T23:10:00+00:00,1905-12-31T23:10:00+00:00
+1941-09-30T23:15:00+00:00,1942-08-31T23:15:00+00:00,1905-12-31T23:15:00+00:00
+1941-09-30T23:20:00+00:00,1942-08-31T23:20:00+00:00,1905-12-31T23:20:00+00:00
+1941-09-30T23:25:00+00:00,1942-08-31T23:25:00+00:00,1905-12-31T23:25:00+00:00
+1941-09-30T23:30:00+00:00,1942-08-31T23:30:00+00:00,1905-12-31T23:30:00+00:00
+1941-09-30T23:35:00+00:00,1942-08-31T23:35:00+00:00,1905-12-31T23:35:00+00:00
+1941-09-30T23:40:00+00:00,1942-08-31T23:40:00+00:00,1905-12-31T23:40:00+00:00
+1941-09-30T23:45:00+00:00,1942-08-31T23:45:00+00:00,1905-12-31T23:45:00+00:00
+1941-09-30T23:50:00+00:00,1942-08-31T23:50:00+00:00,1905-12-31T23:50:00+00:00
+1941-09-30T23:55:00+00:00,1942-08-31T23:55:00+00:00,1905-12-31T23:55:00+00:00
+1941-10-01,1942-09-01,1906-01-01
+1941-10-01T00:05:00+00:00,1942-09-01T00:05:00+00:00,1906-01-01T00:05:00+00:00
+1941-10-01T00:10:00+00:00,1942-09-01T00:10:00+00:00,1906-01-01T00:10:00+00:00
+1941-10-01T00:15:00+00:00,1942-09-01T00:15:00+00:00,1906-01-01T00:15:00+00:00
+1941-10-01T00:20:00+00:00,1942-09-01T00:20:00+00:00,1906-01-01T00:20:00+00:00
+1941-10-01T00:25:00+00:00,1942-09-01T00:25:00+00:00,1906-01-01T00:25:00+00:00
+1941-10-01T00:30:00+00:00,1942-09-01T00:30:00+00:00,1906-01-01T00:30:00+00:00
+1941-10-01T00:35:00+00:00,1942-09-01T00:35:00+00:00,1906-01-01T00:35:00+00:00
+1941-10-01T00:40:00+00:00,1942-09-01T00:40:00+00:00,1906-01-01T00:40:00+00:00
+1941-10-01T00:45:00+00:00,1942-09-01T00:45:00+00:00,1906-01-01T00:45:00+00:00
+1941-10-01T00:50:00+00:00,1942-09-01T00:50:00+00:00,1906-01-01T00:50:00+00:00
+1941-10-01T00:55:00+00:00,1942-09-01T00:55:00+00:00,1906-01-01T00:55:00+00:00
+1941-10-01T01:00:00+00:00,1942-09-01T01:00:00+00:00,1906-01-01T01:00:00+00:00
+1941-10-01T01:05:00+00:00,1942-09-01T01:05:00+00:00,1906-01-01T01:05:00+00:00
+1941-10-01T01:10:00+00:00,1942-09-01T01:10:00+00:00,1906-01-01T01:10:00+00:00
+1941-10-01T01:15:00+00:00,1942-09-01T01:15:00+00:00,1906-01-01T01:15:00+00:00
+1941-10-01T01:20:00+00:00,1942-09-01T01:20:00+00:00,1906-01-01T01:20:00+00:00
+1941-10-01T01:25:00+00:00,1942-09-01T01:25:00+00:00,1906-01-01T01:25:00+00:00
+1941-10-01T01:30:00+00:00,1942-09-01T01:30:00+00:00,1906-01-01T01:30:00+00:00
+1941-10-01T01:35:00+00:00,1942-09-01T01:35:00+00:00,1906-01-01T01:35:00+00:00
+1941-10-01T01:40:00+00:00,1942-09-01T01:40:00+00:00,1906-01-01T01:40:00+00:00
+1941-10-01T01:45:00+00:00,1942-09-01T01:45:00+00:00,1906-01-01T01:45:00+00:00
+1941-10-01T01:50:00+00:00,1942-09-01T01:50:00+00:00,1906-01-01T01:50:00+00:00
+1941-10-01T01:55:00+00:00,1942-09-01T01:55:00+00:00,1906-01-01T01:55:00+00:00
+1941-10-01T02:00:00+00:00,1942-09-01T02:00:00+00:00,1906-01-01T02:00:00+00:00
+1941-10-01T02:05:00+00:00,1942-09-01T02:05:00+00:00,1906-01-01T02:05:00+00:00
+1941-10-01T02:10:00+00:00,1942-09-01T02:10:00+00:00,1906-01-01T02:10:00+00:00
+1941-10-01T02:15:00+00:00,1942-09-01T02:15:00+00:00,1906-01-01T02:15:00+00:00
+1941-10-01T02:20:00+00:00,1942-09-01T02:20:00+00:00,1906-01-01T02:20:00+00:00
+1941-10-01T02:25:00+00:00,1942-09-01T02:25:00+00:00,1906-01-01T02:25:00+00:00
+1941-10-01T02:30:00+00:00,1942-09-01T02:30:00+00:00,1906-01-01T02:30:00+00:00
+1941-10-01T02:35:00+00:00,1942-09-01T02:35:00+00:00,1906-01-01T02:35:00+00:00
+1941-10-01T02:40:00+00:00,1942-09-01T02:40:00+00:00,1906-01-01T02:40:00+00:00
+1941-10-01T02:45:00+00:00,1942-09-01T02:45:00+00:00,1906-01-01T02:45:00+00:00
+1941-10-01T02:50:00+00:00,1942-09-01T02:50:00+00:00,1906-01-01T02:50:00+00:00
+1941-10-01T02:55:00+00:00,1942-09-01T02:55:00+00:00,1906-01-01T02:55:00+00:00
+1941-10-01T03:00:00+00:00,1942-09-01T03:00:00+00:00,1906-01-01T03:00:00+00:00
+1941-10-01T03:05:00+00:00,1942-09-01T03:05:00+00:00,1906-01-01T03:05:00+00:00
+1941-10-01T03:10:00+00:00,1942-09-01T03:10:00+00:00,1906-01-01T03:10:00+00:00
+1941-10-01T03:15:00+00:00,1942-09-01T03:15:00+00:00,1906-01-01T03:15:00+00:00
+1941-10-01T03:20:00+00:00,1942-09-01T03:20:00+00:00,1906-01-01T03:20:00+00:00
+1941-10-01T03:25:00+00:00,1942-09-01T03:25:00+00:00,1906-01-01T03:25:00+00:00
+1941-10-01T03:30:00+00:00,1942-09-01T03:30:00+00:00,1906-01-01T03:30:00+00:00
+1941-10-01T03:35:00+00:00,1942-09-01T03:35:00+00:00,1906-01-01T03:35:00+00:00
+1941-10-01T03:40:00+00:00,1942-09-01T03:40:00+00:00,1906-01-01T03:40:00+00:00
+1941-10-01T03:45:00+00:00,1942-09-01T03:45:00+00:00,1906-01-01T03:45:00+00:00
+1941-10-01T03:50:00+00:00,1942-09-01T03:50:00+00:00,1906-01-01T03:50:00+00:00
+1941-10-01T03:55:00+00:00,1942-09-01T03:55:00+00:00,1906-01-01T03:55:00+00:00
+1941-10-01T04:00:00+00:00,1942-09-01T04:00:00+00:00,1906-01-01T04:00:00+00:00
+1941-10-01T04:05:00+00:00,1942-09-01T04:05:00+00:00,1906-01-01T04:05:00+00:00
+1941-10-01T04:10:00+00:00,1942-09-01T04:10:00+00:00,1906-01-01T04:10:00+00:00
+1941-10-01T04:15:00+00:00,1942-09-01T04:15:00+00:00,1906-01-01T04:15:00+00:00
+1941-10-01T04:20:00+00:00,1942-09-01T04:20:00+00:00,1906-01-01T04:20:00+00:00
+1941-10-01T04:25:00+00:00,1942-09-01T04:25:00+00:00,1906-01-01T04:25:00+00:00
+1941-10-01T04:30:00+00:00,1942-09-01T04:30:00+00:00,1906-01-01T04:30:00+00:00
+1941-10-01T04:35:00+00:00,1942-09-01T04:35:00+00:00,1906-01-01T04:35:00+00:00
+1941-10-01T04:40:00+00:00,1942-09-01T04:40:00+00:00,1906-01-01T04:40:00+00:00
+1941-10-01T04:45:00+00:00,1942-09-01T04:45:00+00:00,1906-01-01T04:45:00+00:00
+1941-10-01T04:50:00+00:00,1942-09-01T04:50:00+00:00,1906-01-01T04:50:00+00:00
+1941-10-01T04:55:00+00:00,1942-09-01T04:55:00+00:00,1906-01-01T04:55:00+00:00
+1941-10-01T05:00:00+00:00,1942-09-01T05:00:00+00:00,1906-01-01T05:00:00+00:00
+1941-10-01T05:05:00+00:00,1942-09-01T05:05:00+00:00,1906-01-01T05:05:00+00:00
+1941-10-01T05:10:00+00:00,1942-09-01T05:10:00+00:00,1906-01-01T05:10:00+00:00
+1941-10-01T05:15:00+00:00,1942-09-01T05:15:00+00:00,1906-01-01T05:15:00+00:00
+1941-10-01T05:20:00+00:00,1942-09-01T05:20:00+00:00,1906-01-01T05:20:00+00:00
+1941-10-01T05:25:00+00:00,1942-09-01T05:25:00+00:00,1906-01-01T05:25:00+00:00
+1941-10-01T05:30:00+00:00,1942-09-01T05:30:00+00:00,1906-01-01T05:30:00+00:00
+1941-10-01T05:35:00+00:00,1942-09-01T05:35:00+00:00,1906-01-01T05:35:00+00:00
+1941-10-01T05:40:00+00:00,1942-09-01T05:40:00+00:00,1906-01-01T05:40:00+00:00
+1941-10-01T05:45:00+00:00,1942-09-01T05:45:00+00:00,1906-01-01T05:45:00+00:00
+1941-10-01T05:50:00+00:00,1942-09-01T05:50:00+00:00,1906-01-01T05:50:00+00:00
+1941-10-01T05:55:00+00:00,1942-09-01T05:55:00+00:00,1906-01-01T05:55:00+00:00
+1941-10-01T06:00:00+00:00,1942-09-01T06:00:00+00:00,1906-01-01T06:00:00+00:00
+1941-10-01T06:05:00+00:00,1942-09-01T06:05:00+00:00,1906-01-01T06:05:00+00:00
+1941-10-01T06:10:00+00:00,1942-09-01T06:10:00+00:00,1906-01-01T06:10:00+00:00
+1941-10-01T06:15:00+00:00,1942-09-01T06:15:00+00:00,1906-01-01T06:15:00+00:00
+1941-10-01T06:20:00+00:00,1942-09-01T06:20:00+00:00,1906-01-01T06:20:00+00:00
+1941-10-01T06:25:00+00:00,1942-09-01T06:25:00+00:00,1906-01-01T06:25:00+00:00
+1941-10-01T06:30:00+00:00,1942-09-01T06:30:00+00:00,1906-01-01T06:30:00+00:00
+1941-10-01T06:35:00+00:00,1942-09-01T06:35:00+00:00,1906-01-01T06:35:00+00:00
+1941-10-01T06:40:00+00:00,1942-09-01T06:40:00+00:00,1906-01-01T06:40:00+00:00
+1941-10-01T06:45:00+00:00,1942-09-01T06:45:00+00:00,1906-01-01T06:45:00+00:00
+1941-10-01T06:50:00+00:00,1942-09-01T06:50:00+00:00,1906-01-01T06:50:00+00:00
+1941-10-01T06:55:00+00:00,1942-09-01T06:55:00+00:00,1906-01-01T06:55:00+00:00
+1941-10-01T07:00:00+00:00,1942-09-01T07:00:00+00:00,1906-01-01T07:00:00+00:00
+1941-10-01T07:05:00+00:00,1942-09-01T07:05:00+00:00,1906-01-01T07:05:00+00:00
+1941-10-01T07:10:00+00:00,1942-09-01T07:10:00+00:00,1906-01-01T07:10:00+00:00
+1941-10-01T07:15:00+00:00,1942-09-01T07:15:00+00:00,1906-01-01T07:15:00+00:00
+1941-10-01T07:20:00+00:00,1942-09-01T07:20:00+00:00,1906-01-01T07:20:00+00:00
+1941-10-01T07:25:00+00:00,1942-09-01T07:25:00+00:00,1906-01-01T07:25:00+00:00
+1941-10-01T07:30:00+00:00,1942-09-01T07:30:00+00:00,1906-01-01T07:30:00+00:00
+1941-10-01T07:35:00+00:00,1942-09-01T07:35:00+00:00,1906-01-01T07:35:00+00:00
+1941-10-01T07:40:00+00:00,1942-09-01T07:40:00+00:00,1906-01-01T07:40:00+00:00
+1941-10-01T07:45:00+00:00,1942-09-01T07:45:00+00:00,1906-01-01T07:45:00+00:00
+1941-10-01T07:50:00+00:00,1942-09-01T07:50:00+00:00,1906-01-01T07:50:00+00:00
+1941-10-01T07:55:00+00:00,1942-09-01T07:55:00+00:00,1906-01-01T07:55:00+00:00
+1941-10-01T08:00:00+00:00,1942-09-01T08:00:00+00:00,1906-01-01T08:00:00+00:00
+1941-10-01T08:05:00+00:00,1942-09-01T08:05:00+00:00,1906-01-01T08:05:00+00:00
+1941-10-01T08:10:00+00:00,1942-09-01T08:10:00+00:00,1906-01-01T08:10:00+00:00
+1941-10-01T08:15:00+00:00,1942-09-01T08:15:00+00:00,1906-01-01T08:15:00+00:00
+1941-10-01T08:20:00+00:00,1942-09-01T08:20:00+00:00,1906-01-01T08:20:00+00:00
+1941-10-01T08:25:00+00:00,1942-09-01T08:25:00+00:00,1906-01-01T08:25:00+00:00
+1941-10-01T08:30:00+00:00,1942-09-01T08:30:00+00:00,1906-01-01T08:30:00+00:00
+1941-10-01T08:35:00+00:00,1942-09-01T08:35:00+00:00,1906-01-01T08:35:00+00:00
+1941-10-01T08:40:00+00:00,1942-09-01T08:40:00+00:00,1906-01-01T08:40:00+00:00
+1941-10-01T08:45:00+00:00,1942-09-01T08:45:00+00:00,1906-01-01T08:45:00+00:00
+1941-10-01T08:50:00+00:00,1942-09-01T08:50:00+00:00,1906-01-01T08:50:00+00:00
+1941-10-01T08:55:00+00:00,1942-09-01T08:55:00+00:00,1906-01-01T08:55:00+00:00
+1941-10-01T09:00:00+00:00,1942-09-01T09:00:00+00:00,1906-01-01T09:00:00+00:00
+1941-10-01T09:05:00+00:00,1942-09-01T09:05:00+00:00,1906-01-01T09:05:00+00:00
+1941-10-01T09:10:00+00:00,1942-09-01T09:10:00+00:00,1906-01-01T09:10:00+00:00
+1941-10-01T09:15:00+00:00,1942-09-01T09:15:00+00:00,1906-01-01T09:15:00+00:00
+1941-10-01T09:20:00+00:00,1942-09-01T09:20:00+00:00,1906-01-01T09:20:00+00:00
+1941-10-01T09:25:00+00:00,1942-09-01T09:25:00+00:00,1906-01-01T09:25:00+00:00
+1941-10-01T09:30:00+00:00,1942-09-01T09:30:00+00:00,1906-01-01T09:30:00+00:00
+1941-10-01T09:35:00+00:00,1942-09-01T09:35:00+00:00,1906-01-01T09:35:00+00:00
+1941-10-01T09:40:00+00:00,1942-09-01T09:40:00+00:00,1906-01-01T09:40:00+00:00
+1941-10-01T09:45:00+00:00,1942-09-01T09:45:00+00:00,1906-01-01T09:45:00+00:00
+1941-10-01T09:50:00+00:00,1942-09-01T09:50:00+00:00,1906-01-01T09:50:00+00:00
+1941-10-01T09:55:00+00:00,1942-09-01T09:55:00+00:00,1906-01-01T09:55:00+00:00
+1941-10-01T10:00:00+00:00,1942-09-01T10:00:00+00:00,1906-01-01T10:00:00+00:00
+1941-10-01T10:05:00+00:00,1942-09-01T10:05:00+00:00,1906-01-01T10:05:00+00:00
+1941-10-01T10:10:00+00:00,1942-09-01T10:10:00+00:00,1906-01-01T10:10:00+00:00
+1941-10-01T10:15:00+00:00,1942-09-01T10:15:00+00:00,1906-01-01T10:15:00+00:00
+1941-10-01T10:20:00+00:00,1942-09-01T10:20:00+00:00,1906-01-01T10:20:00+00:00
+1941-10-01T10:25:00+00:00,1942-09-01T10:25:00+00:00,1906-01-01T10:25:00+00:00
+1941-10-01T10:30:00+00:00,1942-09-01T10:30:00+00:00,1906-01-01T10:30:00+00:00
+1941-10-01T10:35:00+00:00,1942-09-01T10:35:00+00:00,1906-01-01T10:35:00+00:00
+1941-10-01T10:40:00+00:00,1942-09-01T10:40:00+00:00,1906-01-01T10:40:00+00:00
+1941-10-01T10:45:00+00:00,1942-09-01T10:45:00+00:00,1906-01-01T10:45:00+00:00
+1941-10-01T10:50:00+00:00,1942-09-01T10:50:00+00:00,1906-01-01T10:50:00+00:00
+1941-10-01T10:55:00+00:00,1942-09-01T10:55:00+00:00,1906-01-01T10:55:00+00:00
+1941-10-01T11:00:00+00:00,1942-09-01T11:00:00+00:00,1906-01-01T11:00:00+00:00
+1941-10-01T11:05:00+00:00,1942-09-01T11:05:00+00:00,1906-01-01T11:05:00+00:00
+1941-10-01T11:10:00+00:00,1942-09-01T11:10:00+00:00,1906-01-01T11:10:00+00:00
+1941-10-01T11:15:00+00:00,1942-09-01T11:15:00+00:00,1906-01-01T11:15:00+00:00
+1941-10-01T11:20:00+00:00,1942-09-01T11:20:00+00:00,1906-01-01T11:20:00+00:00
+1941-10-01T11:25:00+00:00,1942-09-01T11:25:00+00:00,1906-01-01T11:25:00+00:00
+1941-10-01T11:30:00+00:00,1942-09-01T11:30:00+00:00,1906-01-01T11:30:00+00:00
+1941-10-01T11:35:00+00:00,1942-09-01T11:35:00+00:00,1906-01-01T11:35:00+00:00
+1941-10-01T11:40:00+00:00,1942-09-01T11:40:00+00:00,1906-01-01T11:40:00+00:00
+1941-10-01T11:45:00+00:00,1942-09-01T11:45:00+00:00,1906-01-01T11:45:00+00:00
+1941-10-01T11:50:00+00:00,1942-09-01T11:50:00+00:00,1906-01-01T11:50:00+00:00
+1941-10-01T11:55:00+00:00,1942-09-01T11:55:00+00:00,1906-01-01T11:55:00+00:00
+1941-10-01T12:00:00+00:00,1942-09-01T12:00:00+00:00,1906-01-01T12:00:00+00:00
+1941-10-01T12:05:00+00:00,1942-09-01T12:05:00+00:00,1906-01-01T12:05:00+00:00
+1941-10-01T12:10:00+00:00,1942-09-01T12:10:00+00:00,1906-01-01T12:10:00+00:00
+1941-10-01T12:15:00+00:00,1942-09-01T12:15:00+00:00,1906-01-01T12:15:00+00:00
+1941-10-01T12:20:00+00:00,1942-09-01T12:20:00+00:00,1906-01-01T12:20:00+00:00
+1941-10-01T12:25:00+00:00,1942-09-01T12:25:00+00:00,1906-01-01T12:25:00+00:00
+1941-10-01T12:30:00+00:00,1942-09-01T12:30:00+00:00,1906-01-01T12:30:00+00:00
+1941-10-01T12:35:00+00:00,1942-09-01T12:35:00+00:00,1906-01-01T12:35:00+00:00
+1941-10-01T12:40:00+00:00,1942-09-01T12:40:00+00:00,1906-01-01T12:40:00+00:00
+1941-10-01T12:45:00+00:00,1942-09-01T12:45:00+00:00,1906-01-01T12:45:00+00:00
+1941-10-01T12:50:00+00:00,1942-09-01T12:50:00+00:00,1906-01-01T12:50:00+00:00
+1941-10-01T12:55:00+00:00,1942-09-01T12:55:00+00:00,1906-01-01T12:55:00+00:00
+1941-10-01T13:00:00+00:00,1942-09-01T13:00:00+00:00,1906-01-01T13:00:00+00:00
+1941-10-01T13:05:00+00:00,1942-09-01T13:05:00+00:00,1906-01-01T13:05:00+00:00
+1941-10-01T13:10:00+00:00,1942-09-01T13:10:00+00:00,1906-01-01T13:10:00+00:00
+1941-10-01T13:15:00+00:00,1942-09-01T13:15:00+00:00,1906-01-01T13:15:00+00:00
+1941-10-01T13:20:00+00:00,1942-09-01T13:20:00+00:00,1906-01-01T13:20:00+00:00
+1941-10-01T13:25:00+00:00,1942-09-01T13:25:00+00:00,1906-01-01T13:25:00+00:00
+1941-10-01T13:30:00+00:00,1942-09-01T13:30:00+00:00,1906-01-01T13:30:00+00:00
+1941-10-01T13:35:00+00:00,1942-09-01T13:35:00+00:00,1906-01-01T13:35:00+00:00
+1941-10-01T13:40:00+00:00,1942-09-01T13:40:00+00:00,1906-01-01T13:40:00+00:00
+1941-10-01T13:45:00+00:00,1942-09-01T13:45:00+00:00,1906-01-01T13:45:00+00:00
+1941-10-01T13:50:00+00:00,1942-09-01T13:50:00+00:00,1906-01-01T13:50:00+00:00
+1941-10-01T13:55:00+00:00,1942-09-01T13:55:00+00:00,1906-01-01T13:55:00+00:00
+1941-10-01T14:00:00+00:00,1942-09-01T14:00:00+00:00,1906-01-01T14:00:00+00:00
+1941-10-01T14:05:00+00:00,1942-09-01T14:05:00+00:00,1906-01-01T14:05:00+00:00
+1941-10-01T14:10:00+00:00,1942-09-01T14:10:00+00:00,1906-01-01T14:10:00+00:00
+1941-10-01T14:15:00+00:00,1942-09-01T14:15:00+00:00,1906-01-01T14:15:00+00:00
+1941-10-01T14:20:00+00:00,1942-09-01T14:20:00+00:00,1906-01-01T14:20:00+00:00
+1941-10-01T14:25:00+00:00,1942-09-01T14:25:00+00:00,1906-01-01T14:25:00+00:00
+1941-10-01T14:30:00+00:00,1942-09-01T14:30:00+00:00,1906-01-01T14:30:00+00:00
+1941-10-01T14:35:00+00:00,1942-09-01T14:35:00+00:00,1906-01-01T14:35:00+00:00
+1941-10-01T14:40:00+00:00,1942-09-01T14:40:00+00:00,1906-01-01T14:40:00+00:00
+1941-10-01T14:45:00+00:00,1942-09-01T14:45:00+00:00,1906-01-01T14:45:00+00:00
+1941-10-01T14:50:00+00:00,1942-09-01T14:50:00+00:00,1906-01-01T14:50:00+00:00
+1941-10-01T14:55:00+00:00,1942-09-01T14:55:00+00:00,1906-01-01T14:55:00+00:00
+1941-10-01T15:00:00+00:00,1942-09-01T15:00:00+00:00,1906-01-01T15:00:00+00:00
+1941-10-01T15:05:00+00:00,1942-09-01T15:05:00+00:00,1906-01-01T15:05:00+00:00
+1941-10-01T15:10:00+00:00,1942-09-01T15:10:00+00:00,1906-01-01T15:10:00+00:00
+1941-10-01T15:15:00+00:00,1942-09-01T15:15:00+00:00,1906-01-01T15:15:00+00:00
+1941-10-01T15:20:00+00:00,1942-09-01T15:20:00+00:00,1906-01-01T15:20:00+00:00
+1941-10-01T15:25:00+00:00,1942-09-01T15:25:00+00:00,1906-01-01T15:25:00+00:00
+1941-10-01T15:30:00+00:00,1942-09-01T15:30:00+00:00,1906-01-01T15:30:00+00:00
+1941-10-01T15:35:00+00:00,1942-09-01T15:35:00+00:00,1906-01-01T15:35:00+00:00
+1941-10-01T15:40:00+00:00,1942-09-01T15:40:00+00:00,1906-01-01T15:40:00+00:00
+1941-10-01T15:45:00+00:00,1942-09-01T15:45:00+00:00,1906-01-01T15:45:00+00:00
+1941-10-01T15:50:00+00:00,1942-09-01T15:50:00+00:00,1906-01-01T15:50:00+00:00
+1941-10-01T15:55:00+00:00,1942-09-01T15:55:00+00:00,1906-01-01T15:55:00+00:00
+1941-10-01T16:00:00+00:00,1942-09-01T16:00:00+00:00,1906-01-01T16:00:00+00:00
+1941-10-01T16:05:00+00:00,1942-09-01T16:05:00+00:00,1906-01-01T16:05:00+00:00
+1941-10-01T16:10:00+00:00,1942-09-01T16:10:00+00:00,1906-01-01T16:10:00+00:00
+1941-10-01T16:15:00+00:00,1942-09-01T16:15:00+00:00,1906-01-01T16:15:00+00:00
+1941-10-01T16:20:00+00:00,1942-09-01T16:20:00+00:00,1906-01-01T16:20:00+00:00
+1941-10-01T16:25:00+00:00,1942-09-01T16:25:00+00:00,1906-01-01T16:25:00+00:00
+1941-10-01T16:30:00+00:00,1942-09-01T16:30:00+00:00,1906-01-01T16:30:00+00:00
+1941-10-01T16:35:00+00:00,1942-09-01T16:35:00+00:00,1906-01-01T16:35:00+00:00
+1941-10-01T16:40:00+00:00,1942-09-01T16:40:00+00:00,1906-01-01T16:40:00+00:00
+1941-10-01T16:45:00+00:00,1942-09-01T16:45:00+00:00,1906-01-01T16:45:00+00:00
+1941-10-01T16:50:00+00:00,1942-09-01T16:50:00+00:00,1906-01-01T16:50:00+00:00
+1941-10-01T16:55:00+00:00,1942-09-01T16:55:00+00:00,1906-01-01T16:55:00+00:00
+1941-10-01T17:00:00+00:00,1942-09-01T17:00:00+00:00,1906-01-01T17:00:00+00:00
+1941-10-01T17:05:00+00:00,1942-09-01T17:05:00+00:00,1906-01-01T17:05:00+00:00
+1941-10-01T17:10:00+00:00,1942-09-01T17:10:00+00:00,1906-01-01T17:10:00+00:00
+1941-10-01T17:15:00+00:00,1942-09-01T17:15:00+00:00,1906-01-01T17:15:00+00:00
+1941-10-01T17:20:00+00:00,1942-09-01T17:20:00+00:00,1906-01-01T17:20:00+00:00
+1941-10-01T17:25:00+00:00,1942-09-01T17:25:00+00:00,1906-01-01T17:25:00+00:00
+1941-10-01T17:30:00+00:00,1942-09-01T17:30:00+00:00,1906-01-01T17:30:00+00:00
+1941-10-01T17:35:00+00:00,1942-09-01T17:35:00+00:00,1906-01-01T17:35:00+00:00
+1941-10-01T17:40:00+00:00,1942-09-01T17:40:00+00:00,1906-01-01T17:40:00+00:00
+1941-10-01T17:45:00+00:00,1942-09-01T17:45:00+00:00,1906-01-01T17:45:00+00:00
+1941-10-01T17:50:00+00:00,1942-09-01T17:50:00+00:00,1906-01-01T17:50:00+00:00
+1941-10-01T17:55:00+00:00,1942-09-01T17:55:00+00:00,1906-01-01T17:55:00+00:00
+1941-10-01T18:00:00+00:00,1942-09-01T18:00:00+00:00,1906-01-01T18:00:00+00:00
+1941-10-01T18:05:00+00:00,1942-09-01T18:05:00+00:00,1906-01-01T18:05:00+00:00
+1941-10-01T18:10:00+00:00,1942-09-01T18:10:00+00:00,1906-01-01T18:10:00+00:00
+1941-10-01T18:15:00+00:00,1942-09-01T18:15:00+00:00,1906-01-01T18:15:00+00:00
+1941-10-01T18:20:00+00:00,1942-09-01T18:20:00+00:00,1906-01-01T18:20:00+00:00
+1941-10-01T18:25:00+00:00,1942-09-01T18:25:00+00:00,1906-01-01T18:25:00+00:00
+1941-10-01T18:30:00+00:00,1942-09-01T18:30:00+00:00,1906-01-01T18:30:00+00:00
+1941-10-01T18:35:00+00:00,1942-09-01T18:35:00+00:00,1906-01-01T18:35:00+00:00
+1941-10-01T18:40:00+00:00,1942-09-01T18:40:00+00:00,1906-01-01T18:40:00+00:00
+1941-10-01T18:45:00+00:00,1942-09-01T18:45:00+00:00,1906-01-01T18:45:00+00:00
+1941-10-01T18:50:00+00:00,1942-09-01T18:50:00+00:00,1906-01-01T18:50:00+00:00
+1941-10-01T18:55:00+00:00,1942-09-01T18:55:00+00:00,1906-01-01T18:55:00+00:00
+1941-10-01T19:00:00+00:00,1942-09-01T19:00:00+00:00,1906-01-01T19:00:00+00:00
+1941-10-01T19:05:00+00:00,1942-09-01T19:05:00+00:00,1906-01-01T19:05:00+00:00
+1941-10-01T19:10:00+00:00,1942-09-01T19:10:00+00:00,1906-01-01T19:10:00+00:00
+1941-10-01T19:15:00+00:00,1942-09-01T19:15:00+00:00,1906-01-01T19:15:00+00:00
+1941-10-01T19:20:00+00:00,1942-09-01T19:20:00+00:00,1906-01-01T19:20:00+00:00
+1941-10-01T19:25:00+00:00,1942-09-01T19:25:00+00:00,1906-01-01T19:25:00+00:00
+1941-10-01T19:30:00+00:00,1942-09-01T19:30:00+00:00,1906-01-01T19:30:00+00:00
+1941-10-01T19:35:00+00:00,1942-09-01T19:35:00+00:00,1906-01-01T19:35:00+00:00
+1941-10-01T19:40:00+00:00,1942-09-01T19:40:00+00:00,1906-01-01T19:40:00+00:00
+1941-10-01T19:45:00+00:00,1942-09-01T19:45:00+00:00,1906-01-01T19:45:00+00:00
+1941-10-01T19:50:00+00:00,1942-09-01T19:50:00+00:00,1906-01-01T19:50:00+00:00
+1941-10-01T19:55:00+00:00,1942-09-01T19:55:00+00:00,1906-01-01T19:55:00+00:00
+1941-10-01T20:00:00+00:00,1942-09-01T20:00:00+00:00,1906-01-01T20:00:00+00:00
+1941-10-01T20:05:00+00:00,1942-09-01T20:05:00+00:00,1906-01-01T20:05:00+00:00
+1941-10-01T20:10:00+00:00,1942-09-01T20:10:00+00:00,1906-01-01T20:10:00+00:00
+1941-10-01T20:15:00+00:00,1942-09-01T20:15:00+00:00,1906-01-01T20:15:00+00:00
+1941-10-01T20:20:00+00:00,1942-09-01T20:20:00+00:00,1906-01-01T20:20:00+00:00
+1941-10-01T20:25:00+00:00,1942-09-01T20:25:00+00:00,1906-01-01T20:25:00+00:00
+1941-10-01T20:30:00+00:00,1942-09-01T20:30:00+00:00,1906-01-01T20:30:00+00:00
+1941-10-01T20:35:00+00:00,1942-09-01T20:35:00+00:00,1906-01-01T20:35:00+00:00
+1941-10-01T20:40:00+00:00,1942-09-01T20:40:00+00:00,1906-01-01T20:40:00+00:00
+1941-10-01T20:45:00+00:00,1942-09-01T20:45:00+00:00,1906-01-01T20:45:00+00:00
+1941-10-01T20:50:00+00:00,1942-09-01T20:50:00+00:00,1906-01-01T20:50:00+00:00
+1941-10-01T20:55:00+00:00,1942-09-01T20:55:00+00:00,1906-01-01T20:55:00+00:00
+1941-10-01T21:00:00+00:00,1942-09-01T21:00:00+00:00,1906-01-01T21:00:00+00:00
+1941-10-01T21:05:00+00:00,1942-09-01T21:05:00+00:00,1906-01-01T21:05:00+00:00
+1941-10-01T21:10:00+00:00,1942-09-01T21:10:00+00:00,1906-01-01T21:10:00+00:00
+1941-10-01T21:15:00+00:00,1942-09-01T21:15:00+00:00,1906-01-01T21:15:00+00:00
+1941-10-01T21:20:00+00:00,1942-09-01T21:20:00+00:00,1906-01-01T21:20:00+00:00
+1941-10-01T21:25:00+00:00,1942-09-01T21:25:00+00:00,1906-01-01T21:25:00+00:00
+1941-10-01T21:30:00+00:00,1942-09-01T21:30:00+00:00,1906-01-01T21:30:00+00:00
+1941-10-01T21:35:00+00:00,1942-09-01T21:35:00+00:00,1906-01-01T21:35:00+00:00
+1941-10-01T21:40:00+00:00,1942-09-01T21:40:00+00:00,1906-01-01T21:40:00+00:00
+1941-10-01T21:45:00+00:00,1942-09-01T21:45:00+00:00,1906-01-01T21:45:00+00:00
+1941-10-01T21:50:00+00:00,1942-09-01T21:50:00+00:00,1906-01-01T21:50:00+00:00
+1941-10-01T21:55:00+00:00,1942-09-01T21:55:00+00:00,1906-01-01T21:55:00+00:00
+1941-10-01T22:00:00+00:00,1942-09-01T22:00:00+00:00,1906-01-01T22:00:00+00:00
+1941-10-01T22:05:00+00:00,1942-09-01T22:05:00+00:00,1906-01-01T22:05:00+00:00
+1941-10-01T22:10:00+00:00,1942-09-01T22:10:00+00:00,1906-01-01T22:10:00+00:00
+1941-10-01T22:15:00+00:00,1942-09-01T22:15:00+00:00,1906-01-01T22:15:00+00:00
+1941-10-01T22:20:00+00:00,1942-09-01T22:20:00+00:00,1906-01-01T22:20:00+00:00
+1941-10-01T22:25:00+00:00,1942-09-01T22:25:00+00:00,1906-01-01T22:25:00+00:00
+1941-10-01T22:30:00+00:00,1942-09-01T22:30:00+00:00,1906-01-01T22:30:00+00:00
+1941-10-01T22:35:00+00:00,1942-09-01T22:35:00+00:00,1906-01-01T22:35:00+00:00
+1941-10-01T22:40:00+00:00,1942-09-01T22:40:00+00:00,1906-01-01T22:40:00+00:00
+1941-10-01T22:45:00+00:00,1942-09-01T22:45:00+00:00,1906-01-01T22:45:00+00:00
+1941-10-01T22:50:00+00:00,1942-09-01T22:50:00+00:00,1906-01-01T22:50:00+00:00
+1941-10-01T22:55:00+00:00,1942-09-01T22:55:00+00:00,1906-01-01T22:55:00+00:00
+1941-10-01T23:00:00+00:00,1942-09-01T23:00:00+00:00,1906-01-01T23:00:00+00:00
+1941-10-01T23:05:00+00:00,1942-09-01T23:05:00+00:00,1906-01-01T23:05:00+00:00
+1941-10-01T23:10:00+00:00,1942-09-01T23:10:00+00:00,1906-01-01T23:10:00+00:00
+1941-10-01T23:15:00+00:00,1942-09-01T23:15:00+00:00,1906-01-01T23:15:00+00:00
+1941-10-01T23:20:00+00:00,1942-09-01T23:20:00+00:00,1906-01-01T23:20:00+00:00
+1941-10-01T23:25:00+00:00,1942-09-01T23:25:00+00:00,1906-01-01T23:25:00+00:00
+1941-10-01T23:30:00+00:00,1942-09-01T23:30:00+00:00,1906-01-01T23:30:00+00:00
+1941-10-01T23:35:00+00:00,1942-09-01T23:35:00+00:00,1906-01-01T23:35:00+00:00
+1941-10-01T23:40:00+00:00,1942-09-01T23:40:00+00:00,1906-01-01T23:40:00+00:00
+1941-10-01T23:45:00+00:00,1942-09-01T23:45:00+00:00,1906-01-01T23:45:00+00:00
+1941-10-01T23:50:00+00:00,1942-09-01T23:50:00+00:00,1906-01-01T23:50:00+00:00
+1941-10-01T23:55:00+00:00,1942-09-01T23:55:00+00:00,1906-01-01T23:55:00+00:00
+1941-10-02,1942-09-02,1906-01-02
+1941-10-02T00:05:00+00:00,1942-09-02T00:05:00+00:00,1906-01-02T00:05:00+00:00
+1941-10-02T00:10:00+00:00,1942-09-02T00:10:00+00:00,1906-01-02T00:10:00+00:00
+1941-10-02T00:15:00+00:00,1942-09-02T00:15:00+00:00,1906-01-02T00:15:00+00:00
+1941-10-02T00:20:00+00:00,1942-09-02T00:20:00+00:00,1906-01-02T00:20:00+00:00
+1941-10-02T00:25:00+00:00,1942-09-02T00:25:00+00:00,1906-01-02T00:25:00+00:00
+1941-10-02T00:30:00+00:00,1942-09-02T00:30:00+00:00,1906-01-02T00:30:00+00:00
+1941-10-02T00:35:00+00:00,1942-09-02T00:35:00+00:00,1906-01-02T00:35:00+00:00
+1941-10-02T00:40:00+00:00,1942-09-02T00:40:00+00:00,1906-01-02T00:40:00+00:00
+1941-10-02T00:45:00+00:00,1942-09-02T00:45:00+00:00,1906-01-02T00:45:00+00:00
+1941-10-02T00:50:00+00:00,1942-09-02T00:50:00+00:00,1906-01-02T00:50:00+00:00
+1941-10-02T00:55:00+00:00,1942-09-02T00:55:00+00:00,1906-01-02T00:55:00+00:00
+1941-10-02T01:00:00+00:00,1942-09-02T01:00:00+00:00,1906-01-02T01:00:00+00:00
+1941-10-02T01:05:00+00:00,1942-09-02T01:05:00+00:00,1906-01-02T01:05:00+00:00
+1941-10-02T01:10:00+00:00,1942-09-02T01:10:00+00:00,1906-01-02T01:10:00+00:00
+1941-10-02T01:15:00+00:00,1942-09-02T01:15:00+00:00,1906-01-02T01:15:00+00:00
+1941-10-02T01:20:00+00:00,1942-09-02T01:20:00+00:00,1906-01-02T01:20:00+00:00
+1941-10-02T01:25:00+00:00,1942-09-02T01:25:00+00:00,1906-01-02T01:25:00+00:00
+1941-10-02T01:30:00+00:00,1942-09-02T01:30:00+00:00,1906-01-02T01:30:00+00:00
+1941-10-02T01:35:00+00:00,1942-09-02T01:35:00+00:00,1906-01-02T01:35:00+00:00
+1941-10-02T01:40:00+00:00,1942-09-02T01:40:00+00:00,1906-01-02T01:40:00+00:00
+1941-10-02T01:45:00+00:00,1942-09-02T01:45:00+00:00,1906-01-02T01:45:00+00:00
+1941-10-02T01:50:00+00:00,1942-09-02T01:50:00+00:00,1906-01-02T01:50:00+00:00
+1941-10-02T01:55:00+00:00,1942-09-02T01:55:00+00:00,1906-01-02T01:55:00+00:00
+1941-10-02T02:00:00+00:00,1942-09-02T02:00:00+00:00,1906-01-02T02:00:00+00:00
+1941-10-02T02:05:00+00:00,1942-09-02T02:05:00+00:00,1906-01-02T02:05:00+00:00
+1941-10-02T02:10:00+00:00,1942-09-02T02:10:00+00:00,1906-01-02T02:10:00+00:00
+1941-10-02T02:15:00+00:00,1942-09-02T02:15:00+00:00,1906-01-02T02:15:00+00:00
+1941-10-02T02:20:00+00:00,1942-09-02T02:20:00+00:00,1906-01-02T02:20:00+00:00
+1941-10-02T02:25:00+00:00,1942-09-02T02:25:00+00:00,1906-01-02T02:25:00+00:00
+1941-10-02T02:30:00+00:00,1942-09-02T02:30:00+00:00,1906-01-02T02:30:00+00:00
+1941-10-02T02:35:00+00:00,1942-09-02T02:35:00+00:00,1906-01-02T02:35:00+00:00
+1941-10-02T02:40:00+00:00,1942-09-02T02:40:00+00:00,1906-01-02T02:40:00+00:00
+1941-10-02T02:45:00+00:00,1942-09-02T02:45:00+00:00,1906-01-02T02:45:00+00:00
+1941-10-02T02:50:00+00:00,1942-09-02T02:50:00+00:00,1906-01-02T02:50:00+00:00
+1941-10-02T02:55:00+00:00,1942-09-02T02:55:00+00:00,1906-01-02T02:55:00+00:00
+1941-10-02T03:00:00+00:00,1942-09-02T03:00:00+00:00,1906-01-02T03:00:00+00:00
+1941-10-02T03:05:00+00:00,1942-09-02T03:05:00+00:00,1906-01-02T03:05:00+00:00
+1941-10-02T03:10:00+00:00,1942-09-02T03:10:00+00:00,1906-01-02T03:10:00+00:00
+1941-10-02T03:15:00+00:00,1942-09-02T03:15:00+00:00,1906-01-02T03:15:00+00:00
+1941-10-02T03:20:00+00:00,1942-09-02T03:20:00+00:00,1906-01-02T03:20:00+00:00
+1941-10-02T03:25:00+00:00,1942-09-02T03:25:00+00:00,1906-01-02T03:25:00+00:00
+1941-10-02T03:30:00+00:00,1942-09-02T03:30:00+00:00,1906-01-02T03:30:00+00:00
+1941-10-02T03:35:00+00:00,1942-09-02T03:35:00+00:00,1906-01-02T03:35:00+00:00
+1941-10-02T03:40:00+00:00,1942-09-02T03:40:00+00:00,1906-01-02T03:40:00+00:00
+1941-10-02T03:45:00+00:00,1942-09-02T03:45:00+00:00,1906-01-02T03:45:00+00:00
+1941-10-02T03:50:00+00:00,1942-09-02T03:50:00+00:00,1906-01-02T03:50:00+00:00
+1941-10-02T03:55:00+00:00,1942-09-02T03:55:00+00:00,1906-01-02T03:55:00+00:00
+1941-10-02T04:00:00+00:00,1942-09-02T04:00:00+00:00,1906-01-02T04:00:00+00:00
+1941-10-02T04:05:00+00:00,1942-09-02T04:05:00+00:00,1906-01-02T04:05:00+00:00
+1941-10-02T04:10:00+00:00,1942-09-02T04:10:00+00:00,1906-01-02T04:10:00+00:00
+1941-10-02T04:15:00+00:00,1942-09-02T04:15:00+00:00,1906-01-02T04:15:00+00:00
+1941-10-02T04:20:00+00:00,1942-09-02T04:20:00+00:00,1906-01-02T04:20:00+00:00
+1941-10-02T04:25:00+00:00,1942-09-02T04:25:00+00:00,1906-01-02T04:25:00+00:00
+1941-10-02T04:30:00+00:00,1942-09-02T04:30:00+00:00,1906-01-02T04:30:00+00:00
+1941-10-02T04:35:00+00:00,1942-09-02T04:35:00+00:00,1906-01-02T04:35:00+00:00
+1941-10-02T04:40:00+00:00,1942-09-02T04:40:00+00:00,1906-01-02T04:40:00+00:00
+1941-10-02T04:45:00+00:00,1942-09-02T04:45:00+00:00,1906-01-02T04:45:00+00:00
+1941-10-02T04:50:00+00:00,1942-09-02T04:50:00+00:00,1906-01-02T04:50:00+00:00
+1941-10-02T04:55:00+00:00,1942-09-02T04:55:00+00:00,1906-01-02T04:55:00+00:00
+1941-10-02T05:00:00+00:00,1942-09-02T05:00:00+00:00,1906-01-02T05:00:00+00:00
+1941-10-02T05:05:00+00:00,1942-09-02T05:05:00+00:00,1906-01-02T05:05:00+00:00
+1941-10-02T05:10:00+00:00,1942-09-02T05:10:00+00:00,1906-01-02T05:10:00+00:00
+1941-10-02T05:15:00+00:00,1942-09-02T05:15:00+00:00,1906-01-02T05:15:00+00:00
+1941-10-02T05:20:00+00:00,1942-09-02T05:20:00+00:00,1906-01-02T05:20:00+00:00
+1941-10-02T05:25:00+00:00,1942-09-02T05:25:00+00:00,1906-01-02T05:25:00+00:00
+1941-10-02T05:30:00+00:00,1942-09-02T05:30:00+00:00,1906-01-02T05:30:00+00:00
+1941-10-02T05:35:00+00:00,1942-09-02T05:35:00+00:00,1906-01-02T05:35:00+00:00
+1941-10-02T05:40:00+00:00,1942-09-02T05:40:00+00:00,1906-01-02T05:40:00+00:00
+1941-10-02T05:45:00+00:00,1942-09-02T05:45:00+00:00,1906-01-02T05:45:00+00:00
+1941-10-02T05:50:00+00:00,1942-09-02T05:50:00+00:00,1906-01-02T05:50:00+00:00
+1941-10-02T05:55:00+00:00,1942-09-02T05:55:00+00:00,1906-01-02T05:55:00+00:00
+1941-10-02T06:00:00+00:00,1942-09-02T06:00:00+00:00,1906-01-02T06:00:00+00:00
+1941-10-02T06:05:00+00:00,1942-09-02T06:05:00+00:00,1906-01-02T06:05:00+00:00
+1941-10-02T06:10:00+00:00,1942-09-02T06:10:00+00:00,1906-01-02T06:10:00+00:00
+1941-10-02T06:15:00+00:00,1942-09-02T06:15:00+00:00,1906-01-02T06:15:00+00:00
+1941-10-02T06:20:00+00:00,1942-09-02T06:20:00+00:00,1906-01-02T06:20:00+00:00
+1941-10-02T06:25:00+00:00,1942-09-02T06:25:00+00:00,1906-01-02T06:25:00+00:00
+1941-10-02T06:30:00+00:00,1942-09-02T06:30:00+00:00,1906-01-02T06:30:00+00:00
+1941-10-02T06:35:00+00:00,1942-09-02T06:35:00+00:00,1906-01-02T06:35:00+00:00
+1941-10-02T06:40:00+00:00,1942-09-02T06:40:00+00:00,1906-01-02T06:40:00+00:00
+1941-10-02T06:45:00+00:00,1942-09-02T06:45:00+00:00,1906-01-02T06:45:00+00:00
+1941-10-02T06:50:00+00:00,1942-09-02T06:50:00+00:00,1906-01-02T06:50:00+00:00
+1941-10-02T06:55:00+00:00,1942-09-02T06:55:00+00:00,1906-01-02T06:55:00+00:00
+1941-10-02T07:00:00+00:00,1942-09-02T07:00:00+00:00,1906-01-02T07:00:00+00:00
+1941-10-02T07:05:00+00:00,1942-09-02T07:05:00+00:00,1906-01-02T07:05:00+00:00
+1941-10-02T07:10:00+00:00,1942-09-02T07:10:00+00:00,1906-01-02T07:10:00+00:00
+1941-10-02T07:15:00+00:00,1942-09-02T07:15:00+00:00,1906-01-02T07:15:00+00:00
+1941-10-02T07:20:00+00:00,1942-09-02T07:20:00+00:00,1906-01-02T07:20:00+00:00
+1941-10-02T07:25:00+00:00,1942-09-02T07:25:00+00:00,1906-01-02T07:25:00+00:00
+1941-10-02T07:30:00+00:00,1942-09-02T07:30:00+00:00,1906-01-02T07:30:00+00:00
+1941-10-02T07:35:00+00:00,1942-09-02T07:35:00+00:00,1906-01-02T07:35:00+00:00
+1941-10-02T07:40:00+00:00,1942-09-02T07:40:00+00:00,1906-01-02T07:40:00+00:00
+1941-10-02T07:45:00+00:00,1942-09-02T07:45:00+00:00,1906-01-02T07:45:00+00:00
+1941-10-02T07:50:00+00:00,1942-09-02T07:50:00+00:00,1906-01-02T07:50:00+00:00
+1941-10-02T07:55:00+00:00,1942-09-02T07:55:00+00:00,1906-01-02T07:55:00+00:00
+1941-10-02T08:00:00+00:00,1942-09-02T08:00:00+00:00,1906-01-02T08:00:00+00:00
+1941-10-02T08:05:00+00:00,1942-09-02T08:05:00+00:00,1906-01-02T08:05:00+00:00
+1941-10-02T08:10:00+00:00,1942-09-02T08:10:00+00:00,1906-01-02T08:10:00+00:00
+1941-10-02T08:15:00+00:00,1942-09-02T08:15:00+00:00,1906-01-02T08:15:00+00:00
+1941-10-02T08:20:00+00:00,1942-09-02T08:20:00+00:00,1906-01-02T08:20:00+00:00
+1941-10-02T08:25:00+00:00,1942-09-02T08:25:00+00:00,1906-01-02T08:25:00+00:00
+1941-10-02T08:30:00+00:00,1942-09-02T08:30:00+00:00,1906-01-02T08:30:00+00:00
+1941-10-02T08:35:00+00:00,1942-09-02T08:35:00+00:00,1906-01-02T08:35:00+00:00
+1941-10-02T08:40:00+00:00,1942-09-02T08:40:00+00:00,1906-01-02T08:40:00+00:00
+1941-10-02T08:45:00+00:00,1942-09-02T08:45:00+00:00,1906-01-02T08:45:00+00:00
+1941-10-02T08:50:00+00:00,1942-09-02T08:50:00+00:00,1906-01-02T08:50:00+00:00
+1941-10-02T08:55:00+00:00,1942-09-02T08:55:00+00:00,1906-01-02T08:55:00+00:00
+1941-10-02T09:00:00+00:00,1942-09-02T09:00:00+00:00,1906-01-02T09:00:00+00:00
+1941-10-02T09:05:00+00:00,1942-09-02T09:05:00+00:00,1906-01-02T09:05:00+00:00
+1941-10-02T09:10:00+00:00,1942-09-02T09:10:00+00:00,1906-01-02T09:10:00+00:00
+1941-10-02T09:15:00+00:00,1942-09-02T09:15:00+00:00,1906-01-02T09:15:00+00:00
+1941-10-02T09:20:00+00:00,1942-09-02T09:20:00+00:00,1906-01-02T09:20:00+00:00
+1941-10-02T09:25:00+00:00,1942-09-02T09:25:00+00:00,1906-01-02T09:25:00+00:00
+1941-10-02T09:30:00+00:00,1942-09-02T09:30:00+00:00,1906-01-02T09:30:00+00:00
+1941-10-02T09:35:00+00:00,1942-09-02T09:35:00+00:00,1906-01-02T09:35:00+00:00
+1941-10-02T09:40:00+00:00,1942-09-02T09:40:00+00:00,1906-01-02T09:40:00+00:00
+1941-10-02T09:45:00+00:00,1942-09-02T09:45:00+00:00,1906-01-02T09:45:00+00:00
+1941-10-02T09:50:00+00:00,1942-09-02T09:50:00+00:00,1906-01-02T09:50:00+00:00
+1941-10-02T09:55:00+00:00,1942-09-02T09:55:00+00:00,1906-01-02T09:55:00+00:00
+1941-10-02T10:00:00+00:00,1942-09-02T10:00:00+00:00,1906-01-02T10:00:00+00:00
+1941-10-02T10:05:00+00:00,1942-09-02T10:05:00+00:00,1906-01-02T10:05:00+00:00
+1941-10-02T10:10:00+00:00,1942-09-02T10:10:00+00:00,1906-01-02T10:10:00+00:00
+1941-10-02T10:15:00+00:00,1942-09-02T10:15:00+00:00,1906-01-02T10:15:00+00:00
+1941-10-02T10:20:00+00:00,1942-09-02T10:20:00+00:00,1906-01-02T10:20:00+00:00
+1941-10-02T10:25:00+00:00,1942-09-02T10:25:00+00:00,1906-01-02T10:25:00+00:00
+1941-10-02T10:30:00+00:00,1942-09-02T10:30:00+00:00,1906-01-02T10:30:00+00:00
+1941-10-02T10:35:00+00:00,1942-09-02T10:35:00+00:00,1906-01-02T10:35:00+00:00
+1941-10-02T10:40:00+00:00,1942-09-02T10:40:00+00:00,1906-01-02T10:40:00+00:00
+1941-10-02T10:45:00+00:00,1942-09-02T10:45:00+00:00,1906-01-02T10:45:00+00:00
+1941-10-02T10:50:00+00:00,1942-09-02T10:50:00+00:00,1906-01-02T10:50:00+00:00
+1941-10-02T10:55:00+00:00,1942-09-02T10:55:00+00:00,1906-01-02T10:55:00+00:00
+1941-10-02T11:00:00+00:00,1942-09-02T11:00:00+00:00,1906-01-02T11:00:00+00:00
+1941-10-02T11:05:00+00:00,1942-09-02T11:05:00+00:00,1906-01-02T11:05:00+00:00
+1941-10-02T11:10:00+00:00,1942-09-02T11:10:00+00:00,1906-01-02T11:10:00+00:00
+1941-10-02T11:15:00+00:00,1942-09-02T11:15:00+00:00,1906-01-02T11:15:00+00:00
+1941-10-02T11:20:00+00:00,1942-09-02T11:20:00+00:00,1906-01-02T11:20:00+00:00
+1941-10-02T11:25:00+00:00,1942-09-02T11:25:00+00:00,1906-01-02T11:25:00+00:00
+1941-10-02T11:30:00+00:00,1942-09-02T11:30:00+00:00,1906-01-02T11:30:00+00:00
+1941-10-02T11:35:00+00:00,1942-09-02T11:35:00+00:00,1906-01-02T11:35:00+00:00
+1941-10-02T11:40:00+00:00,1942-09-02T11:40:00+00:00,1906-01-02T11:40:00+00:00
+1941-10-02T11:45:00+00:00,1942-09-02T11:45:00+00:00,1906-01-02T11:45:00+00:00
+1941-10-02T11:50:00+00:00,1942-09-02T11:50:00+00:00,1906-01-02T11:50:00+00:00
+1941-10-02T11:55:00+00:00,1942-09-02T11:55:00+00:00,1906-01-02T11:55:00+00:00
+1941-10-02T12:00:00+00:00,1942-09-02T12:00:00+00:00,1906-01-02T12:00:00+00:00
+1941-10-02T12:05:00+00:00,1942-09-02T12:05:00+00:00,1906-01-02T12:05:00+00:00
+1941-10-02T12:10:00+00:00,1942-09-02T12:10:00+00:00,1906-01-02T12:10:00+00:00
+1941-10-02T12:15:00+00:00,1942-09-02T12:15:00+00:00,1906-01-02T12:15:00+00:00
+1941-10-02T12:20:00+00:00,1942-09-02T12:20:00+00:00,1906-01-02T12:20:00+00:00
+1941-10-02T12:25:00+00:00,1942-09-02T12:25:00+00:00,1906-01-02T12:25:00+00:00
+1941-10-02T12:30:00+00:00,1942-09-02T12:30:00+00:00,1906-01-02T12:30:00+00:00
+1941-10-02T12:35:00+00:00,1942-09-02T12:35:00+00:00,1906-01-02T12:35:00+00:00
+1941-10-02T12:40:00+00:00,1942-09-02T12:40:00+00:00,1906-01-02T12:40:00+00:00
+1941-10-02T12:45:00+00:00,1942-09-02T12:45:00+00:00,1906-01-02T12:45:00+00:00
+1941-10-02T12:50:00+00:00,1942-09-02T12:50:00+00:00,1906-01-02T12:50:00+00:00
+1941-10-02T12:55:00+00:00,1942-09-02T12:55:00+00:00,1906-01-02T12:55:00+00:00
+1941-10-02T13:00:00+00:00,1942-09-02T13:00:00+00:00,1906-01-02T13:00:00+00:00
+1941-10-02T13:05:00+00:00,1942-09-02T13:05:00+00:00,1906-01-02T13:05:00+00:00
+1941-10-02T13:10:00+00:00,1942-09-02T13:10:00+00:00,1906-01-02T13:10:00+00:00
+1941-10-02T13:15:00+00:00,1942-09-02T13:15:00+00:00,1906-01-02T13:15:00+00:00
+1941-10-02T13:20:00+00:00,1942-09-02T13:20:00+00:00,1906-01-02T13:20:00+00:00
+1941-10-02T13:25:00+00:00,1942-09-02T13:25:00+00:00,1906-01-02T13:25:00+00:00
+1941-10-02T13:30:00+00:00,1942-09-02T13:30:00+00:00,1906-01-02T13:30:00+00:00
+1941-10-02T13:35:00+00:00,1942-09-02T13:35:00+00:00,1906-01-02T13:35:00+00:00
+1941-10-02T13:40:00+00:00,1942-09-02T13:40:00+00:00,1906-01-02T13:40:00+00:00
+1941-10-02T13:45:00+00:00,1942-09-02T13:45:00+00:00,1906-01-02T13:45:00+00:00
+1941-10-02T13:50:00+00:00,1942-09-02T13:50:00+00:00,1906-01-02T13:50:00+00:00
+1941-10-02T13:55:00+00:00,1942-09-02T13:55:00+00:00,1906-01-02T13:55:00+00:00
+1941-10-02T14:00:00+00:00,1942-09-02T14:00:00+00:00,1906-01-02T14:00:00+00:00
+1941-10-02T14:05:00+00:00,1942-09-02T14:05:00+00:00,1906-01-02T14:05:00+00:00
+1941-10-02T14:10:00+00:00,1942-09-02T14:10:00+00:00,1906-01-02T14:10:00+00:00
+1941-10-02T14:15:00+00:00,1942-09-02T14:15:00+00:00,1906-01-02T14:15:00+00:00
+1941-10-02T14:20:00+00:00,1942-09-02T14:20:00+00:00,1906-01-02T14:20:00+00:00
+1941-10-02T14:25:00+00:00,1942-09-02T14:25:00+00:00,1906-01-02T14:25:00+00:00
+1941-10-02T14:30:00+00:00,1942-09-02T14:30:00+00:00,1906-01-02T14:30:00+00:00
+1941-10-02T14:35:00+00:00,1942-09-02T14:35:00+00:00,1906-01-02T14:35:00+00:00
+1941-10-02T14:40:00+00:00,1942-09-02T14:40:00+00:00,1906-01-02T14:40:00+00:00
+1941-10-02T14:45:00+00:00,1942-09-02T14:45:00+00:00,1906-01-02T14:45:00+00:00
+1941-10-02T14:50:00+00:00,1942-09-02T14:50:00+00:00,1906-01-02T14:50:00+00:00
+1941-10-02T14:55:00+00:00,1942-09-02T14:55:00+00:00,1906-01-02T14:55:00+00:00
+1941-10-02T15:00:00+00:00,1942-09-02T15:00:00+00:00,1906-01-02T15:00:00+00:00
+1941-10-02T15:05:00+00:00,1942-09-02T15:05:00+00:00,1906-01-02T15:05:00+00:00
+1941-10-02T15:10:00+00:00,1942-09-02T15:10:00+00:00,1906-01-02T15:10:00+00:00
+1941-10-02T15:15:00+00:00,1942-09-02T15:15:00+00:00,1906-01-02T15:15:00+00:00
+1941-10-02T15:20:00+00:00,1942-09-02T15:20:00+00:00,1906-01-02T15:20:00+00:00
+1941-10-02T15:25:00+00:00,1942-09-02T15:25:00+00:00,1906-01-02T15:25:00+00:00
+1941-10-02T15:30:00+00:00,1942-09-02T15:30:00+00:00,1906-01-02T15:30:00+00:00
+1941-10-02T15:35:00+00:00,1942-09-02T15:35:00+00:00,1906-01-02T15:35:00+00:00
+1941-10-02T15:40:00+00:00,1942-09-02T15:40:00+00:00,1906-01-02T15:40:00+00:00
+1941-10-02T15:45:00+00:00,1942-09-02T15:45:00+00:00,1906-01-02T15:45:00+00:00
+1941-10-02T15:50:00+00:00,1942-09-02T15:50:00+00:00,1906-01-02T15:50:00+00:00
+1941-10-02T15:55:00+00:00,1942-09-02T15:55:00+00:00,1906-01-02T15:55:00+00:00
+1941-10-02T16:00:00+00:00,1942-09-02T16:00:00+00:00,1906-01-02T16:00:00+00:00
+1941-10-02T16:05:00+00:00,1942-09-02T16:05:00+00:00,1906-01-02T16:05:00+00:00
+1941-10-02T16:10:00+00:00,1942-09-02T16:10:00+00:00,1906-01-02T16:10:00+00:00
+1941-10-02T16:15:00+00:00,1942-09-02T16:15:00+00:00,1906-01-02T16:15:00+00:00
+1941-10-02T16:20:00+00:00,1942-09-02T16:20:00+00:00,1906-01-02T16:20:00+00:00
+1941-10-02T16:25:00+00:00,1942-09-02T16:25:00+00:00,1906-01-02T16:25:00+00:00
+1941-10-02T16:30:00+00:00,1942-09-02T16:30:00+00:00,1906-01-02T16:30:00+00:00
+1941-10-02T16:35:00+00:00,1942-09-02T16:35:00+00:00,1906-01-02T16:35:00+00:00
+1941-10-02T16:40:00+00:00,1942-09-02T16:40:00+00:00,1906-01-02T16:40:00+00:00
+1941-10-02T16:45:00+00:00,1942-09-02T16:45:00+00:00,1906-01-02T16:45:00+00:00
+1941-10-02T16:50:00+00:00,1942-09-02T16:50:00+00:00,1906-01-02T16:50:00+00:00
+1941-10-02T16:55:00+00:00,1942-09-02T16:55:00+00:00,1906-01-02T16:55:00+00:00
+1941-10-02T17:00:00+00:00,1942-09-02T17:00:00+00:00,1906-01-02T17:00:00+00:00
+1941-10-02T17:05:00+00:00,1942-09-02T17:05:00+00:00,1906-01-02T17:05:00+00:00
+1941-10-02T17:10:00+00:00,1942-09-02T17:10:00+00:00,1906-01-02T17:10:00+00:00
+1941-10-02T17:15:00+00:00,1942-09-02T17:15:00+00:00,1906-01-02T17:15:00+00:00
+1941-10-02T17:20:00+00:00,1942-09-02T17:20:00+00:00,1906-01-02T17:20:00+00:00
+1941-10-02T17:25:00+00:00,1942-09-02T17:25:00+00:00,1906-01-02T17:25:00+00:00
+1941-10-02T17:30:00+00:00,1942-09-02T17:30:00+00:00,1906-01-02T17:30:00+00:00
+1941-10-02T17:35:00+00:00,1942-09-02T17:35:00+00:00,1906-01-02T17:35:00+00:00
+1941-10-02T17:40:00+00:00,1942-09-02T17:40:00+00:00,1906-01-02T17:40:00+00:00
+1941-10-02T17:45:00+00:00,1942-09-02T17:45:00+00:00,1906-01-02T17:45:00+00:00
+1941-10-02T17:50:00+00:00,1942-09-02T17:50:00+00:00,1906-01-02T17:50:00+00:00
+1941-10-02T17:55:00+00:00,1942-09-02T17:55:00+00:00,1906-01-02T17:55:00+00:00
+1941-10-02T18:00:00+00:00,1942-09-02T18:00:00+00:00,1906-01-02T18:00:00+00:00
+1941-10-02T18:05:00+00:00,1942-09-02T18:05:00+00:00,1906-01-02T18:05:00+00:00
+1941-10-02T18:10:00+00:00,1942-09-02T18:10:00+00:00,1906-01-02T18:10:00+00:00
+1941-10-02T18:15:00+00:00,1942-09-02T18:15:00+00:00,1906-01-02T18:15:00+00:00
+1941-10-02T18:20:00+00:00,1942-09-02T18:20:00+00:00,1906-01-02T18:20:00+00:00
+1941-10-02T18:25:00+00:00,1942-09-02T18:25:00+00:00,1906-01-02T18:25:00+00:00
+1941-10-02T18:30:00+00:00,1942-09-02T18:30:00+00:00,1906-01-02T18:30:00+00:00
+1941-10-02T18:35:00+00:00,1942-09-02T18:35:00+00:00,1906-01-02T18:35:00+00:00
+1941-10-02T18:40:00+00:00,1942-09-02T18:40:00+00:00,1906-01-02T18:40:00+00:00
+1941-10-02T18:45:00+00:00,1942-09-02T18:45:00+00:00,1906-01-02T18:45:00+00:00
+1941-10-02T18:50:00+00:00,1942-09-02T18:50:00+00:00,1906-01-02T18:50:00+00:00
+1941-10-02T18:55:00+00:00,1942-09-02T18:55:00+00:00,1906-01-02T18:55:00+00:00
+1941-10-02T19:00:00+00:00,1942-09-02T19:00:00+00:00,1906-01-02T19:00:00+00:00
+1941-10-02T19:05:00+00:00,1942-09-02T19:05:00+00:00,1906-01-02T19:05:00+00:00
+1941-10-02T19:10:00+00:00,1942-09-02T19:10:00+00:00,1906-01-02T19:10:00+00:00
+1941-10-02T19:15:00+00:00,1942-09-02T19:15:00+00:00,1906-01-02T19:15:00+00:00
+1941-10-02T19:20:00+00:00,1942-09-02T19:20:00+00:00,1906-01-02T19:20:00+00:00
+1941-10-02T19:25:00+00:00,1942-09-02T19:25:00+00:00,1906-01-02T19:25:00+00:00
+1941-10-02T19:30:00+00:00,1942-09-02T19:30:00+00:00,1906-01-02T19:30:00+00:00
+1941-10-02T19:35:00+00:00,1942-09-02T19:35:00+00:00,1906-01-02T19:35:00+00:00
+1941-10-02T19:40:00+00:00,1942-09-02T19:40:00+00:00,1906-01-02T19:40:00+00:00
+1941-10-02T19:45:00+00:00,1942-09-02T19:45:00+00:00,1906-01-02T19:45:00+00:00
+1941-10-02T19:50:00+00:00,1942-09-02T19:50:00+00:00,1906-01-02T19:50:00+00:00
+1941-10-02T19:55:00+00:00,1942-09-02T19:55:00+00:00,1906-01-02T19:55:00+00:00
+1941-10-02T20:00:00+00:00,1942-09-02T20:00:00+00:00,1906-01-02T20:00:00+00:00
+1941-10-02T20:05:00+00:00,1942-09-02T20:05:00+00:00,1906-01-02T20:05:00+00:00
+1941-10-02T20:10:00+00:00,1942-09-02T20:10:00+00:00,1906-01-02T20:10:00+00:00
+1941-10-02T20:15:00+00:00,1942-09-02T20:15:00+00:00,1906-01-02T20:15:00+00:00
+1941-10-02T20:20:00+00:00,1942-09-02T20:20:00+00:00,1906-01-02T20:20:00+00:00
+1941-10-02T20:25:00+00:00,1942-09-02T20:25:00+00:00,1906-01-02T20:25:00+00:00
+1941-10-02T20:30:00+00:00,1942-09-02T20:30:00+00:00,1906-01-02T20:30:00+00:00
+1941-10-02T20:35:00+00:00,1942-09-02T20:35:00+00:00,1906-01-02T20:35:00+00:00
+1941-10-02T20:40:00+00:00,1942-09-02T20:40:00+00:00,1906-01-02T20:40:00+00:00
+1941-10-02T20:45:00+00:00,1942-09-02T20:45:00+00:00,1906-01-02T20:45:00+00:00
+1941-10-02T20:50:00+00:00,1942-09-02T20:50:00+00:00,1906-01-02T20:50:00+00:00
+1941-10-02T20:55:00+00:00,1942-09-02T20:55:00+00:00,1906-01-02T20:55:00+00:00
+1941-10-02T21:00:00+00:00,1942-09-02T21:00:00+00:00,1906-01-02T21:00:00+00:00
+1941-10-02T21:05:00+00:00,1942-09-02T21:05:00+00:00,1906-01-02T21:05:00+00:00
+1941-10-02T21:10:00+00:00,1942-09-02T21:10:00+00:00,1906-01-02T21:10:00+00:00
+1941-10-02T21:15:00+00:00,1942-09-02T21:15:00+00:00,1906-01-02T21:15:00+00:00
+1941-10-02T21:20:00+00:00,1942-09-02T21:20:00+00:00,1906-01-02T21:20:00+00:00
+1941-10-02T21:25:00+00:00,1942-09-02T21:25:00+00:00,1906-01-02T21:25:00+00:00
+1941-10-02T21:30:00+00:00,1942-09-02T21:30:00+00:00,1906-01-02T21:30:00+00:00
+1941-10-02T21:35:00+00:00,1942-09-02T21:35:00+00:00,1906-01-02T21:35:00+00:00
+1941-10-02T21:40:00+00:00,1942-09-02T21:40:00+00:00,1906-01-02T21:40:00+00:00
+1941-10-02T21:45:00+00:00,1942-09-02T21:45:00+00:00,1906-01-02T21:45:00+00:00
+1941-10-02T21:50:00+00:00,1942-09-02T21:50:00+00:00,1906-01-02T21:50:00+00:00
+1941-10-02T21:55:00+00:00,1942-09-02T21:55:00+00:00,1906-01-02T21:55:00+00:00
+1941-10-02T22:00:00+00:00,1942-09-02T22:00:00+00:00,1906-01-02T22:00:00+00:00
+1941-10-02T22:05:00+00:00,1942-09-02T22:05:00+00:00,1906-01-02T22:05:00+00:00
+1941-10-02T22:10:00+00:00,1942-09-02T22:10:00+00:00,1906-01-02T22:10:00+00:00
+1941-10-02T22:15:00+00:00,1942-09-02T22:15:00+00:00,1906-01-02T22:15:00+00:00
+1941-10-02T22:20:00+00:00,1942-09-02T22:20:00+00:00,1906-01-02T22:20:00+00:00
+1941-10-02T22:25:00+00:00,1942-09-02T22:25:00+00:00,1906-01-02T22:25:00+00:00
+1941-10-02T22:30:00+00:00,1942-09-02T22:30:00+00:00,1906-01-02T22:30:00+00:00
+1941-10-02T22:35:00+00:00,1942-09-02T22:35:00+00:00,1906-01-02T22:35:00+00:00
+1941-10-02T22:40:00+00:00,1942-09-02T22:40:00+00:00,1906-01-02T22:40:00+00:00
+1941-10-02T22:45:00+00:00,1942-09-02T22:45:00+00:00,1906-01-02T22:45:00+00:00
+1941-10-02T22:50:00+00:00,1942-09-02T22:50:00+00:00,1906-01-02T22:50:00+00:00
+1941-10-02T22:55:00+00:00,1942-09-02T22:55:00+00:00,1906-01-02T22:55:00+00:00
+1941-10-02T23:00:00+00:00,1942-09-02T23:00:00+00:00,1906-01-02T23:00:00+00:00
+1941-10-02T23:05:00+00:00,1942-09-02T23:05:00+00:00,1906-01-02T23:05:00+00:00
+1941-10-02T23:10:00+00:00,1942-09-02T23:10:00+00:00,1906-01-02T23:10:00+00:00
+1941-10-02T23:15:00+00:00,1942-09-02T23:15:00+00:00,1906-01-02T23:15:00+00:00
+1941-10-02T23:20:00+00:00,1942-09-02T23:20:00+00:00,1906-01-02T23:20:00+00:00
+1941-10-02T23:25:00+00:00,1942-09-02T23:25:00+00:00,1906-01-02T23:25:00+00:00
+1941-10-02T23:30:00+00:00,1942-09-02T23:30:00+00:00,1906-01-02T23:30:00+00:00
+1941-10-02T23:35:00+00:00,1942-09-02T23:35:00+00:00,1906-01-02T23:35:00+00:00
+1941-10-02T23:40:00+00:00,1942-09-02T23:40:00+00:00,1906-01-02T23:40:00+00:00
+1941-10-02T23:45:00+00:00,1942-09-02T23:45:00+00:00,1906-01-02T23:45:00+00:00
+1941-10-02T23:50:00+00:00,1942-09-02T23:50:00+00:00,1906-01-02T23:50:00+00:00
+1941-10-02T23:55:00+00:00,1942-09-02T23:55:00+00:00,1906-01-02T23:55:00+00:00
\ No newline at end of file diff --git a/test/files/strict.xlsx b/test/files/strict.xlsx Binary files differnew file mode 100644 index 0000000..16ae030 --- /dev/null +++ b/test/files/strict.xlsx diff --git a/test/formatters/test_csv.rb b/test/formatters/test_csv.rb index a4443f4..d47d93d 100644 --- a/test/formatters/test_csv.rb +++ b/test/formatters/test_csv.rb @@ -100,6 +100,23 @@ class TestRooFormatterCSV < Minitest::Test end end + def test_bug_datetime_offset_change + # DO NOT REMOVE Asia/Calcutta + [nil, "US/Eastern", "US/Pacific", "Asia/Calcutta"].each do |zone| + with_timezone(zone) do + with_each_spreadsheet(name: "datetime_timezone_ist_offset_change", format: %i[excelx openoffice libreoffice]) do |workbook| + Dir.mktmpdir do |tempdir| + datetime_csv_file = File.join(tempdir, "datetime_timezone_ist_offset_change.csv") + + assert workbook.to_csv(datetime_csv_file) + assert File.exist?(datetime_csv_file) + assert_equal "", file_diff("#{TESTDIR}/so_datetime_timezone_ist_offset_change.csv", datetime_csv_file) + end + end + end + end + end + def test_true_class assert_equal "true", cell_to_csv(1, 1) end diff --git a/test/formatters/test_xml.rb b/test/formatters/test_xml.rb index c8390b6..ef904ec 100644 --- a/test/formatters/test_xml.rb +++ b/test/formatters/test_xml.rb @@ -16,7 +16,7 @@ class TestRooFormatterXML < Minitest::Test all_cells = init_all_cells(workbook, sheetname) cells = xml_sheet.children.reject(&:text?) - assert_equal sheetname, xml_sheet.attribute("name").value + assert_equal sheetname, xml_sheet["name"] assert_equal all_cells.size, cells.size cells.each_with_index do |cell, i| @@ -27,10 +27,10 @@ class TestRooFormatterXML < Minitest::Test all_cells[i][:type], ] result = [ - cell.attribute("row").value, - cell.attribute("column").value, + cell["row"], + cell["column"], cell.text, - cell.attribute("type").value, + cell["type"], ] assert_equal expected, result diff --git a/test/roo/test_base.rb b/test/roo/test_base.rb index 31ddb09..a9a4dd6 100644 --- a/test/roo/test_base.rb +++ b/test/roo/test_base.rb @@ -63,11 +63,11 @@ class TestRooBase < Minitest::Test workbook.default_sheet = workbook.sheets.first result = workbook.find 20 assert result - assert_equal "Brief aus dem Sekretariat", result[0]["TITEL"] + assert_equal "Brief aus dem Sekretariat", result[0] result = workbook.find 22 assert result - assert_equal "Brief aus dem Skretariat. Tagung in Amberg/Opf.", result[0]["TITEL"] + assert_equal "Brief aus dem Skretariat. Tagung in Amberg/Opf.", result[0] end end diff --git a/test/roo/test_csv.rb b/test/roo/test_csv.rb index c5e251d..336191b 100644 --- a/test/roo/test_csv.rb +++ b/test/roo/test_csv.rb @@ -13,6 +13,34 @@ class TestRooCSV < Minitest::Test end end + def test_download_uri_with_query_string + file = filename("simple_spreadsheet") + port = 12_347 + url = "#{local_server(port)}/#{file}?query-param=value" + + start_local_server(file, port) do + csv = roo_class.new(url) + assert_equal "Task 1", csv.cell("f", 4) + assert_equal 1, csv.first_row + assert_equal 13, csv.last_row + assert_equal 1, csv.first_column + assert_equal 6, csv.last_column + end + end + + def test_open_stream + file = filename("Bibelbund") + file_contents = File.read File.join(TESTDIR, file) + stream = StringIO.new(file_contents) + csv = roo_class.new(stream) + + assert_equal "Aktuelle Seite", csv.cell("h", 12) + assert_equal 1, csv.first_row + assert_equal 3735, csv.last_row + assert_equal 1, csv.first_column + assert_equal 8, csv.last_column + end + def test_nil_rows_and_lines_csv # x_123 oo = Roo::CSV.new(File.join(TESTDIR,'Bibelbund.csv')) diff --git a/test/test_helper.rb b/test/test_helper.rb index 96af546..9b761af 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -154,3 +154,16 @@ def skip_jruby_incompatible_test msg = "This test uses a feature incompatible with JRuby" skip(msg) if defined?(JRUBY_VERSION) end + +def with_timezone(new_tz) + if new_tz + begin + prev_tz, ENV['TZ'] = ENV['TZ'], new_tz + yield + ensure + ENV['TZ'] = prev_tz + end + else + yield + end +end diff --git a/test/test_roo.rb b/test/test_roo.rb index 9b732e9..dae4a41 100644 --- a/test/test_roo.rb +++ b/test/test_roo.rb @@ -339,21 +339,21 @@ class TestRoo < Minitest::Test # compare large spreadsheets def test_compare_large_spreadsheets - # problematisch, weil Formeln in Excel nicht unterstützt werden skip_long_test - qq = Roo::OpenOffice.new(File.join('test',"Bibelbund.ods")) - with_each_spreadsheet(:name=>'Bibelbund') do |oo| - # p "comparing Bibelbund.ods with #{oo.class}" + qq = Roo::OpenOffice.new(File.join('test', 'files', "Bibelbund.ods")) + with_each_spreadsheet(name: 'Bibelbund') do |oo| oo.sheets.each do |sh| oo.first_row.upto(oo.last_row) do |row| oo.first_column.upto(oo.last_column) do |col| - c1 = qq.cell(row,col,sh) + c1 = qq.cell(row, col, sh) c1.force_encoding("UTF-8") if c1.class == String c2 = oo.cell(row,col,sh) c2.force_encoding("UTF-8") if c2.class == String + next if c1.nil? && c2.nil? + assert_equal c1, c2, "diff in #{sh}/#{row}/#{col}}" - assert_equal qq.celltype(row,col,sh), oo.celltype(row,col,sh) - assert_equal qq.formula?(row,col,sh), oo.formula?(row,col,sh) if oo.class != Roo::Excel + assert_equal qq.celltype(row, col, sh), oo.celltype(row, col, sh) + assert_equal qq.formula?(row, col, sh), oo.formula?(row, col, sh) end end end |