Skip to content

Commit 0307266

Browse files
jspricketbabej
authored andcommitted
Replace pytz and tzlocal by zoneinfo
Requires Python 3.9
1 parent e11e163 commit 0307266

6 files changed

Lines changed: 44 additions & 49 deletions

File tree

docs/index.rst

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -305,15 +305,15 @@ Dealing with dates and time
305305
---------------------------
306306

307307
Any timestamp-like attributes of the tasks are converted to timezone-aware
308-
datetime objects. To achieve this, Tasklib leverages ``pytz`` Python module,
308+
datetime objects. To achieve this, Tasklib leverages ``zoneinfo`` Python module,
309309
which brings the Olsen timezone database to Python.
310310

311311
This shields you from annoying details of Daylight Saving Time shifts
312312
or conversion between different timezones. For example, to list all the
313313
tasks which are due midnight if you're currently in Berlin:
314314

315-
>>> myzone = pytz.timezone('Europe/Berlin')
316-
>>> midnight = myzone.localize(datetime(2015,2,2,0,0,0))
315+
>>> myzone = zoneinfo.ZoneInfo('Europe/Berlin')
316+
>>> midnight = datetime(2015,2,2,0,0,0,tzinfo=myzone)
317317
>>> tw.tasks.filter(due__before=midnight)
318318

319319
However, this is still a little bit tedious. That's why TaskWarrior object
@@ -360,7 +360,7 @@ to localize the naive value first:
360360

361361
>>> from datetime import datetime
362362
>>> from tasklib.task import local_zone
363-
>>> now = local_zone.localize(datetime.now())
363+
>>> now = datetime.now().replace(tzinfo=local_zone)
364364
>>> t['due'] = now
365365
>>> now
366366
datetime.datetime(2015, 2, 1, 19, 44, 4, 770001, tzinfo=<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>)
@@ -370,12 +370,12 @@ to localize the naive value first:
370370
Also, note that it does not matter whether the timezone aware datetime objects
371371
are set in the same timezone:
372372

373-
>>> import pytz
373+
>>> import zoneinfo
374374
>>> t['due']
375375
datetime.datetime(2015, 2, 1, 19, 44, 4, 770001, tzinfo=<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>)
376-
>>> now.astimezone(pytz.utc)
376+
>>> now.astimezone(zoneinfo.ZoneInfo('UTC'))
377377
datetime.datetime(2015, 2, 1, 18, 44, 4, 770001, tzinfo=<UTC>)
378-
>>> t['due'] == now.astimezone(pytz.utc)
378+
>>> t['due'] == now.astimezone(zoneinfo.ZoneInfo('UTC'))
379379
True
380380

381381
*Note*: Following behaviour is available only for TaskWarrior >= 2.4.0.

setup.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
from setuptools import setup, find_packages
22

3-
install_requirements = ['pytz', 'tzlocal']
4-
53
version = '2.4.3'
64

75
setup(
@@ -17,7 +15,6 @@
1715
packages=find_packages(),
1816
include_package_data=True,
1917
test_suite='tasklib.tests',
20-
install_requires=install_requirements,
2118
classifiers=[
2219
'Development Status :: 6 - Mature',
2320
'Programming Language :: Python',

tasklib/backends.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ def convert_datetime_string(self, value):
225225
args = value.split()
226226
result = self.execute_command(['calc'] + args)
227227
naive = datetime.datetime.strptime(result[0], DATE_FORMAT_CALC)
228-
localized = local_zone.localize(naive)
228+
localized = naive.replace(tzinfo=local_zone)
229229
return localized
230230

231231
@property

tasklib/serializing.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import datetime
22
import importlib
33
import json
4-
import pytz
5-
import tzlocal
4+
from zoneinfo import ZoneInfo
65

76

87
from .lazy import LazyUUIDTaskSet, LazyUUIDTask
98

109
DATE_FORMAT = '%Y%m%dT%H%M%SZ'
11-
local_zone = pytz.timezone(str(tzlocal.get_localzone()))
10+
local_zone = ZoneInfo('localtime')
1211

1312

1413
class SerializingObject(object):
@@ -73,7 +72,7 @@ def timestamp_serializer(self, date):
7372

7473
# Any serialized timestamp should be localized, we need to
7574
# convert to UTC before converting to string (DATE_FORMAT uses UTC)
76-
date = date.astimezone(pytz.utc)
75+
date = date.astimezone(ZoneInfo('UTC'))
7776

7877
return date.strftime(DATE_FORMAT)
7978

@@ -83,7 +82,7 @@ def timestamp_deserializer(self, date_str):
8382

8483
# Return timestamp localized in the local zone
8584
naive_timestamp = datetime.datetime.strptime(date_str, DATE_FORMAT)
86-
localized_timestamp = pytz.utc.localize(naive_timestamp)
85+
localized_timestamp = naive_timestamp.replace(tzinfo=ZoneInfo('UTC'))
8786
return localized_timestamp.astimezone(local_zone)
8887

8988
def serialize_entry(self, value):
@@ -226,11 +225,11 @@ def datetime_normalizer(self, value):
226225
):
227226
# Convert to local midnight
228227
value_full = datetime.datetime.combine(value, datetime.time.min)
229-
localized = local_zone.localize(value_full)
228+
localized = value_full.replace(tzinfo=local_zone)
230229
elif isinstance(value, datetime.datetime):
231230
if value.tzinfo is None:
232231
# Convert to localized datetime object
233-
localized = local_zone.localize(value)
232+
localized = value.replace(tzinfo=local_zone)
234233
else:
235234
# If the value is already localized, there is no need to change
236235
# time zone at this point. Also None is a valid value too.

tasklib/task.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ def waiting(self):
299299
if not self['wait']:
300300
return False
301301

302-
return self['wait'] > local_zone.localize(datetime.datetime.now())
302+
return self['wait'] > datetime.datetime.now().replace(tzinfo=local_zone)
303303

304304
@property
305305
def pending(self):

tasklib/tests.py

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
import itertools
66
import json
77
import os
8-
import pytz
98
import shutil
109
import sys
1110
import tempfile
1211
import unittest
1312
from io import StringIO
13+
from zoneinfo import ZoneInfo
1414

1515
from .backends import TaskWarrior
1616
from .task import Task, ReadOnlyDictView
@@ -1110,8 +1110,7 @@ def test_export_data(self):
11101110
self.tw,
11111111
description='test task',
11121112
project='Home',
1113-
due=pytz.utc.localize(
1114-
datetime.datetime(2015, 1, 1, 23, 23, 23)),
1113+
due=datetime.datetime(2015, 1, 1, 23, 23, 23, tzinfo=ZoneInfo('UTC')),
11151114
)
11161115

