Skip to content

Commit 4ca6185

Browse files
committed
add ns
1 parent 6687799 commit 4ca6185

File tree

6 files changed

+275
-183
lines changed

6 files changed

+275
-183
lines changed

Include/datetime.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ typedef struct
134134
(((PyDateTime_DateTime*)(o))->data[8] << 8) | \
135135
((PyDateTime_DateTime*)(o))->data[9])
136136
#define PyDateTime_DATE_GET_NANOSECOND(o) \
137-
(((PyDateTime_DateTime*)(o))->data[10] << 8) | \
137+
((((PyDateTime_DateTime*)(o))->data[10] << 8) | \
138138
((PyDateTime_DateTime*)(o))->data[11])
139139
#define PyDateTime_DATE_GET_FOLD(o) (((PyDateTime_DateTime*)(o))->fold)
140140
#define PyDateTime_DATE_GET_TZINFO(o) (_PyDateTime_HAS_TZINFO((o)) ? \
@@ -149,7 +149,7 @@ typedef struct
149149
(((PyDateTime_Time*)(o))->data[4] << 8) | \
150150
((PyDateTime_Time*)(o))->data[5])
151151
#define PyDateTime_TIME_GET_NANOSECOND(o) \
152-
(((PyDateTime_Time*)(o))->data[6] << 8) | \
152+
((((PyDateTime_Time*)(o))->data[6] << 8) | \
153153
((PyDateTime_Time*)(o))->data[7])
154154
#define PyDateTime_TIME_GET_FOLD(o) (((PyDateTime_Time*)(o))->fold)
155155
#define PyDateTime_TIME_GET_TZINFO(o) (_PyDateTime_HAS_TZINFO(o) ? \
@@ -233,15 +233,15 @@ static PyDateTime_CAPI *PyDateTimeAPI = NULL;
233233

234234
#define PyDateTime_FromDateAndTime(year, month, day, hour, min, sec, usec, nanosec) \
235235
PyDateTimeAPI->DateTime_FromDateAndTime((year), (month), (day), (hour), \
236-
(min), (sec), (usec), Py_None, Py_None, (nanosec), PyDateTimeAPI->DateTimeType)
236+
(min), (sec), (usec), Py_None, (nanosec), PyDateTimeAPI->DateTimeType)
237237

238-
#define PyDateTime_FromDateAndTimeAndFold(year, month, day, hour, min, sec, usec, nanosec, fold) \
238+
#define PyDateTime_FromDateAndTimeAndFold(year, month, day, hour, min, sec, usec, fold, nanosec) \
239239
PyDateTimeAPI->DateTime_FromDateAndTimeAndFold((year), (month), (day), (hour), \
240240
(min), (sec), (usec), Py_None, (fold), (nanosec), PyDateTimeAPI->DateTimeType)
241241

242242
#define PyTime_FromTime(hour, minute, second, usecond, nanosec) \
243243
PyDateTimeAPI->Time_FromTime((hour), (minute), (second), (usecond), \
244-
Py_None, Py_None, (nanosec), PyDateTimeAPI->TimeType)
244+
Py_None, (nanosec), PyDateTimeAPI->TimeType)
245245

246246
#define PyTime_FromTimeAndFold(hour, minute, second, usecond, nanosec, fold) \
247247
PyDateTimeAPI->Time_FromTimeAndFold((hour), (minute), (second), (usecond), \

Lib/_pydatetime.py

Lines changed: 58 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -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):
992999
timedelta.min = timedelta(-999999999)
9931000
timedelta.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

9971004
class 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

18001809
time.min = time(0, 0, 0)
18011810
time.max = time(23, 59, 59, 999999, nanosecond=999)
1802-
time.resolution = timedelta(microseconds=1)
1811+
time.resolution = timedelta(nanoseconds=1)
18031812

18041813

18051814
class 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

24642480
datetime.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

24692485
def _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

Comments
 (0)