csvw icon indicating copy to clipboard operation
csvw copied to clipboard

Check alternatives for dependency on python-dateutil

Open xrotwang opened this issue 5 months ago • 1 comments

See https://github.com/dateutil/dateutil/issues/1404

xrotwang avatar Aug 14 '25 07:08 xrotwang

For python >= 3.11 the following changes remove the dependency on python-dateutil:

diff --git a/src/csvw/datatypes.py b/src/csvw/datatypes.py
index ad17973..e7c155b 100644
--- a/src/csvw/datatypes.py
+++ b/src/csvw/datatypes.py
@@ -22,7 +22,6 @@ import collections
 
 import isodate
 import rfc3986
-import dateutil.parser
 import babel.numbers
 import babel.dates
 import jsonschema
@@ -288,13 +287,26 @@ def with_tz(v, func, args, kw):
         tz = tz.groups()[0]
     res = func(v, *args, **kw)
     if tz:
-        dt = dateutil.parser.parse('{}{}'.format(datetime.datetime.now(), tz))
+        dt = datetime.datetime.strptime(tz, '%z')
         res = datetime.datetime(
             res.year, res.month, res.day, res.hour, res.minute, res.second, res.microsecond,
             dt.tzinfo)
     return res
 
 
+def _tzinfo(v):
+    res = v.split()[-1]
+    try:
+        return datetime.datetime.strptime(res, '%Z').tzinfo
+    except ValueError:
+        pass
+    if re.fullmatch(r'[+-][0-9]{2}', res):
+        res += '00'
+    if ':' in res:
+        res = res.replace(':', '')
+    return datetime.datetime.strptime(res, '%z').tzinfo
+
+
 @register
 class dateTime(anyAtomicType):
     """
@@ -329,8 +341,7 @@ class dateTime(anyAtomicType):
                 comps[a] = getattr(d, a)
         res = cls(**{k: int(v) for k, v in comps.items() if v is not None})
         if tz_marker:
-            # Let dateutils take care of parsing the timezone info:
-            res = res.replace(tzinfo=dateutil.parser.parse(v).tzinfo)
+            res = res.replace(tzinfo=_tzinfo(v))
         return res
 
     @staticmethod
@@ -340,7 +351,7 @@ class dateTime(anyAtomicType):
             if not match:
                 raise ValueError('{} -- {} -- {}'.format(pattern, v, regex))  # pragma:
         try:
-            return dateutil.parser.isoparse(v)
+            return datetime.datetime.fromisoformat(v)
         except ValueError:
             return dateTime._parse(v, datetime.datetime, regex, tz_marker=tz_marker)
 
@@ -419,7 +430,8 @@ class _time(dateTime):
     @staticmethod
     def to_python(v, regex=None, fmt=None, tz_marker=None, pattern=None):
         if pattern and 'x' in pattern.lower():
-            return dateutil.parser.parse('{}T{}'.format(datetime.date.today().isoformat(), v))
+            return datetime.datetime.fromisoformat(
+                '{}T{}'.format(datetime.date.today().isoformat(), v))
         assert regex is not None
         return with_tz(v, dateTime._parse, [datetime.datetime, regex], dict(tz_marker=tz_marker))

xrotwang avatar Aug 14 '25 08:08 xrotwang