11171116
# Check that the output is a permutation of:
@@ -1135,8 +1134,8 @@ def setUp(self):
11351134
self.zone = local_zone
11361135
self.localdate_naive = datetime.datetime(2015, 2, 2)
11371136
self.localtime_naive = datetime.datetime(2015, 2, 2, 0, 0, 0)
1138-
self.localtime_aware = self.zone.localize(self.localtime_naive)
1139-
self.utctime_aware = self.localtime_aware.astimezone(pytz.utc)
1137+
self.localtime_aware = self.localtime_naive.replace(tzinfo=self.zone)
1138+
self.utctime_aware = self.localtime_aware.astimezone(ZoneInfo('UTC'))
11401139

11411140
def test_timezone_naive_datetime_setitem(self):
11421141
t = Task(self.tw, description='test task')
@@ -1217,7 +1216,7 @@ def test_simple_now_conversion(self):
12171216
return
12181217

12191218
t = Task(self.tw, description='test task', due='now')
1220-
now = local_zone.localize(datetime.datetime.now())
1219+
now = datetime.datetime.now().replace(tzinfo=local_zone)
12211220

12221221
# Assert that both times are not more than 5 seconds apart
12231222
if sys.version_info < (2, 7):
@@ -1237,24 +1236,26 @@ def test_simple_eoy_conversion(self):
12371236
return
12381237

12391238
t = Task(self.tw, description='test task', due='eoy')
1240-
now = local_zone.localize(datetime.datetime.now())
1241-
eoy = local_zone.localize(datetime.datetime(
1239+
now = datetime.datetime.now().replace(tzinfo=local_zone)
1240+
eoy = datetime.datetime(
12421241
year=now.year,
12431242
month=12,
12441243
day=31,
12451244
hour=23,
12461245
minute=59,
12471246
second=59,
1248-
))
1247+
tzinfo=local_zone
1248+
)
12491249
if self.tw.version >= '2.5.2' and self.tw.version < '2.6.0':
1250-
eoy = local_zone.localize(datetime.datetime(
1250+
eoy = datetime.datetime(
12511251
year=now.year+1,
12521252
month=1,
12531253
day=1,
12541254
hour=0,
12551255
minute=0,
12561256
second=0,
1257-
))
1257+
tzinfo=local_zone
1258+
)
12581259
self.assertEqual(eoy, t['due'])
12591260

12601261
def test_complex_eoy_conversion(self):
@@ -1267,27 +1268,25 @@ def test_complex_eoy_conversion(self):
12671268
return
12681269

12691270
t = Task(self.tw, description='test task', due='eoy - 4 months')
1270-
now = local_zone.localize(datetime.datetime.now())
1271-
due_date = local_zone.localize(
1272-
datetime.datetime(
1273-
year=now.year,
1274-
month=12,
1275-
day=31,
1276-
hour=23,
1277-
minute=59,
1278-
second=59,
1279-
)
1271+
now = datetime.datetime.now().replace(tzinfo=local_zone)
1272+
due_date = datetime.datetime(
1273+
year=now.year,
1274+
month=12,
1275+
day=31,
1276+
hour=23,
1277+
minute=59,
1278+
second=59,
1279+
tzinfo=local_zone
12801280
) - datetime.timedelta(0, 4 * 30 * 86400)
12811281
if self.tw.version >= '2.5.2' and self.tw.version < '2.6.0':
1282-
due_date = local_zone.localize(
1283-
datetime.datetime(
1284-
year=now.year+1,
1285-
month=1,
1286-
day=1,
1287-
hour=0,
1288-
minute=0,
1289-
second=0,
1290-
)
1282+
due_date = datetime.datetime(
1283+
year=now.year+1,
1284+
month=1,
1285+
day=1,
1286+
hour=0,
1287+
minute=0,
1288+
second=0,
1289+
tzinfo=local_zone
12911290
) - datetime.timedelta(0, 4 * 30 * 86400)
12921291
self.assertEqual(due_date, t['due'])
12931292

0 commit comments

Comments
 (0)