11# datetime.py
22
3- import time as _tmod
4-
5- _DBM = (0 , 0 , 31 , 59 , 90 , 120 , 151 , 181 , 212 , 243 , 273 , 304 , 334 )
6- _DIM = (0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 )
7- _TIME_SPEC = ("auto" , "hours" , "minutes" , "seconds" , "milliseconds" , "microseconds" )
3+ import time as _t
84
95
106def _leap (y ):
117 return y % 4 == 0 and (y % 100 != 0 or y % 400 == 0 )
128
139
14- def _dby (y ):
15- # year -> number of days before January 1st of year.
16- Y = y - 1
17- return Y * 365 + Y // 4 - Y // 100 + Y // 400
18-
19-
2010def _dim (y , m ):
2111 # year, month -> number of days in that month in that year.
2212 if m == 2 and _leap (y ):
2313 return 29
24- return _DIM [m ]
14+ return ( 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 ) [m ]
2515
2616
2717def _dbm (y , m ):
2818 # year, month -> number of days in year preceding first day of month.
29- return _DBM [m ] + (m > 2 and _leap (y ))
30-
31-
32- def _ymd2o (y , m , d ):
33- # y, month, day -> ordinal, considering 01-Jan-0001 as day 1.
34- return _dby (y ) + _dbm (y , m ) + d
19+ return (0 , 0 , 31 , 59 , 90 , 120 , 151 , 181 , 212 , 243 , 273 , 304 , 334 )[m ] + (m > 2 and _leap (y ))
3520
3621
3722def _o2ymd (n ):
@@ -73,7 +58,7 @@ def total_seconds(self):
7358
7459 @property
7560 def days (self ):
76- return self ._tuple (2 )[ 0 ]
61+ return self ._tuple (1 )
7762
7863 @property
7964 def seconds (self ):
@@ -145,17 +130,17 @@ def __bool__(self):
145130 return self ._us != 0
146131
147132 def __str__ (self ):
148- return self ._format (0x40 )
133+ return self ._fmt (0x40 )
149134
150135 def __hash__ (self ):
151136 if not hasattr (self , "_hash" ):
152137 self ._hash = hash (self ._us )
153138 return self ._hash
154139
155140 def isoformat (self ):
156- return self ._format (0 )
141+ return self ._fmt (0 )
157142
158- def _format (self , spec = 0 ):
143+ def _fmt (self , spec = 0 ):
159144 if self ._us >= 0 :
160145 td = self
161146 g = ""
@@ -201,8 +186,8 @@ def tuple(self):
201186
202187 def _tuple (self , n ):
203188 d , us = divmod (self ._us , 86_400_000_000 )
204- if n == 2 :
205- return d , us
189+ if n == 1 :
190+ return d
206191 s , us = divmod (us , 1_000_000 )
207192 if n == 3 :
208193 return d , s , us
@@ -241,7 +226,7 @@ def fromutc(self, dt):
241226 return dt + dtdst
242227
243228 def isoformat (self , dt ):
244- return self .utcoffset (dt )._format (0x12 )
229+ return self .utcoffset (dt )._fmt (0x12 )
245230
246231
247232class timezone (tzinfo ):
@@ -276,7 +261,7 @@ def dst(self, dt):
276261 def tzname (self , dt ):
277262 if self ._name :
278263 return self ._name
279- return self ._offset ._format (0x22 )
264+ return self ._offset ._fmt (0x22 )
280265
281266 def fromutc (self , dt ):
282267 return dt + self ._offset
@@ -287,7 +272,11 @@ def fromutc(self, dt):
287272
288273def _date (y , m , d ):
289274 if MINYEAR <= y <= MAXYEAR and 1 <= m <= 12 and 1 <= d <= _dim (y , m ):
290- return _ymd2o (y , m , d )
275+ # year -> number of days before January 1st of year.
276+ Y = y - 1
277+ _dby = Y * 365 + Y // 4 - Y // 100 + Y // 400
278+ # y, month, day -> ordinal, considering 01-Jan-0001 as day 1.
279+ return _dby + _dbm (y , m ) + d
291280 elif y == 0 and m == 0 and 1 <= d <= 3_652_059 :
292281 return d
293282 else :
@@ -310,11 +299,11 @@ def __init__(self, year, month, day):
310299
311300 @classmethod
312301 def fromtimestamp (cls , ts ):
313- return cls (* _tmod .localtime (ts )[:3 ])
302+ return cls (* _t .localtime (ts )[:3 ])
314303
315304 @classmethod
316305 def today (cls ):
317- return cls (* _tmod .localtime ()[:3 ])
306+ return cls (* _t .localtime ()[:3 ])
318307
319308 @classmethod
320309 def fromordinal (cls , n ):
@@ -490,7 +479,9 @@ def _iso2t(s):
490479
491480
492481def _t2iso (td , timespec , dt , tz ):
493- s = td ._format (_TIME_SPEC .index (timespec ))
482+ s = td ._fmt (
483+ ("auto" , "hours" , "minutes" , "seconds" , "milliseconds" , "microseconds" ).index (timespec )
484+ )
494485 if tz is not None :
495486 s += tz .isoformat (dt )
496487 return s
@@ -633,15 +624,18 @@ def fromtimestamp(cls, ts, tz=None):
633624 else :
634625 us = 0
635626 if tz is None :
636- raise NotImplementedError
627+ dt = cls (* _t .localtime (ts )[:6 ], microsecond = us , tzinfo = tz )
628+ s = (dt - datetime (* _t .localtime (ts - 86400 )[:6 ]))._us // 1_000_000 - 86400
629+ if s < 0 and dt == datetime (* _t .localtime (ts + s )[:6 ]):
630+ dt ._fd = 1
637631 else :
638- dt = cls (* _tmod .gmtime (ts )[:6 ], microsecond = us , tzinfo = tz )
632+ dt = cls (* _t .gmtime (ts )[:6 ], microsecond = us , tzinfo = tz )
639633 dt = tz .fromutc (dt )
640634 return dt
641635
642636 @classmethod
643637 def now (cls , tz = None ):
644- return cls .fromtimestamp (_tmod .time (), tz )
638+ return cls .fromtimestamp (_t .time (), tz )
645639
646640 @classmethod
647641 def fromordinal (cls , n ):
@@ -810,13 +804,45 @@ def astimezone(self, tz=None):
810804 return self
811805 _tz = self ._tz
812806 if _tz is None :
813- raise NotImplementedError
807+ ts = int (self ._mktime ())
808+ os = datetime (* _t .localtime (ts )[:6 ]) - datetime (* _t .gmtime (ts )[:6 ])
814809 else :
815810 os = _tz .utcoffset (self )
816811 utc = self - os
817812 utc = utc .replace (tzinfo = tz )
818813 return tz .fromutc (utc )
819814
815+ def _mktime (self ):
816+ def local (u ):
817+ return (datetime (* _t .localtime (u )[:6 ]) - epoch )._us // 1_000_000
818+
819+ epoch = datetime .EPOCH .replace (tzinfo = None )
820+ t , us = divmod ((self - epoch )._us , 1_000_000 )
821+ ts = None
822+
823+ a = local (t ) - t
824+ u1 = t - a
825+ t1 = local (u1 )
826+ if t1 == t :
827+ u2 = u1 + (86400 if self .fold else - 86400 )
828+ b = local (u2 ) - u2
829+ if a == b :
830+ ts = u1
831+ else :
832+ b = t1 - u1
833+ if ts is None :
834+ u2 = t - b
835+ t2 = local (u2 )
836+ if t2 == t :
837+ ts = u2
838+ elif t1 == t :
839+ ts = u1
840+ elif self .fold :
841+ ts = min (u1 , u2 )
842+ else :
843+ ts = max (u1 , u2 )
844+ return ts + us / 1_000_000
845+
820846 def utcoffset (self ):
821847 return None if self ._tz is None else self ._tz .utcoffset (self )
822848
@@ -828,10 +854,10 @@ def tzname(self):
828854
829855 def timetuple (self ):
830856 if self ._tz is None :
831- conv = _tmod .gmtime
857+ conv = _t .gmtime
832858 epoch = datetime .EPOCH .replace (tzinfo = None )
833859 else :
834- conv = _tmod .localtime
860+ conv = _t .localtime
835861 epoch = datetime .EPOCH
836862 return conv (round ((self - epoch ).total_seconds ()))
837863
@@ -840,7 +866,7 @@ def toordinal(self):
840866
841867 def timestamp (self ):
842868 if self ._tz is None :
843- raise NotImplementedError
869+ return self . _mktime ()
844870 else :
845871 return (self - datetime .EPOCH ).total_seconds ()
846872
@@ -874,4 +900,4 @@ def tuple(self):
874900 return d + t + (self ._tz , self ._fd )
875901
876902
877- datetime .EPOCH = datetime (* _tmod .gmtime (0 )[:6 ], tzinfo = timezone .utc )
903+ datetime .EPOCH = datetime (* _t .gmtime (0 )[:6 ], tzinfo = timezone .utc )
0 commit comments