Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use standard library for date and time when possible #1443

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ dependencies = [
"requests>=2.7.0",
"requests-toolbelt>=0.7.1",
"requests-file>=1.5.1",
"pytz",
]

[project.urls]
Expand Down
5 changes: 2 additions & 3 deletions src/zeep/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from typing import Dict, Tuple, Union

import platformdirs
import pytz

# The sqlite3 is not available on Google App Engine so we handle the
# ImportError here and set the sqlite3 var to None.
Expand Down Expand Up @@ -169,8 +168,8 @@ def _is_expired(value, timeout):
if timeout is None:
return False

now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=pytz.utc)
max_age = value.replace(tzinfo=pytz.utc)
now = datetime.datetime.now(datetime.timezone.utc)
max_age = value.replace(tzinfo=datetime.timezone.utc)
max_age += datetime.timedelta(seconds=timeout)
return now > max_age

Expand Down
3 changes: 1 addition & 2 deletions src/zeep/wsse/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import datetime
from uuid import uuid4

import pytz
from lxml import etree
from lxml.builder import ElementMaker

Expand Down Expand Up @@ -29,7 +28,7 @@ def get_security_header(doc):

def get_timestamp(timestamp=None, zulu_timestamp=None):
timestamp = timestamp or datetime.datetime.now(datetime.timezone.utc)
timestamp = timestamp.replace(tzinfo=pytz.utc, microsecond=0)
timestamp = timestamp.replace(tzinfo=datetime.timezone.utc, microsecond=0)
if zulu_timestamp:
return timestamp.isoformat().replace("+00:00", "Z")
else:
Expand Down
41 changes: 9 additions & 32 deletions src/zeep/xsd/types/builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from decimal import Decimal as _Decimal

import isodate
import pytz

from zeep.xsd.const import xsd_ns
from zeep.xsd.types.any import AnyType
Expand Down Expand Up @@ -151,22 +150,7 @@ def xmlvalue(self, value):
if isinstance(value, str):
return value

# Bit of a hack, since datetime is a subclass of date we can't just
# test it with an isinstance(). And actually, we should not really
# care about the type, as long as it has the required attributes
if not all(hasattr(value, attr) for attr in ("hour", "minute", "second")):
value = datetime.datetime.combine(
value,
datetime.time(
getattr(value, "hour", 0),
getattr(value, "minute", 0),
getattr(value, "second", 0),
),
)

if getattr(value, "microsecond", 0):
return isodate.isostrf.strftime(value, "%Y-%m-%dT%H:%M:%S.%f%Z")
return isodate.isostrf.strftime(value, "%Y-%m-%dT%H:%M:%S%Z")
return value.isoformat().replace("+00:00", "Z")

@treat_whitespace("collapse")
def pythonvalue(self, value):
Expand All @@ -189,9 +173,7 @@ def xmlvalue(self, value):
if isinstance(value, str):
return value

if value.microsecond:
return isodate.isostrf.strftime(value, "%H:%M:%S.%f%Z")
return isodate.isostrf.strftime(value, "%H:%M:%S%Z")
return value.isoformat().replace("+00:00", "Z")

@treat_whitespace("collapse")
def pythonvalue(self, value):
Expand All @@ -207,7 +189,7 @@ class Date(BuiltinType):
def xmlvalue(self, value):
if isinstance(value, str):
return value
return isodate.isostrf.strftime(value, "%Y-%m-%d")
return value.strftime("%Y-%m-%d")

@treat_whitespace("collapse")
def pythonvalue(self, value):
Expand Down Expand Up @@ -548,35 +530,30 @@ class PositiveInteger(NonNegativeInteger):
##
# Other
def _parse_timezone(val):
"""Return a pytz.tzinfo object"""
"""Return a timezone object"""
if not val:
return

if val == "Z" or val == "+00:00":
return pytz.utc
return datetime.timezone.utc

negative = val.startswith("-")
minutes = int(val[-2:])
minutes += int(val[1:3]) * 60

if negative:
minutes = 0 - minutes
return pytz.FixedOffset(minutes)
return datetime.timezone(offset=datetime.timedelta(minutes=minutes))


def _unparse_timezone(tzinfo):
def _unparse_timezone(tzinfo: datetime.timezone):
if not tzinfo:
return ""

