@@ -665,12 +665,12 @@ def fromisoformat(cls, date_string):
665
665
666
666
"""
667
667
match = _re .match (
668
- r"([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])" , date_string
668
+ r"([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$ " , date_string
669
669
)
670
670
if match :
671
671
y , m , d = int (match .group (1 )), int (match .group (2 )), int (match .group (3 ))
672
672
return cls (y , m , d )
673
- raise ValueError ("Not a valid ISO Date " )
673
+ raise ValueError ("Invalid isoformat string " )
674
674
675
675
@classmethod
676
676
def today (cls ):
@@ -922,6 +922,90 @@ def tzinfo(self):
922
922
"""
923
923
return self ._tzinfo
924
924
925
+ @staticmethod
926
+ def _parse_iso_string (string_to_parse , segments ):
927
+ results = []
928
+
929
+ for regex in segments :
930
+ match = _re .match (regex , string_to_parse )
931
+ if match :
932
+ for grp in range (regex .count ("(" )):
933
+ results .append (int (match .group (grp + 1 )))
934
+ string_to_parse = string_to_parse [len (match .group (0 )) :]
935
+ elif string_to_parse : # Only raise an error if we're not done yet
936
+ raise ValueError ("Invalid isoformat string" )
937
+ if string_to_parse :
938
+ raise ValueError ("Invalid isoformat string" )
939
+ return results
940
+
941
+ # pylint: disable=too-many-locals
942
+ @classmethod
943
+ def fromisoformat (cls , time_string ):
944
+ """Return a time object constructed from an ISO date format.
945
+ Valid format is ``HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]``
946
+
947
+ """
948
+ match = _re .match (r"(.*)[\-\+]" , time_string )
949
+ offset_string = None
950
+ if match :
951
+ offset_string = time_string [len (match .group (1 )) :]
952
+ time_string = match .group (1 )
953
+
954
+ time_segments = (
955
+ r"([0-9][0-9])" ,
956
+ r":([0-9][0-9])" ,
957
+ r":([0-9][0-9])" ,
958
+ r"\.([0-9][0-9][0-9])" ,
959
+ r"([0-9][0-9][0-9])" ,
960
+ )
961
+ offset_segments = (
962
+ r"([\-\+][0-9][0-9]):([0-9][0-9])" ,
963
+ r":([0-9][0-9])" ,
964
+ r"\.([0-9][0-9][0-9][0-9][0-9][0-9])" ,
965
+ )
966
+
967
+ results = cls ._parse_iso_string (time_string , time_segments )
968
+ if len (results ) < 1 :
969
+ raise ValueError ("Invalid isoformat string" )
970
+ if len (results ) < len (time_segments ):
971
+ results += [None ] * (len (time_segments ) - len (results ))
972
+ if offset_string :
973
+ results += cls ._parse_iso_string (offset_string , offset_segments )
974
+
975
+ hh = results [0 ]
976
+ mm = results [1 ] if len (results ) >= 2 and results [1 ] is not None else 0
977
+ ss = results [2 ] if len (results ) >= 3 and results [2 ] is not None else 0
978
+ us = 0
979
+ if len (results ) >= 4 and results [3 ] is not None :
980
+ us += results [3 ] * 1000
981
+ if len (results ) >= 5 and results [4 ] is not None :
982
+ us += results [4 ]
983
+ tz = None
984
+ if len (results ) >= 7 :
985
+ offset_hh = results [5 ]
986
+ multiplier = - 1 if offset_hh < 0 else 1
987
+ offset_mm = results [6 ] * multiplier
988
+ offset_ss = (results [7 ] if len (results ) >= 8 else 0 ) * multiplier
989
+ offset_us = (results [8 ] if len (results ) >= 9 else 0 ) * multiplier
990
+ offset = timedelta (
991
+ hours = offset_hh ,
992
+ minutes = offset_mm ,
993
+ seconds = offset_ss ,
994
+ microseconds = offset_us ,
995
+ )
996
+ tz = timezone (offset , name = "utcoffset" )
997
+
998
+ result = cls (
999
+ hh ,
1000
+ mm ,
1001
+ ss ,
1002
+ us ,
1003
+ tz ,
1004
+ )
1005
+ return result
1006
+
1007
+ # pylint: enable=too-many-locals
1008
+
925
1009
# Instance methods
926
1010
def isoformat (self , timespec = "auto" ):
927
1011
"""Return a string representing the time in ISO 8601 format, one of:
@@ -1222,41 +1306,20 @@ def fromtimestamp(cls, timestamp, tz=None):
1222
1306
return cls ._fromtimestamp (timestamp , tz is not None , tz )
1223
1307
1224
1308
@classmethod
1225
- def fromisoformat (cls , date_string , tz = None ):
1309
+ def fromisoformat (cls , date_string ):
1226
1310
"""Return a datetime object constructed from an ISO date format.
1227
- Valid format is ``YYYY-MM-DD[*HH[:MM[:SS[.fff[fff]]]]]``
1311
+ Valid format is ``YYYY-MM-DD[*HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]] ]``
1228
1312
1229
1313
"""
1230
- match = _re .match (
1231
- (
1232
- r"([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])(T([0-9][0-9]))?(:([0-9][0-9]))?"
1233
- r"(:([0-9][0-9]))?(\.([0-9][0-9][0-9])([0-9][0-9][0-9])?)?"
1234
- ),
1235
- date_string ,
1236
- )
1237
- if match :
1238
- y , m , d = int (match .group (1 )), int (match .group (2 )), int (match .group (3 ))
1239
- hh = int (match .group (5 )) if match .group (5 ) else 0
1240
- mm = int (match .group (5 )) if match .group (7 ) else 0
1241
- ss = int (match .group (9 )) if match .group (9 ) else 0
1242
- us = 0
1243
- print (match .group (10 ))
1244
- if match .group (11 ):
1245
- us += int (match .group (11 )) * 1000
1246
- if match .group (12 ):
1247
- us += int (match .group (12 ))
1248
- result = cls (
1249
- y ,
1250
- m ,
1251
- d ,
1252
- hh ,
1253
- mm ,
1254
- ss ,
1255
- us ,
1256
- tz ,
1257
- )
1258
- return result
1259
- raise ValueError ("Not a valid ISO Date" )
1314
+ if "T" in date_string :
1315
+ date_string , time_string = date_string .split ("T" )
1316
+ dateval = date .fromisoformat (date_string )
1317
+ timeval = time .fromisoformat (time_string )
1318
+ else :
1319
+ dateval = date .fromisoformat (date_string )
1320
+ timeval = time ()
1321
+
1322
+ return cls .combine (dateval , timeval )
1260
1323
1261
1324
@classmethod
1262
1325
def now (cls , timezone = None ):
0 commit comments