@@ -175,7 +175,7 @@ def _format_time(hh, mm, ss, us, ns, timespec='auto'):
175175
176176 if timespec == 'auto' :
177177 # Skip trailing microseconds when us==0.
178- timespec = 'microseconds' if us else 'seconds'
178+ timespec = 'nanoseconds' if ns else ' microseconds' if us else 'seconds'
179179 elif timespec == 'milliseconds' :
180180 us //= 1000
181181 try :
@@ -196,11 +196,14 @@ def _format_offset(off, sep=':'):
196196 hh , mm = divmod (off , timedelta (hours = 1 ))
197197 mm , ss = divmod (mm , timedelta (minutes = 1 ))
198198 s += "%s%02d%s%02d" % (sign , hh , sep , mm )
199- if ss or ss .microseconds :
199+ if ss or ss .microseconds or ss . nanoseconds :
200200 s += "%s%02d" % (sep , ss .seconds )
201201
202- if ss .microseconds :
202+ if ss .microseconds or ss . nanoseconds :
203203 s += '.%06d' % ss .microseconds
204+
205+ if ss .nanoseconds :
206+ s += '.%03d' % ss .nanoseconds
204207 return s
205208
206209_normalize_century = None
@@ -514,7 +517,8 @@ def _parse_isoformat_time(tstr):
514517 tzsign = - 1 if tstr [tz_pos - 1 ] == '-' else 1
515518
516519 td = timedelta (hours = tz_comps [0 ], minutes = tz_comps [1 ],
517- seconds = tz_comps [2 ], microseconds = tz_comps [3 ])
520+ seconds = tz_comps [2 ], microseconds = tz_comps [3 ],
521+ nanoseconds = tz_comps [4 ])
518522
519523 tzi = timezone (tzsign * td )
520524
@@ -690,7 +694,7 @@ def __new__(cls, days=0, seconds=0, microseconds=0, nanoseconds=0,
690694 # s and us fit in 32-bit signed ints; d isn't bounded.
691695 d = s = us = ns = 0
692696
693- # Normalize everything to days, seconds, microseconds, nanoseconds.
697+ # Normalize everything to days, seconds, microseconds
694698 days += weeks * 7
695699 seconds += minutes * 60 + hours * 3600
696700 microseconds += milliseconds * 1000
@@ -758,6 +762,7 @@ def __new__(cls, days=0, seconds=0, microseconds=0, nanoseconds=0,
758762 # Normalize nanoseconds to microseconds.
759763 microseconds1 , ns = divmod (nanoseconds , 1000 )
760764 microseconds += microseconds1
765+ assert isinstance (ns , int ) and 0 <= ns < 1000
761766
762767 # Just a little bit of carrying possible for microseconds and seconds.
763768 seconds , us = divmod (microseconds , 1000000 )
@@ -805,10 +810,11 @@ def __str__(self):
805810 def plural (n ):
806811 return n , abs (n ) != 1 and "s" or ""
807812 s = ("%d day%s, " % plural (self ._days )) + s
808- if self ._microseconds :
813+ if self ._microseconds or self . _nanoseconds :
809814 s = s + ".%06d" % self ._microseconds
810- if self ._nanoseconds :
811- s = s + "%03d" % self ._nanoseconds
815+
816+ if self ._nanoseconds :
817+ s = s + ".%03d" % self ._nanoseconds
812818 return s
813819
814820 def total_seconds (self ):
@@ -979,7 +985,8 @@ def __hash__(self):
979985 def __bool__ (self ):
980986 return (self ._days != 0 or
981987 self ._seconds != 0 or
982- self ._microseconds != 0 )
988+ self ._microseconds != 0 or
989+ self ._nanoseconds != 0 )
983990
984991 # Pickle support.
985992
@@ -992,7 +999,7 @@ def __reduce__(self):
992999timedelta .min = timedelta (- 999999999 )
9931000timedelta .max = timedelta (days = 999999999 , hours = 23 , minutes = 59 , seconds = 59 ,
9941001 microseconds = 999999 , nanoseconds = 999 )
995- timedelta .resolution = timedelta (microseconds = 1 )
1002+ timedelta .resolution = timedelta (nanoseconds = 1 )
9961003
9971004class date :
9981005 """Concrete date type.
@@ -1463,6 +1470,7 @@ def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold
14631470 second, microsecond (default to zero)
14641471 tzinfo (default to None)
14651472 fold (keyword only, default to zero)
1473+ nanosecond (keyword only, default to zero)
14661474 """
14671475 if (isinstance (hour , (bytes , str )) and len (hour ) == 6 and
14681476 ord (hour [0 :1 ])& 0x7F < 24 ):
@@ -1584,18 +1592,18 @@ def _cmp(self, other, allow_mixed=False):
15841592
15851593 if base_compare :
15861594 return _cmp ((self ._hour , self ._minute , self ._second ,
1587- self ._microsecond ),
1595+ self ._microsecond , self . _nanosecond ),
15881596 (other ._hour , other ._minute , other ._second ,
1589- other ._microsecond ))
1597+ other ._microsecond , other . _nanosecond ))
15901598 if myoff is None or otoff is None :
15911599 if allow_mixed :
15921600 return 2 # arbitrary non-zero value
15931601 else :
15941602 raise TypeError ("cannot compare naive and aware times" )
15951603 myhhmm = self ._hour * 60 + self ._minute - myoff // timedelta (minutes = 1 )
15961604 othhmm = other ._hour * 60 + other ._minute - otoff // timedelta (minutes = 1 )
1597- return _cmp ((myhhmm , self ._second , self ._microsecond ),
1598- (othhmm , other ._second , other ._microsecond ))
1605+ return _cmp ((myhhmm , self ._second , self ._microsecond , self . _nanosecond ),
1606+ (othhmm , other ._second , other ._microsecond , other . _nanosecond ))
15991607
16001608 def __hash__ (self ):
16011609 """Hash."""
@@ -1650,12 +1658,12 @@ def __repr__(self):
16501658 def isoformat (self , timespec = 'auto' ):
16511659 """Return the time formatted according to ISO.
16521660
1653- The full format is 'HH:MM:SS.mmmmmm +zz:zz'. By default, the fractional
1654- part is omitted if self.microsecond == 0 .
1661+ The full format is 'HH:MM:SS.mmmmmmmmm +zz:zz'. By default, the fractional
1662+ part is truncated based on the value of self.nanosecond and self.microsecond .
16551663
16561664 The optional argument timespec specifies the number of additional
16571665 terms of the time to include. Valid options are 'auto', 'hours',
1658- 'minutes', 'seconds', 'milliseconds' and 'microseconds '.
1666+ 'minutes', 'seconds', 'milliseconds', 'microseconds' and 'nanoseconds '.
16591667 """
16601668 s = _format_time (self ._hour , self ._minute , self ._second ,
16611669 self ._microsecond , self ._nanosecond , timespec )
@@ -1765,11 +1773,12 @@ def replace(self, hour=None, minute=None, second=None, microsecond=None,
17651773 def _getstate (self , protocol = 3 ):
17661774 us2 , us3 = divmod (self ._microsecond , 256 )
17671775 us1 , us2 = divmod (us2 , 256 )
1776+ ns1 , ns2 = divmod (self ._nanosecond , 256 )
17681777 h = self ._hour
17691778 if self ._fold and protocol > 3 :
17701779 h += 128
17711780 basestate = bytes ([h , self ._minute , self ._second ,
1772- us1 , us2 , us3 ])
1781+ us1 , us2 , us3 , ns1 , ns2 ])
17731782 if self ._tzinfo is None :
17741783 return (basestate ,)
17751784 else :
@@ -1799,7 +1808,7 @@ def __reduce__(self):
17991808
18001809time .min = time (0 , 0 , 0 )
18011810time .max = time (23 , 59 , 59 , 999999 , nanosecond = 999 )
1802- time .resolution = timedelta (microseconds = 1 )
1811+ time .resolution = timedelta (nanoseconds = 1 )
18031812
18041813
18051814class datetime (date ):
@@ -1988,7 +1997,7 @@ def combine(cls, date, time, tzinfo=True):
19881997 tzinfo = time .tzinfo
19891998 return cls (date .year , date .month , date .day ,
19901999 time .hour , time .minute , time .second , time .microsecond ,
1991- tzinfo , fold = time .fold )
2000+ tzinfo , fold = time .fold , nanosecond = time . nanosecond )
19922001
19932002 @classmethod
19942003 def fromisoformat (cls , date_string ):
@@ -2018,7 +2027,7 @@ def fromisoformat(cls, date_string):
20182027 f'Invalid isoformat string: { date_string !r} ' ) from None
20192028 else :
20202029 if error_from_components :
2021- raise ValueError ("minute, second, and microsecond must be 0 when hour is 24" )
2030+ raise ValueError ("minute, second, microsecond and nanosecond must be 0 when hour is 24" )
20222031
20232032 if became_next_day :
20242033 year , month , day = date_components
@@ -2114,11 +2123,11 @@ def time(self):
21142123 def timetz (self ):
21152124 "Return the time part, with same tzinfo."
21162125 return time (self .hour , self .minute , self .second , self .microsecond ,
2117- self ._tzinfo , fold = self .fold )
2126+ self ._tzinfo , fold = self .fold , nanosecond = self . nanosecond )
21182127
21192128 def replace (self , year = None , month = None , day = None , hour = None ,
21202129 minute = None , second = None , microsecond = None , tzinfo = True ,
2121- * , fold = None ):
2130+ * , fold = None , nanosecond = None ):
21222131 """Return a new datetime with new values for the specified fields."""
21232132 if year is None :
21242133 year = self .year
@@ -2134,12 +2143,14 @@ def replace(self, year=None, month=None, day=None, hour=None,
21342143 second = self .second
21352144 if microsecond is None :
21362145 microsecond = self .microsecond
2146+ if nanosecond is None :
2147+ nanosecond = self .nanosecond
21372148 if tzinfo is True :
21382149 tzinfo = self .tzinfo
21392150 if fold is None :
21402151 fold = self .fold
21412152 return type (self )(year , month , day , hour , minute , second ,
2142- microsecond , tzinfo , fold = fold )
2153+ microsecond , tzinfo , fold = fold , nanosecond = nanosecond )
21432154
21442155 __replace__ = replace
21452156
@@ -2200,11 +2211,11 @@ def ctime(self):
22002211 def isoformat (self , sep = 'T' , timespec = 'auto' ):
22012212 """Return the time formatted according to ISO.
22022213
2203- The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm '.
2204- By default, the fractional part is omitted if self.microsecond == 0 .
2214+ The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmmmmm '.
2215+ By default, the fractional part is truncated based on the value of self.nanosecond and self.microsecond .
22052216
22062217 If self.tzinfo is not None, the UTC offset is also attached, giving
2207- giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm +HH:MM'.
2218+ giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmmmmm +HH:MM'.
22082219
22092220 Optional argument sep specifies the separator between date and
22102221 time, default 'T'.
@@ -2347,10 +2358,10 @@ def _cmp(self, other, allow_mixed=False):
23472358 if base_compare :
23482359 return _cmp ((self ._year , self ._month , self ._day ,
23492360 self ._hour , self ._minute , self ._second ,
2350- self ._microsecond ),
2361+ self ._microsecond , self . _nanosecond ),
23512362 (other ._year , other ._month , other ._day ,
23522363 other ._hour , other ._minute , other ._second ,
2353- other ._microsecond ))
2364+ other ._microsecond , other . _nanosecond ))
23542365 if myoff is None or otoff is None :
23552366 if allow_mixed :
23562367 return 2 # arbitrary non-zero value
@@ -2370,15 +2381,17 @@ def __add__(self, other):
23702381 hours = self ._hour ,
23712382 minutes = self ._minute ,
23722383 seconds = self ._second ,
2373- microseconds = self ._microsecond )
2384+ microseconds = self ._microsecond ,
2385+ nanoseconds = self ._nanosecond )
23742386 delta += other
23752387 hour , rem = divmod (delta .seconds , 3600 )
23762388 minute , second = divmod (rem , 60 )
23772389 if 0 < delta .days <= _MAXORDINAL :
23782390 return type (self ).combine (date .fromordinal (delta .days ),
23792391 time (hour , minute , second ,
23802392 delta .microseconds ,
2381- tzinfo = self ._tzinfo ))
2393+ tzinfo = self ._tzinfo ,
2394+ nanosecond = delta .nanosecond ))
23822395 raise OverflowError ("result out of range" )
23832396
23842397 __radd__ = __add__
@@ -2396,7 +2409,8 @@ def __sub__(self, other):
23962409 secs2 = other ._second + other ._minute * 60 + other ._hour * 3600
23972410 base = timedelta (days1 - days2 ,
23982411 secs1 - secs2 ,
2399- self ._microsecond - other ._microsecond )
2412+ self ._microsecond - other ._microsecond ,
2413+ self ._nanosecond - other ._nanosecond )
24002414 if self ._tzinfo is other ._tzinfo :
24012415 return base
24022416 myoff = self .utcoffset ()
@@ -2419,7 +2433,7 @@ def __hash__(self):
24192433 else :
24202434 days = _ymd2ord (self .year , self .month , self .day )
24212435 seconds = self .hour * 3600 + self .minute * 60 + self .second
2422- self ._hashcode = hash (timedelta (days , seconds , self .microsecond ) - tzoff )
2436+ self ._hashcode = hash (timedelta (days , seconds , self .microsecond , self . nanosecond ) - tzoff )
24232437 return self ._hashcode
24242438
24252439 # Pickle support.
@@ -2428,12 +2442,13 @@ def _getstate(self, protocol=3):
24282442 yhi , ylo = divmod (self ._year , 256 )
24292443 us2 , us3 = divmod (self ._microsecond , 256 )
24302444 us1 , us2 = divmod (us2 , 256 )
2445+ ns1 , ns2 = divmod (self ._nanosecond , 256 )
24312446 m = self ._month
24322447 if self ._fold and protocol > 3 :
24332448 m += 128
24342449 basestate = bytes ([yhi , ylo , m , self ._day ,
24352450 self ._hour , self ._minute , self ._second ,
2436- us1 , us2 , us3 ])
2451+ us1 , us2 , us3 , ns1 , ns2 ])
24372452 if self ._tzinfo is None :
24382453 return (basestate ,)
24392454 else :
@@ -2443,7 +2458,7 @@ def __setstate(self, string, tzinfo):
24432458 if tzinfo is not None and not isinstance (tzinfo , _tzinfo_class ):
24442459 raise TypeError ("bad tzinfo state arg" )
24452460 (yhi , ylo , m , self ._day , self ._hour ,
2446- self ._minute , self ._second , us1 , us2 , us3 ) = string
2461+ self ._minute , self ._second , us1 , us2 , us3 , ns1 , ns2 ) = string
24472462 if m > 127 :
24482463 self ._fold = 1
24492464 self ._month = m - 128
@@ -2452,6 +2467,7 @@ def __setstate(self, string, tzinfo):
24522467 self ._month = m
24532468 self ._year = yhi * 256 + ylo
24542469 self ._microsecond = (((us1 << 8 ) | us2 ) << 8 ) | us3
2470+ self ._nanosecond = ((ns1 << 8 ) | ns2 ) << 8
24552471 self ._tzinfo = tzinfo
24562472
24572473 def __reduce_ex__ (self , protocol ):
@@ -2462,8 +2478,8 @@ def __reduce__(self):
24622478
24632479
24642480datetime .min = datetime (1 , 1 , 1 )
2465- datetime .max = datetime (9999 , 12 , 31 , 23 , 59 , 59 , 999999 )
2466- datetime .resolution = timedelta (microseconds = 1 )
2481+ datetime .max = datetime (9999 , 12 , 31 , 23 , 59 , 59 , 999999 , nanosecond = 999 )
2482+ datetime .resolution = timedelta (nanoseconds = 1 )
24672483
24682484
24692485def _isoweek1monday (year ):
@@ -2573,7 +2589,7 @@ def fromutc(self, dt):
25732589 raise TypeError ("fromutc() argument must be a datetime instance"
25742590 " or None" )
25752591
2576- _maxoffset = timedelta (hours = 24 , microseconds = - 1 )
2592+ _maxoffset = timedelta (hours = 24 , nanoseconds = - 1 )
25772593 _minoffset = - _maxoffset
25782594
25792595 @staticmethod
@@ -2589,6 +2605,10 @@ def _name_from_offset(delta):
25892605 minutes , rest = divmod (rest , timedelta (minutes = 1 ))
25902606 seconds = rest .seconds
25912607 microseconds = rest .microseconds
2608+ nanoseconds = rest .nanoseconds
2609+ if nanoseconds :
2610+ return (f'UTC{ sign } { hours :02d} :{ minutes :02d} :{ seconds :02d} '
2611+ f'.{ microseconds :06d} { nanoseconds :03d} ' )
25922612 if microseconds :
25932613 return (f'UTC{ sign } { hours :02d} :{ minutes :02d} :{ seconds :02d} '
25942614 f'.{ microseconds :06d} ' )
0 commit comments