if tzinfo == pytz.utc:
if tzinfo == datetime.timezone.utc:
return "Z"

hours = math.floor(tzinfo._minutes / 60)
minutes = tzinfo._minutes % 60

if hours > 0:
return "+%02d:%02d" % (hours, minutes)
return "-%02d:%02d" % (abs(hours), minutes)
return datetime.datetime.now(tz=tzinfo).isoformat()[-6:]


_types = [
Expand Down
80 changes: 54 additions & 26 deletions tests/test_xsd_builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import isodate
import pytest
import pytz

from zeep.xsd.types import builtins

Expand Down Expand Up @@ -161,14 +160,16 @@ def test_xmlvalue(self):
value = datetime.datetime(2016, 3, 4, 21, 14, 42)
assert instance.xmlvalue(value) == "2016-03-04T21:14:42"

value = datetime.datetime(2016, 3, 4, 21, 14, 42, tzinfo=pytz.utc)
value = datetime.datetime(2016, 3, 4, 21, 14, 42, tzinfo=datetime.timezone.utc)
assert instance.xmlvalue(value) == "2016-03-04T21:14:42Z"

value = datetime.datetime(2016, 3, 4, 21, 14, 42, 123456, tzinfo=pytz.utc)
value = datetime.datetime(
2016, 3, 4, 21, 14, 42, 123456, tzinfo=datetime.timezone.utc
)
assert instance.xmlvalue(value) == "2016-03-04T21:14:42.123456Z"

value = datetime.datetime(2016, 3, 4, 21, 14, 42, tzinfo=pytz.utc)
value = value.astimezone(pytz.timezone("Europe/Amsterdam"))
value = datetime.datetime(2016, 3, 4, 21, 14, 42, tzinfo=datetime.timezone.utc)
value = value.astimezone(datetime.timezone(datetime.timedelta(hours=1)))
assert instance.xmlvalue(value) == "2016-03-04T22:14:42+01:00"

assert (
Expand Down Expand Up @@ -262,18 +263,22 @@ class TestgYearMonth:
def test_xmlvalue(self):
instance = builtins.gYearMonth()
assert instance.xmlvalue((2012, 10, None)) == "2012-10"
assert instance.xmlvalue((2012, 10, pytz.utc)) == "2012-10Z"
assert instance.xmlvalue((2012, 10, datetime.timezone.utc)) == "2012-10Z"

def test_pythonvalue(self):
instance = builtins.gYearMonth()
assert instance.pythonvalue("2001-10") == (2001, 10, None)
assert instance.pythonvalue("2001-10+02:00") == (
2001,
10,
pytz.FixedOffset(120),
datetime.timezone(datetime.timedelta(minutes=120)),
)
assert instance.pythonvalue("2001-10Z") == (2001, 10, datetime.timezone.utc)
assert instance.pythonvalue("2001-10+00:00") == (
2001,
10,
datetime.timezone.utc,
)
assert instance.pythonvalue("2001-10Z") == (2001, 10, pytz.utc)
assert instance.pythonvalue("2001-10+00:00") == (2001, 10, pytz.utc)
assert instance.pythonvalue("-2001-10") == (-2001, 10, None)
assert instance.pythonvalue("-20001-10") == (-20001, 10, None)

Expand All @@ -285,19 +290,22 @@ class TestgYear:
def test_xmlvalue(self):
instance = builtins.gYear()
assert instance.xmlvalue((2001, None)) == "2001"
assert instance.xmlvalue((2001, pytz.utc)) == "2001Z"
assert instance.xmlvalue((2001, datetime.timezone.utc)) == "2001Z"

def test_pythonvalue(self):
instance = builtins.gYear()
assert instance.pythonvalue("2001") == (2001, None)
assert instance.pythonvalue("2001+02:00") == (2001, pytz.FixedOffset(120))
assert instance.pythonvalue("2001Z") == (2001, pytz.utc)
assert instance.pythonvalue("2001+00:00") == (2001, pytz.utc)
assert instance.pythonvalue("2001+02:00") == (
2001,
datetime.timezone(datetime.timedelta(minutes=120)),
)
assert instance.pythonvalue("2001Z") == (2001, datetime.timezone.utc)
assert instance.pythonvalue("2001+00:00") == (2001, datetime.timezone.utc)
assert instance.pythonvalue("-2001") == (-2001, None)
assert instance.pythonvalue("-20000") == (-20000, None)
assert instance.pythonvalue(" \t2001+02:00\r\n ") == (
2001,
pytz.FixedOffset(120),
datetime.timezone(datetime.timedelta(minutes=120)),
)

with pytest.raises(builtins.ParseError):
Expand All @@ -312,9 +320,17 @@ def test_xmlvalue(self):
def test_pythonvalue(self):
instance = builtins.gMonthDay()
assert instance.pythonvalue("--05-01") == (5, 1, None)
assert instance.pythonvalue("--11-01Z") == (11, 1, pytz.utc)
assert instance.pythonvalue("--11-01+02:00") == (11, 1, pytz.FixedOffset(120))
assert instance.pythonvalue("--11-01-04:00") == (11, 1, pytz.FixedOffset(-240))
assert instance.pythonvalue("--11-01Z") == (11, 1, datetime.timezone.utc)
assert instance.pythonvalue("--11-01+02:00") == (
11,
1,
datetime.timezone(datetime.timedelta(minutes=120)),
)
assert instance.pythonvalue("--11-01-04:00") == (
11,
1,
datetime.timezone(datetime.timedelta(minutes=-240)),
)
assert instance.pythonvalue("--11-15") == (11, 15, None)
assert instance.pythonvalue("--02-29") == (2, 29, None)
assert instance.pythonvalue("\t\r\n --05-01 ") == (5, 1, None)
Expand All @@ -331,12 +347,18 @@ def test_xmlvalue(self):
def test_pythonvalue(self):
instance = builtins.gMonth()
assert instance.pythonvalue("--05") == (5, None)
assert instance.pythonvalue("--11Z") == (11, pytz.utc)
assert instance.pythonvalue("--11+02:00") == (11, pytz.FixedOffset(120))
assert instance.pythonvalue("--11-04:00") == (11, pytz.FixedOffset(-240))
assert instance.pythonvalue("--11Z") == (11, datetime.timezone.utc)
assert instance.pythonvalue("--11+02:00") == (
11,
datetime.timezone(datetime.timedelta(minutes=120)),
)
assert instance.pythonvalue("--11-04:00") == (
11,
datetime.timezone(datetime.timedelta(minutes=-240)),
)
assert instance.pythonvalue("--11") == (11, None)
assert instance.pythonvalue("--02") == (2, None)
assert instance.pythonvalue("\n\t --11Z \r") == (11, pytz.utc)
assert instance.pythonvalue("\n\t --11Z \r") == (11, datetime.timezone.utc)

with pytest.raises(builtins.ParseError):
assert instance.pythonvalue("99")
Expand All @@ -349,18 +371,24 @@ def test_xmlvalue(self):
value = (1, None)
assert instance.xmlvalue(value) == "---01"

value = (1, pytz.FixedOffset(120))
value = (1, datetime.timezone(datetime.timedelta(minutes=120)))
assert instance.xmlvalue(value) == "---01+02:00"

value = (1, pytz.FixedOffset(-240))
value = (1, datetime.timezone(datetime.timedelta(minutes=-240)))
assert instance.xmlvalue(value) == "---01-04:00"

def test_pythonvalue(self):
instance = builtins.gDay()
assert instance.pythonvalue("---01") == (1, None)
assert instance.pythonvalue("---01Z") == (1, pytz.utc)
assert instance.pythonvalue("---01+02:00") == (1, pytz.FixedOffset(120))
assert instance.pythonvalue("---01-04:00") == (1, pytz.FixedOffset(-240))
assert instance.pythonvalue("---01Z") == (1, datetime.timezone.utc)
assert instance.pythonvalue("---01+02:00") == (
1,
datetime.timezone(datetime.timedelta(minutes=120)),
)
assert instance.pythonvalue("---01-04:00") == (
1,
datetime.timezone(datetime.timedelta(minutes=-240)),
)
assert instance.pythonvalue("---15") == (15, None)
assert instance.pythonvalue("---31") == (31, None)
assert instance.pythonvalue("\r\n \t---31 ") == (31, None)
Expand Down