From dddfa903d2b856146f05ffb4415c31d6127bb5bf Mon Sep 17 00:00:00 2001 From: Unit 193 Date: Mon, 14 Jan 2019 01:40:56 -0500 Subject: New upstream version 2.8.0 --- .github/ISSUE_TEMPLATE | 10 - .github/issue_template.md | 16 + .github/pull_request_template.md | 14 + .rubocop.yml | 186 +++++ .travis.yml | 19 +- CHANGELOG.md | 31 +- Gemfile_ruby2 | 30 - LICENSE | 2 + README.md | 37 +- lib/roo.rb | 5 +- lib/roo/base.rb | 121 +-- lib/roo/constants.rb | 8 +- lib/roo/csv.rb | 32 +- lib/roo/excelx.rb | 58 +- lib/roo/excelx/cell.rb | 16 +- lib/roo/excelx/cell/base.rb | 38 +- lib/roo/excelx/cell/boolean.rb | 15 +- lib/roo/excelx/cell/date.rb | 14 +- lib/roo/excelx/cell/datetime.rb | 32 +- lib/roo/excelx/cell/empty.rb | 5 +- lib/roo/excelx/cell/number.rb | 69 +- lib/roo/excelx/cell/string.rb | 6 +- lib/roo/excelx/cell/time.rb | 7 +- lib/roo/excelx/comments.rb | 6 +- lib/roo/excelx/coordinate.rb | 15 +- lib/roo/excelx/extractor.rb | 24 +- lib/roo/excelx/format.rb | 69 +- lib/roo/excelx/images.rb | 26 + lib/roo/excelx/relationships.rb | 6 +- lib/roo/excelx/shared.rb | 13 +- lib/roo/excelx/shared_strings.rb | 24 +- lib/roo/excelx/sheet.rb | 59 +- lib/roo/excelx/sheet_doc.rb | 134 ++-- lib/roo/excelx/styles.rb | 6 +- lib/roo/excelx/workbook.rb | 10 +- lib/roo/helpers/default_attr_reader.rb | 20 + lib/roo/helpers/weak_instance_cache.rb | 41 + lib/roo/open_office.rb | 14 +- lib/roo/spreadsheet.rb | 2 +- lib/roo/utils.rb | 67 +- lib/roo/version.rb | 2 +- roo.gemspec | 24 +- spec/lib/roo/base_spec.rb | 48 +- spec/lib/roo/excelx_spec.rb | 156 +++- spec/lib/roo/strict_spec.rb | 43 + spec/lib/roo/utils_spec.rb | 15 +- spec/lib/roo/weak_instance_cache_spec.rb | 92 +++ spec/lib/roo_spec.rb | 0 test/excelx/cell/test_attr_reader_default.rb | 72 ++ test/excelx/cell/test_base.rb | 5 + test/excelx/cell/test_datetime.rb | 12 +- test/excelx/cell/test_empty.rb | 11 + test/excelx/cell/test_number.rb | 9 + test/excelx/cell/test_string.rb | 20 + test/excelx/cell/test_time.rb | 8 +- test/excelx/test_coordinate.rb | 51 ++ test/files/datetime_timezone_ist_offset_change.ods | Bin 0 -> 37034 bytes .../files/datetime_timezone_ist_offset_change.xlsx | Bin 0 -> 22650 bytes test/files/images.xlsx | Bin 0 -> 1665748 bytes test/files/link_with_location.xlsx | Bin 0 -> 8943 bytes test/files/number_with_zero_prefix.xlsx | Bin 0 -> 108222 bytes test/files/simple_spreadsheet.csv | 13 + .../so_datetime_timezone_ist_offset_change.csv | 864 +++++++++++++++++++++ test/files/strict.xlsx | Bin 0 -> 9353 bytes test/formatters/test_csv.rb | 17 + test/formatters/test_xml.rb | 8 +- test/roo/test_base.rb | 4 +- test/roo/test_csv.rb | 28 + test/test_helper.rb | 13 + test/test_roo.rb | 14 +- 70 files changed, 2373 insertions(+), 463 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE create mode 100644 .github/issue_template.md create mode 100644 .github/pull_request_template.md create mode 100644 .rubocop.yml delete mode 100644 Gemfile_ruby2 mode change 100644 => 100755 lib/roo/excelx.rb mode change 100644 => 100755 lib/roo/excelx/extractor.rb create mode 100644 lib/roo/excelx/images.rb mode change 100644 => 100755 lib/roo/excelx/shared.rb create mode 100644 lib/roo/helpers/default_attr_reader.rb create mode 100644 lib/roo/helpers/weak_instance_cache.rb create mode 100644 spec/lib/roo/strict_spec.rb create mode 100644 spec/lib/roo/weak_instance_cache_spec.rb create mode 100644 spec/lib/roo_spec.rb create mode 100644 test/excelx/cell/test_attr_reader_default.rb create mode 100644 test/excelx/test_coordinate.rb create mode 100644 test/files/datetime_timezone_ist_offset_change.ods create mode 100644 test/files/datetime_timezone_ist_offset_change.xlsx create mode 100644 test/files/images.xlsx create mode 100644 test/files/link_with_location.xlsx create mode 100644 test/files/number_with_zero_prefix.xlsx create mode 100644 test/files/simple_spreadsheet.csv create mode 100644 test/files/so_datetime_timezone_ist_offset_change.csv create mode 100644 test/files/strict.xlsx 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 diff --git a/LICENSE b/LICENSE index 182b747..3dad4df 100644 --- a/LICENSE +++ b/LICENSE @@ -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 diff --git a/README.md b/README.md index 00a9ad9..0b18055 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/lib/roo.rb b/lib/roo.rb index ec8eb42..add1e4a 100644 --- a/lib/roo.rb +++ b/lib/roo.rb @@ -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 old mode 100644 new mode 100755 index 82c1431..f9f0ee2 --- 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) - /(?\d+)-(?\d+)-(?
\d+) (?\d+):(?\d+):(?\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 old mode 100644 new mode 100755 index 1cdd13b..b87a84e --- 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_tag_closed: "" + } + + 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 old mode 100644 new mode 100755 index 3677fa2..bcd2c08 --- 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_tag_closed: "" - } - 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_string = ''.dup si.children.each do |elem| case elem.name when 'r' @@ -95,7 +90,7 @@ module Roo # # Expected Output ::: "TEXT" 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 << "" 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 # # 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(, nil) + # cells_from_xml(, nil, nil) # # => # # Returns a type of . - 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 "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
    \n
  1. Denver Broncos
  2. \n
  3. Carolina Panthers
  4. \n
  5. New England Patriots
  6. \n
  7. Arizona Panthers
  8. \n
