Repository: pyexcel/pyexcel-ods3 (language: python)

Issue:
Update odsw.py
Add `datetime.datetime` support to `pyexcel_ods3.odsw.ODSSheetWriter.write_row(array)` method.
To be usable, this patch requires first that a pull request is accepted by pyexcel_io maintainers (`datetime.datetime: "datetime"` key:value pair must be added to `ODS_WRITE_FORMAT_COVERSION` dictionary.. 
Thank you


With your PR, here is a check list:

- [ ] Has test cases written?
- [ ] Has all code lines tested?
- [ ] Has `make format` been run?
- [ ] Please update CHANGELOG.yml(not CHANGELOG.rst)
- [ ] Has fair amount of documentation if your change is complex
- [ ] Agree on NEW BSD License for your contribution

There's no support for password protected ODS, please update the readme
I suggest to add "... and not support for password protected ODS",  probably under "Known constraints".

(or implement it -- i wish I knew how)
Update odsw.py
Add `datetime.datetime` support to `pyexcel_ods3.odsw.ODSSheetWriter.write_row(array)` method.
To be usable, this patch requires first that a pull request is accepted by pyexcel_io maintainers (`datetime.datetime: "datetime"` key:value pair must be added to `ODS_WRITE_FORMAT_COVERSION` dictionary.. 
Thank you


With your PR, here is a check list:

- [ ] Has test cases written?
- [ ] Has all code lines tested?
- [ ] Has `make format` been run?
- [ ] Please update CHANGELOG.yml(not CHANGELOG.rst)
- [ ] Has fair amount of documentation if your change is complex
- [ ] Agree on NEW BSD License for your contribution



Discussion / hints:

good point! none of the plugins support password protected files. such a disclaimer would extend to xls, xlsx.


Fix patch:
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 6017f21..8996445 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -4,6 +4,5 @@ With your PR, here is a check list:
 - [ ] Has all code lines tested?
 - [ ] Has `make format` been run?
 - [ ] Please update CHANGELOG.yml(not CHANGELOG.rst)
-- [ ] Passes all Travis CI builds
 - [ ] Has fair amount of documentation if your change is complex
 - [ ] Agree on NEW BSD License for your contribution
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 0000000..3789494
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,20 @@
+name: lint
+
+on: [push, pull_request]
+
+jobs:
+  lint:
+    runs-on: ubuntu-latest
+    name: lint code
+    steps:
+      - uses: actions/checkout@v2
+      - name: Set up Python
+        uses: actions/setup-python@v1
+        with:
+            python-version: 3.8
+      - name: lint
+        run: |
+          pip --use-deprecated=legacy-resolver install flake8
+          pip --use-deprecated=legacy-resolver install -r tests/requirements.txt
+          flake8 --exclude=.moban.d,docs,setup.py --builtins=unicode,xrange,long . 
+          python setup.py checkdocs
diff --git a/.github/workflows/moban-update.yml b/.github/workflows/moban-update.yml
index 706fd82..73a3aed 100644
--- a/.github/workflows/moban-update.yml
+++ b/.github/workflows/moban-update.yml
@@ -8,6 +8,7 @@ jobs:
       - uses: actions/checkout@v2
         with:
           ref: ${{ github.head_ref }}
+          token: ${{ secrets.PAT }}
       - name: Set up Python
         uses: actions/setup-python@v1
         with:
@@ -20,10 +21,8 @@ jobs:
           git diff --exit-code
       - name: Auto-commit
         if: failure()
-        uses: docker://cdssnc/auto-commit-github-action
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        uses: stefanzweifel/git-auto-commit-action@v4
         with:
-          args: >-
+          commit_message: >-
             This is an auto-commit, updating project meta data,
             such as changelog.rst, contributors.rst
diff --git a/.moban.d/custom_setup.py.jj2 b/.moban.d/custom_setup.py.jj2
index fd94362..b8d8fbc 100644
--- a/.moban.d/custom_setup.py.jj2
+++ b/.moban.d/custom_setup.py.jj2
@@ -1,4 +1,4 @@
-{% extends 'setup.py.jj2' %}
+{% extends 'pyexcel-setup.py.jj2' %}
 
 {%block platform_block%}
 {%endblock%}
diff --git a/.moban.d/custom_travis.yml.jj2 b/.moban.d/custom_travis.yml.jj2
deleted file mode 100644
index ed5aaa3..0000000
--- a/.moban.d/custom_travis.yml.jj2
+++ /dev/null
@@ -1,7 +0,0 @@
-{% extends "travis.yml.jj2" %}
-{%block custom_python_versions%}
-python:
-  - 3.8
-  - 3.7
-  - 3.6
-{%endblock%}
diff --git a/.moban.yml b/.moban.yml
index 9853a19..33ffede 100644
--- a/.moban.yml
+++ b/.moban.yml
@@ -5,7 +5,6 @@ targets:
   - README.rst: custom_README.rst.jj2
   - setup.py: custom_setup.py.jj2
   - "docs/source/conf.py": "docs/source/conf.py.jj2"
-  - .travis.yml: custom_travis.yml.jj2
   - MANIFEST.in: MANIFEST.in.jj2
   - "tests/requirements.txt": "tests/requirements.txt"
   - .gitignore: commons-gitignore.jj2
diff --git a/.readthedocs.yml b/.readthedocs.yml
new file mode 100644
index 0000000..a379070
--- /dev/null
+++ b/.readthedocs.yml
@@ -0,0 +1,18 @@
+# .readthedocs.yml
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+# Build documentation in the docs/ directory with Sphinx
+sphinx:
+  configuration: docs/source/conf.py
+
+# Optionally build your docs in additional formats such as PDF
+formats:
+  - pdf
+
+# Optionally set the version of Python and requirements required to build your docs
+python:
+  version: 3.7
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 9cb4e91..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-sudo: false
-dist: xenial
-language: python
-notifications:
-  email: false
-python:
-  - 3.8
-  - 3.7
-  - 3.6
-
-stages:
-  - lint
-  - test
-
-
-.lint: &lint
-  git:
-    submodules: false
-  python: 3.6
-  env:
-    - MINREQ=0
-  stage: lint
-  script: make lint
-
-jobs:
-  include:
-    - *moban
-    - *lint
-
-stage: test
-
-before_install:
-  - if [[ -f min_requirements.txt && "$MINREQ" -eq 1 ]]; then
-      mv min_requirements.txt requirements.txt ;
-    fi
-  - test ! -f rnd_requirements.txt ||
-    pip install --no-deps -r rnd_requirements.txt
-  - test ! -f rnd_requirements.txt || pip install -r rnd_requirements.txt ;
-  - pip install -r tests/requirements.txt
-script:
-  - make test
-after_success:
-  codecov
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 1c614f0..68dee15 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,6 +1,13 @@
 Change log
 ================================================================================
 
+0.6.1 - 1.2.2022
+--------------------------------------------------------------------------------
+
+**added**
+
+#. `#28 <https://github.com/pyexcel/pyexcel-ods3/issues/28>`_: support datetime
+
 0.6.0 - 8.10.2020
 --------------------------------------------------------------------------------
 
diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst
index 9bc7915..037b24a 100644
--- a/CONTRIBUTORS.rst
+++ b/CONTRIBUTORS.rst
@@ -1,6 +1,6 @@
 
 
-4 contributors
+5 contributors
 ================================================================================
 
 In alphabetical order:
@@ -9,3 +9,4 @@ In alphabetical order:
 * `Mateusz Konieczny <https://github.com/matkoniecz>`_
 * `Michael K. <https://github.com/michael-k>`_
 * `Stephen J. Fuhry <https://github.com/fuhrysteve>`_
+* `Vincent Raspal <https://github.com/vinraspa>`_
diff --git a/LICENSE b/LICENSE
index 0451a72..56f12a7 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2015-2020 by Onni Software Ltd. and its contributors
+Copyright (c) 2015-2022 by Onni Software Ltd. and its contributors
 All rights reserved.
 
 Redistribution and use in source and binary forms of the software as well
diff --git a/README.rst b/README.rst
index b710b4a..d0d70ac 100644
--- a/README.rst
+++ b/README.rst
@@ -5,11 +5,11 @@ pyexcel-ods3 - Let you focus on data, instead of ods format
 .. image:: https://raw.githubusercontent.com/pyexcel/pyexcel.github.io/master/images/patreon.png
    :target: https://www.patreon.com/chfw
 
-.. image:: https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg
+.. image:: https://raw.githubusercontent.com/pyexcel/pyexcel-mobans/master/images/awesome-badge.svg
    :target: https://awesome-python.com/#specific-formats-processing
 
-.. image:: https://travis-ci.org/pyexcel/pyexcel-ods3.svg?branch=master
-   :target: http://travis-ci.org/pyexcel/pyexcel-ods3
+.. image:: https://github.com/pyexcel/pyexcel-ods3/workflows/run_tests/badge.svg
+   :target: http://github.com/pyexcel/pyexcel-ods3/actions
 
 .. image:: https://codecov.io/gh/pyexcel/pyexcel-ods3/branch/master/graph/badge.svg
    :target: https://codecov.io/gh/pyexcel/pyexcel-ods3
@@ -21,7 +21,7 @@ pyexcel-ods3 - Let you focus on data, instead of ods format
    :target: https://anaconda.org/conda-forge/pyexcel-ods3
 
 .. image:: https://pepy.tech/badge/pyexcel-ods3/month
-   :target: https://pepy.tech/project/pyexcel-ods3/month
+   :target: https://pepy.tech/project/pyexcel-ods3
 
 .. image:: https://anaconda.org/conda-forge/pyexcel-ods3/badges/downloads.svg
    :target: https://anaconda.org/conda-forge/pyexcel-ods3
@@ -65,6 +65,8 @@ Known constraints
 
 Fonts, colors and charts are not supported.
 
+Nor to read password protected xls, xlsx and ods files.
+
 Installation
 ================================================================================
 
@@ -346,7 +348,7 @@ Then install relevant development requirements:
 #. pip install -r tests/requirements.txt
 
 Once you have finished your changes, please provide test case(s), relevant documentation
-and update CHANGELOG.rst.
+and update changelog.yml
 
 .. note::
 
@@ -365,7 +367,7 @@ On Linux/Unix systems, please launch your tests like this::
 
     $ make
 
-On Windows systems, please issue this command::
+On Windows, please issue this command::
 
     > test.bat
 
@@ -377,7 +379,7 @@ Please run::
 
     $ make format
 
-so as to beautify your code otherwise travis-ci may fail your unit test.
+so as to beautify your code otherwise your build may fail your unit test.
 
 
 Installation Note
diff --git a/changelog.yml b/changelog.yml
index 3af250c..09c536a 100644
--- a/changelog.yml
+++ b/changelog.yml
@@ -1,6 +1,12 @@
 name: pyexcel-ods3
 organisation: pyexcel
 releases:
+- changes:
+  - action: added
+    details:
+    - '`#28`: support datetime'
+  date: 1.2.2022
+  version: 0.6.1
 - changes:
   - action: added
     details:
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 7f04ef6..cfd4c3b 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -22,12 +22,12 @@
 # -- Project information -----------------------------------------------------
 
 project = 'pyexcel-ods3'
-copyright = '2015-2020 Onni Software Ltd.'
-author = 'chfw'
+copyright = '2015-2022 Onni Software Ltd.'
+author = 'C.W.'
 # The short X.Y version
-version = '0.6.0'
+version = '0.6.1'
 # The full version, including alpha/beta/rc tags
-release = '0.6.0'
+release = '0.6.1'
 
 # -- General configuration ---------------------------------------------------
 
diff --git a/lint.sh b/lint.sh
index 891aa63..d31eeaa 100644
--- a/lint.sh
+++ b/lint.sh
@@ -1,2 +1,2 @@
 pip install flake8
-flake8 --exclude=.moban.d,docs,setup.py   --builtins=unicode,xrange,long . && python setup.py checkdocs
+flake8 --exclude=.moban.d,docs,setup.py   --builtins=unicode,xrange,long .  && python setup.py checkdocs
\ No newline at end of file
diff --git a/pyexcel-ods3.yml b/pyexcel-ods3.yml
index c849847..cafcee6 100644
--- a/pyexcel-ods3.yml
+++ b/pyexcel-ods3.yml
@@ -1,9 +1,9 @@
 overrides: "pyexcel.yaml"
 name: "pyexcel-ods3"
 nick_name: ods3
-version: 0.6.0
-current_version: 0.6.0
-release: 0.6.0
+version: 0.6.1
+current_version: 0.6.1
+release: 0.6.1
 file_type: ods
 is_on_conda: true
 dependencies:
@@ -17,3 +17,5 @@ test_dependencies:
 moban_command: false
 description: |-
   A wrapper library to read, manipulate and write data in ods format
+python_requires: ">=3.6"
+min_python_version: "3.6"
diff --git a/pyexcel_ods3/odsr.py b/pyexcel_ods3/odsr.py
index 3db0fd1..262d1ce 100644
--- a/pyexcel_ods3/odsr.py
+++ b/pyexcel_ods3/odsr.py
@@ -4,7 +4,7 @@
 
     ods reader
 
-    :copyright: (c)  2015-2020 by Onni Software Ltd. & its contributors
+    :copyright: (c)  2015-2022 by Onni Software Ltd. & its contributors
     :license: New BSD License
 """
 from io import BytesIO
diff --git a/pyexcel_ods3/odsw.py b/pyexcel_ods3/odsw.py
index 474ca43..1cc76e5 100644
--- a/pyexcel_ods3/odsw.py
+++ b/pyexcel_ods3/odsw.py
@@ -4,7 +4,7 @@
 
     ods writer using ezodf
 
-    :copyright: (c)  2015-2020 by Onni Software Ltd. & its contributors
+    :copyright: (c)  2015-2022 by Onni Software Ltd. & its contributors
     :license: New BSD License
 """
 import types
@@ -44,6 +44,16 @@ def write_row(self, array):
                 seconds = cell.seconds % 60
                 cell = "PT%02dH%02dM%02dS" % (hours, minutes, seconds)
                 value_type = "time"
+            if value_type == "datetime":
+                cell = "%04d-%02d-%02dT%02d:%02d:%02d" % (
+                    cell.year,
+                    cell.month,
+                    cell.day,
+                    cell.hour,
+                    cell.minute,
+                    cell.second,
+                )
+                value_type = "date"
             elif value_type == "float":
                 if cell > MAX_INTEGER:
                     raise IntegerAccuracyLossError("%s is too big" % cell)
diff --git a/setup.py b/setup.py
index b9b3810..0917f61 100644
--- a/setup.py
+++ b/setup.py
@@ -31,16 +31,16 @@
     locale.setlocale(locale.LC_ALL, "en_US.UTF-8")
 
 NAME = "pyexcel-ods3"
-AUTHOR = "chfw"
-VERSION = "0.6.0"
+AUTHOR = "C.W."
+VERSION = "0.6.1"
 EMAIL = "info@pyexcel.org"
 LICENSE = "New BSD"
 DESCRIPTION = (
     "A wrapper library to read, manipulate and write data in ods format"
 )
 URL = "https://github.com/pyexcel/pyexcel-ods3"
-DOWNLOAD_URL = "%s/archive/0.6.0.tar.gz" % URL
-FILES = ["README.rst", "CHANGELOG.rst"]
+DOWNLOAD_URL = "%s/archive/0.6.1.tar.gz" % URL
+FILES = ["README.rst", "CONTRIBUTORS.rst", "CHANGELOG.rst"]
 KEYWORDS = [
     "python",
     'ods'
@@ -50,17 +50,20 @@
     "Topic :: Software Development :: Libraries",
     "Programming Language :: Python",
     "Intended Audience :: Developers",
-    "Programming Language :: Python :: 2.6",
-    "Programming Language :: Python :: 2.7",
-    "Programming Language :: Python :: 3.3",
-    "Programming Language :: Python :: 3.4",
-    "Programming Language :: Python :: 3.5",
+
+    "Programming Language :: Python :: 3 :: Only",
+
+
+
     "Programming Language :: Python :: 3.6",
     "Programming Language :: Python :: 3.7",
     "Programming Language :: Python :: 3.8",
 
+    'License :: OSI Approved :: BSD License',
+
 ]
 
+PYTHON_REQUIRES = ">=3.6"
 
 INSTALL_REQUIRES = [
     "pyexcel-io>=0.6.2",
@@ -74,13 +77,14 @@
 }
 # You do not need to read beyond this line
 PUBLISH_COMMAND = "{0} setup.py sdist bdist_wheel upload -r pypi".format(sys.executable)
-GS_COMMAND = ("gs pyexcel-ods3 v0.6.0 " +
-              "Find 0.6.0 in changelog for more details")
+HERE = os.path.abspath(os.path.dirname(__file__))
+
+GS_COMMAND = ("gease pyexcel-ods3 v0.6.1 " +
+              "Find 0.6.1 in changelog for more details")
 NO_GS_MESSAGE = ("Automatic github release is disabled. " +
                  "Please install gease to enable it.")
 UPLOAD_FAILED_MSG = (
     'Upload failed. please run "%s" yourself.' % PUBLISH_COMMAND)
-HERE = os.path.abspath(os.path.dirname(__file__))
 
 
 class PublishCommand(Command):
@@ -126,7 +130,6 @@ def run(self):
     "publish": PublishCommand
 })
 
