1、使用datetime.strptime()转换
from datetime import datetime dt = datetime.now() iso_datetime_string = dt.isoformat() # >>> '2019-10-20T15:54:53.840416' datetime.strptime(iso_datetime_string,"%Y-%m-%dT%H:%M:%S.%f") # >>> datetime.datetime(2019, 10, 20, 15, 54, 53, 840416)
2、实现fromisoformat()方法
fromisoformat()
函数用于从包含 ISO 格式日期的指定字符串构造日期对象。Python 3.7中可以直接使用,之前版本可以参考实现,代码如下:
from datetime import timedelta, time, date, timezone, tzinfo from datetime import datetime as system_datetime import time as _time import math as _math class datetime(system_datetime): def __init__(self, *a, **k): self._fold = 1 # idk what this does @classmethod def fromisoformat(cls, date_string): """ IPCP FW uses python 3.5 (as of Jan 2022) which does not have some helpful methods like .fromisoformat(). This adds some functionality that is found in python 3.8 Copied this directly (mostly) from the python 3.8 datetime module. Construct a datetime from the output of datetime.isoformat().""" if not isinstance(date_string, str): raise TypeError('fromisoformat: argument must be str') # Split this at the separator dstr = date_string[0:10] tstr = date_string[11:] try: date_components = _parse_isoformat_date(dstr) except ValueError: raise ValueError('Invalid isoformat string: {}'.format(date_string)) if tstr: try: time_components = _parse_isoformat_time(tstr) except ValueError: raise ValueError('Invalid isoformat string: {date_string}'.format(date_string)) else: time_components = [0, 0, 0, 0, None] return cls(*(date_components + time_components)) def astimezone(self, tz=None): # converts to local timezone if tz=None if tz is None: tz = self._local_timezone() elif not isinstance(tz, tzinfo): raise TypeError("tz argument must be an instance of tzinfo") mytz = self.tzinfo if mytz is None: mytz = self._local_timezone() myoffset = mytz.utcoffset(self) else: myoffset = mytz.utcoffset(self) if myoffset is None: mytz = self.replace(tzinfo=None)._local_timezone() myoffset = mytz.utcoffset(self) if tz is mytz: return self # Convert self to UTC, and attach the new time zone object. utc = (self - myoffset).replace(tzinfo=tz) # Convert from UTC to tz's local time. return tz.fromutc(utc) def _local_timezone(self): if self.tzinfo is None: ts = self._mktime() else: ts = (self - _EPOCH) // timedelta(seconds=1) localtm = _time.localtime(ts) local = datetime(*localtm[:6]) # Extract TZ data gmtoff = localtm.tm_gmtoff zone = localtm.tm_zone return timezone(timedelta(seconds=gmtoff), zone) def _mktime(self): """Return integer POSIX timestamp.""" epoch = datetime(1970, 1, 1) max_fold_seconds = 24 * 3600 t = (self - epoch) // timedelta(0, 1) def local(u): y, m, d, hh, mm, ss = _time.localtime(u)[:6] return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1) # Our goal is to solve t = local(u) for u. a = local(t) - t u1 = t - a t1 = local(u1) if t1 == t: # We found one solution, but it may not be the one we need. # Look for an earlier solution (if `fold` is 0), or a # later one (if `fold` is 1). u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold] b = local(u2) - u2 if a == b: return u1 else: b = t1 - u1 assert a != b u2 = t - b t2 = local(u2) if t2 == t: return u2 if t1 == t: return u1 # We have found both offsets a and b, but neither t - a nor t - b is # a solution. This means t is in the gap. return (max, min)[self.fold](u1, u2) def setstate(self, string, tzinfo): if tzinfo is not None and not isinstance(tzinfo): raise TypeError("bad tzinfo state arg") (yhi, ylo, m, self._day, self._hour, self._minute, self._second, us1, us2, us3) = string if m > 127: self._fold = 1 self._month = m - 128 else: self._fold = 0 self._month = m self._year = yhi * 256 + ylo self._microsecond = (((us1 << 8) | us2) << 8) | us3 self._tzinfo = tzinfo @property def fold(self): return self._fold _EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc) MINYEAR = 1 MAXYEAR = 9999 _MAXORDINAL = 3652059 # date.max.toordinal() # Helpers for parsing the result of isoformat() def _parse_isoformat_date(dtstr): # It is assumed that this function will only be called with a # string of length exactly 10, and (though this is not used) ASCII-only year = int(dtstr[0:4]) if dtstr[4] != '-': raise ValueError('Invalid date separator: %s' % dtstr[4]) month = int(dtstr[5:7]) if dtstr[7] != '-': raise ValueError('Invalid date separator') day = int(dtstr[8:10]) return [year, month, day] def _parse_hh_mm_ss_ff(tstr): # Parses things of the form HH[:MM[:SS[.fff[fff]]]] len_str = len(tstr) time_comps = [0, 0, 0, 0] pos = 0 for comp in range(0, 3): if (len_str - pos) < 2: raise ValueError('Incomplete time component') time_comps[comp] = int(tstr[pos:pos + 2]) pos += 2 next_char = tstr[pos:pos + 1] if not next_char or comp >= 2: break if next_char != ':': raise ValueError('Invalid time separator: %c' % next_char) pos += 1 if pos < len_str: if tstr[pos] != '.': raise ValueError('Invalid microsecond component') else: pos += 1 len_remainder = len_str - pos if len_remainder not in (3, 6): raise ValueError('Invalid microsecond component') time_comps[3] = int(tstr[pos:]) if len_remainder == 3: time_comps[3] *= 1000 return time_comps def _parse_isoformat_time(tstr): # Format supported is HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]] len_str = len(tstr) if len_str < 2: raise ValueError('Isoformat time too short') # This is equivalent to re.search('[+-]', tstr), but faster tz_pos = (tstr.find('-') + 1 or tstr.find('+') + 1) timestr = tstr[:tz_pos - 1] if tz_pos > 0 else tstr time_comps = _parse_hh_mm_ss_ff(timestr) tzi = None if tz_pos > 0: tzstr = tstr[tz_pos:] # Valid time zone strings are: # HH:MM len: 5 # HH:MM:SS len: 8 # HH:MM:SS.ffffff len: 15 if len(tzstr) not in (5, 8, 15): raise ValueError('Malformed time zone string') tz_comps = _parse_hh_mm_ss_ff(tzstr) if all(x == 0 for x in tz_comps): tzi = timezone.utc else: tzsign = -1 if tstr[tz_pos - 1] == '-' else 1 td = timedelta(hours=tz_comps[0], minutes=tz_comps[1], seconds=tz_comps[2], microseconds=tz_comps[3]) tzi = timezone(tzsign * td) time_comps.append(tzi) return time_comps def _check_utc_offset(name, offset): assert name in ("utcoffset", "dst") if offset is None: return if not isinstance(offset, timedelta): raise TypeError("tzinfo.%s() must return None " "or timedelta, not '%s'" % (name, type(offset))) if not -timedelta(1) < offset < timedelta(1): raise ValueError("%s()=%s, must be strictly between " "-timedelta(hours=24) and timedelta(hours=24)" % (name, offset)) def _check_int_field(value): if isinstance(value, int): return value if isinstance(value, float): raise TypeError('integer argument expected, got float') try: value = value.__index__() except AttributeError: pass else: if not isinstance(value, int): raise TypeError('__index__ returned non-int (type %s)' % type(value).__name__) return value orig = value try: value = value.__int__() except AttributeError: pass else: if not isinstance(value, int): raise TypeError('__int__ returned non-int (type %s)' % type(value).__name__) import warnings warnings.warn("an integer is required (got type %s)" % type(orig).__name__, DeprecationWarning, stacklevel=2) return value raise TypeError('an integer is required (got type %s)' % type(value).__name__) def _check_date_fields(year, month, day): year = _check_int_field(year) month = _check_int_field(month) day = _check_int_field(day) if not MINYEAR <= year <= MAXYEAR: raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year) if not 1 <= month <= 12: raise ValueError('month must be in 1..12', month) dim = _days_in_month(year, month) if not 1 <= day <= dim: raise ValueError('day must be in 1..%d' % dim, day) return year, month, day def _check_time_fields(hour, minute, second, microsecond, fold): hour = _check_int_field(hour) minute = _check_int_field(minute) second = _check_int_field(second) microsecond = _check_int_field(microsecond) if not 0 <= hour <= 23: raise ValueError('hour must be in 0..23', hour) if not 0 <= minute <= 59: raise ValueError('minute must be in 0..59', minute) if not 0 <= second <= 59: raise ValueError('second must be in 0..59', second) if not 0 <= microsecond <= 999999: raise ValueError('microsecond must be in 0..999999', microsecond) if fold not in (0, 1): raise ValueError('fold must be either 0 or 1', fold) return hour, minute, second, microsecond, fold def _check_tzinfo_arg(tz): if tz is not None and not isinstance(tz, tzinfo): raise TypeError("tzinfo argument must be None or of a tzinfo subclass") def _cmperror(x, y): raise TypeError("can't compare '%s' to '%s'" % ( type(x).__name__, type(y).__name__)) def _divide_and_round(a, b): """divide a by b and round result to the nearest integer When the ratio is exactly half-way between two integers, the even integer is returned. """ # Based on the reference implementation for divmod_near # in Objects/longobject.c. q, r = divmod(a, b) # round up if either r / b > 0.5, or r / b == 0.5 and q is odd. # The expression r / b > 0.5 is equivalent to 2 * r > b if b is # positive, 2 * r < b if b negative. r *= 2 greater_than_half = r > b if b > 0 else r < b if greater_than_half or r == b and q % 2 == 1: q += 1 return q def _days_in_month(year, month): "year, month -> number of days in that month in that year." assert 1 <= month <= 12, month if month == 2 and _is_leap(year): return 29 return _DAYS_IN_MONTH[month] _DAYS_IN_MONTH = [-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] def _is_leap(year): "year -> 1 if leap year, else 0." return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
参考文档:https://github.com/cjavapy/gs_datetime/blob/master/gs_datetime.py