" - expect(subject.excelx_value(7, 1, "Sheet1")).to eq "Does create html tags when formatting is used..\n
    \n
  1. Denver Broncos
  2. \n
  3. Carolina Panthers
  4. \n
  5. New England Patriots
  6. \n
  7. Arizona Panthers
  8. \n
" + 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
    \n
  1. Denver Broncos
  2. \n
  3. Carolina Panthers
  4. \n
  5. New England Patriots
  6. \n
  7. Arizona Panthers
  8. \n
") + expect(subject.excelx_value(7, 1, "Sheet1")).to eq("Does create html tags when formatting is used..\n
    \n
  1. Denver Broncos
  2. \n
  3. Carolina Panthers
  4. \n
  5. New England Patriots
  6. \n
  7. Arizona Panthers
  8. \n
") + 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
    \n
  1. Denver Broncos
  2. \n
  3. Carolina Panthers
  4. \n
  5. New England Patriots
  6. \n
  7. Arizona Panthers
  8. \n
") + expect(subject.excelx_value(7, 1, "Sheet1")).to eq("Does create html tags when formatting is used..\n
    \n
  1. Denver Broncos
  2. \n
  3. Carolina Panthers
  4. \n
  5. New England Patriots
  6. \n
  7. Arizona Panthers
  8. \n
") + 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 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 new file mode 100644 index 0000000..ebe469b Binary files /dev/null and b/test/files/datetime_timezone_ist_offset_change.ods differ diff --git a/test/files/datetime_timezone_ist_offset_change.xlsx b/test/files/datetime_timezone_ist_offset_change.xlsx new file mode 100644 index 0000000..62240e9 Binary files /dev/null and b/test/files/datetime_timezone_ist_offset_change.xlsx differ diff --git a/test/files/images.xlsx b/test/files/images.xlsx new file mode 100644 index 0000000..669f371 Binary files /dev/null and b/test/files/images.xlsx differ diff --git a/test/files/link_with_location.xlsx b/test/files/link_with_location.xlsx new file mode 100644 index 0000000..393f5ac Binary files /dev/null and b/test/files/link_with_location.xlsx differ diff --git a/test/files/number_with_zero_prefix.xlsx b/test/files/number_with_zero_prefix.xlsx new file mode 100644 index 0000000..104e07f Binary files /dev/null and b/test/files/number_with_zero_prefix.xlsx differ 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 new file mode 100644 index 0000000..16ae030 Binary files /dev/null and b/test/files/strict.xlsx differ 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 -- cgit v1.2.3