-
 def has_gease():
     """
     test if github release command is installed
@@ -195,6 +198,7 @@ def filter_out_test_code(file_handle):
         long_description=read_files(*FILES),
         license=LICENSE,
         keywords=KEYWORDS,
+        python_requires=PYTHON_REQUIRES,
         extras_require=EXTRAS_REQUIRE,
         tests_require=["nose"],
         install_requires=INSTALL_REQUIRES,


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..b76b2b0
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,35 @@
+name: Run unit tests on Windows and Mac
+
+on: [push, pull_request]
+
+jobs:
+  test:
+    strategy:
+        fail-fast: false
+        matrix:
+            python-version: [3.6, 3.7, 3.8, 3.9]
+            os: [macOs-latest, ubuntu-latest, windows-latest]
+            exclude:
+              - os: macOs-latest
+                python-version: 3.6
+
+    runs-on:  ${{ matrix.os }}
+    name: run tests
+    steps:
+      - uses: actions/checkout@v2
+      - name: Set up Python
+        uses: actions/setup-python@v1
+        with:
+            python-version:  ${{ matrix.python-version }}
+      - name: install
+        run: |
+          pip --use-deprecated=legacy-resolver install -r requirements.txt
+          pip --use-deprecated=legacy-resolver install -r tests/requirements.txt
+      - name: test
+        run: |
+          pip freeze
+          nosetests --verbosity=3 --with-coverage --cover-package pyexcel_ods3 --cover-package tests tests --with-doctest --doctest-extension=.rst README.rst docs/source pyexcel_ods3
+      - name: Upload coverage
+        uses: codecov/codecov-action@v1
+        with:
+          name:  ${{ matrix.os }} Python ${{ matrix.python-version }}
\ No newline at end of file
diff --git a/tests/test_bug_fixes.py b/tests/test_bug_fixes.py
index 7d78acf..aa69420 100644
--- a/tests/test_bug_fixes.py
+++ b/tests/test_bug_fixes.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-# -*- encoding: utf-8 -*-
 import os
 
 import psutil
diff --git a/tests/test_writer.py b/tests/test_writer.py
index 739515c..b48475f 100644
--- a/tests/test_writer.py
+++ b/tests/test_writer.py
@@ -4,6 +4,8 @@
 from pyexcel_ods3 import get_data
 from pyexcel_ods3.odsw import ODSWriter as Writer
 
+from nose.tools import eq_
+
 
 class TestNativeODSWriter:
     def test_write_book(self):
@@ -45,3 +47,20 @@ def setUp(self):
     def tearDown(self):
         if os.path.exists(self.testfile):
             os.unlink(self.testfile)
+
+
+def test_pr_28():
+    from datetime import datetime
+
+    test_file = "pr28.ods"
+    test = {"shee1": [[datetime(2022, 1, 30, 15, 45, 45)]]}
+    writer = Writer(test_file, "ods")
+    writer.write(test)
+    writer.close()
+
+    content = get_data(test_file)
+    for key in content.keys():
+        content[key] = list(content[key])
+    eq_(content, {"shee1": [[datetime(2022, 1, 30, 15, 45, 45)]]})
+
+    os.unlink(test_file)