Repository: sdementen/piecash

Issue:
TZLocal update to 3.0 breaks piecash
2.1 works fine.

Throws this exception:

```
 File "/usr/local/data/laminar/run/accounts_update/1055/accounts/env/lib/python3.7/site-packages/gnucash_reports/wrapper.py", line 241, in get_balance_on_date
    ).order_by(piecash.Price.date.desc()).limit(1).one_or_none()
  File "/usr/local/data/laminar/run/accounts_update/1055/accounts/env/lib/python3.7/site-packages/sqlalchemy/orm/query.py", line 3459, in one_or_none
    ret = list(self)
  File "/usr/local/data/laminar/run/accounts_update/1055/accounts/env/lib/python3.7/site-packages/sqlalchemy/orm/query.py", line 3535, in __iter__
    return self._execute_and_instances(context)
  File "/usr/local/data/laminar/run/accounts_update/1055/accounts/env/lib/python3.7/site-packages/sqlalchemy/orm/query.py", line 3560, in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
  File "/usr/local/data/laminar/run/accounts_update/1055/accounts/env/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1011, in execute
    return meth(self, multiparams, params)
  File "/usr/local/data/laminar/run/accounts_update/1055/accounts/env/lib/python3.7/site-packages/sqlalchemy/sql/elements.py", line 298, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File "/usr/local/data/laminar/run/accounts_update/1055/accounts/env/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1130, in _execute_clauseelement
    distilled_params,
  File "/usr/local/data/laminar/run/accounts_update/1055/accounts/env/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1207, in _execute_context
    e, util.text_type(statement), parameters, None, None
  File "/usr/local/data/laminar/run/accounts_update/1055/accounts/env/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1511, in _handle_dbapi_exception
    sqlalchemy_exception, with_traceback=exc_info[2], from_=e
  File "/usr/local/data/laminar/run/accounts_update/1055/accounts/env/lib/python3.7/site-packages/sqlalchemy/util/compat.py", line 182, in raise_
    raise exception
  File "/usr/local/data/laminar/run/accounts_update/1055/accounts/env/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1204, in _execute_context
    context = constructor(dialect, self, conn, *args)
  File "/usr/local/data/laminar/run/accounts_update/1055/accounts/env/lib/python3.7/site-packages/sqlalchemy/engine/default.py", line 854, in _init_compiled
    param.append(processors[key](compiled_params[key]))
  File "/usr/local/data/laminar/run/accounts_update/1055/accounts/env/lib/python3.7/site-packages/sqlalchemy/sql/type_api.py", line 1232, in process
    return process_param(value, dialect)
  File "/usr/local/data/laminar/run/accounts_update/1055/accounts/env/lib/python3.7/site-packages/piecash/sa_extra.py", line 140, in process_bind_param
    result = tz.localize(datetime.datetime.combine(value, datetime.time(0, 0, 0))) \
sqlalchemy.exc.StatementError: (builtins.AttributeError) 'backports.zoneinfo.ZoneInfo' object has no attribute 'localize'
[SQL: SELECT prices.value_num AS prices_value_num, prices.value_denom AS prices_value_denom, prices.guid AS prices_guid, prices.commodity_guid AS prices_commodity_guid, prices.currency_guid AS prices_currency_guid, prices.date AS prices_date, prices.source AS prices_source, prices.type AS prices_type 
FROM prices 
WHERE %s = prices.commodity_guid AND %s = prices.currency_guid AND prices.date <= %s ORDER BY prices.date DESC 
 LIMIT %s]
```

Discussion:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 1d965e0..3a45556 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,6 +1,11 @@
 What's new
 ==========
 
+devel
+~~~~~
+
+- fix tzlocal>=2.3 issue with change of tz type returned by tzlocal.get_localzone() (fix #180)
+
 Version 1.1.7 (2021-04-04)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/piecash/sa_extra.py b/piecash/sa_extra.py
index 8b8a8c5..49f148f 100644
--- a/piecash/sa_extra.py
+++ b/piecash/sa_extra.py
@@ -69,7 +69,7 @@ class DeclarativeBase(object):
         return str(self)
 
 
-tz = tzlocal.get_localzone()
+tz = pytz.timezone(str(tzlocal.get_localzone()))
 utc = pytz.utc
 
 
@@ -158,8 +158,8 @@ class _DateAsDateTime(types.TypeDecorator):
 
     def process_result_value(self, value, dialect):
         if value is not None:
-            r = utc.localize(value).astimezone(tz).date()
-            return r
+            r = utc.localize(value).astimezone(tz)
+            return r.date()
 
 
 class _Date(types.TypeDecorator):


Test patch:
diff --git a/tests/test_factories.py b/tests/test_factories.py
index 6cf2bbd..e13dd12 100644
--- a/tests/test_factories.py
+++ b/tests/test_factories.py
@@ -5,6 +5,7 @@ from datetime import datetime
 from decimal import Decimal
 
 import pytest
+import pytz
 import tzlocal
 
 from piecash import GnucashException, Commodity
@@ -127,13 +128,13 @@ class TestFactoriesTransactions(object):
         assert sp2.account == book_basic.accounts(name="asset")
         assert sp1.value == -sp2.value
         assert sp1.quantity == sp1.value
-        assert tr.enter_date == tzlocal.get_localzone().localize(
+        assert tr.enter_date == pytz.timezone(str(tzlocal.get_localzone())).localize(
             today.replace(microsecond=0)
         )
-        assert tr.post_date == tzlocal.get_localzone().localize(today).date()
+        assert tr.post_date == pytz.timezone(str(tzlocal.get_localzone())).localize(today).date()
 
     def test_single_transaction_tz(self, book_basic):
-        today = tzlocal.get_localzone().localize(datetime.today())
+        today = pytz.timezone(str(tzlocal.get_localzone())).localize(datetime.today())
         tr = factories.single_transaction(
             today.date(),
             today,
@@ -148,7 +149,7 @@ class TestFactoriesTransactions(object):
         assert tr.enter_date == today.replace(microsecond=0)
 
     def test_single_transaction_rollback(self, book_basic):
-        today = tzlocal.get_localzone().localize(datetime.today())
+        today = pytz.timezone(str(tzlocal.get_localzone())).localize(datetime.today())
         factories.single_transaction(
             today.date(),
             today,