11##
2- # datetime - timewise
2+ # stdlib_datetime - support for the stdlib's datetime.
33#
44# I/O routines for date, time, timetz, timestamp, timestamptz, and interval.
55# Supported by the datetime module.
66##
7- """
8- time_io
9- Floating-point based time I/O.
10-
11- time_noday_io
12- Floating-point based time I/O with noday-intervals.
13-
14- time64_io
15- long-long based time I/O.
16-
17- time64_noday_io
18- long-long based time I/O with noday-intervals.
19- """
207import datetime
218import warnings
229from functools import partial
2310from operator import methodcaller , add
2411
25- from ...python .datetime import UTC , FixedOffset
12+ from ...python .datetime import UTC , FixedOffset , \
13+ infinity_date , infinity_datetime , \
14+ negative_infinity_date , negative_infinity_datetime
2615from ...python .functools import Composition as compose
2716from ...exceptions import TypeConversionWarning
2817
4736pg_epoch_datetime = datetime .datetime (2000 , 1 , 1 )
4837pg_epoch_date = pg_epoch_datetime .date ()
4938pg_date_offset = pg_epoch_date .toordinal ()
39+ pg_minus_date_offset = - pg_date_offset
5040
5141## Difference between PostgreSQL epoch and Unix epoch.
5242## Used to convert a PostgreSQL ordinal to an ordinal usable by datetime
5343pg_time_days = (pg_date_offset - datetime .date (1970 , 1 , 1 ).toordinal ())
5444
55- toordinal = methodcaller ("toordinal" )
5645convert_to_utc = methodcaller ('astimezone' , UTC )
5746remove_tzinfo = methodcaller ('replace' , tzinfo = None )
5847set_as_utc = methodcaller ('replace' , tzinfo = UTC )
5948
49+ ##
50+ # Constants used to special case infinity and -infinity.
51+ time64_pack_constants = {
52+ infinity_datetime : lib .time64_infinity ,
53+ negative_infinity_datetime : lib .time64_negative_infinity ,
54+ 'infinity' : lib .time64_infinity ,
55+ '-infinity' : lib .time64_negative_infinity ,
56+ }
57+ time_pack_constants = {
58+ infinity_datetime : lib .time_infinity ,
59+ negative_infinity_datetime : lib .time_negative_infinity ,
60+ 'infinity' : lib .time_infinity ,
61+ '-infinity' : lib .time_negative_infinity ,
62+ }
63+ date_pack_constants = {
64+ infinity_date : lib .date_infinity ,
65+ negative_infinity_date : lib .date_negative_infinity ,
66+ 'infinity' : lib .date_infinity ,
67+ '-infinity' : lib .date_negative_infinity ,
68+ }
69+ time64_unpack_constants = {
70+ lib .time64_infinity : infinity_datetime ,
71+ lib .time64_negative_infinity : negative_infinity_datetime ,
72+ }
73+ time_unpack_constants = {
74+ lib .time_infinity : infinity_datetime ,
75+ lib .time_negative_infinity : negative_infinity_datetime ,
76+ }
77+ date_unpack_constants = {
78+ lib .date_infinity : infinity_date ,
79+ lib .date_negative_infinity : negative_infinity_date ,
80+ }
81+
6082date_pack = compose ((
61- toordinal , partial (add , - pg_date_offset ), lib .date_pack ,
83+ methodcaller ( " toordinal" ) , partial (add , pg_minus_date_offset ), lib .date_pack ,
6284))
6385date_unpack = compose ((
6486 lib .date_unpack , partial (add , pg_date_offset ), datetime .date .fromordinal
@@ -91,7 +113,7 @@ def time_pack(x):
91113 x .microsecond
92114 )
93115
94- def time_unpack (seconds_ms , time = datetime .time ):
116+ def time_unpack (seconds_ms , time = datetime .time , divmod = divmod ):
95117 """
96118 Create a `datetime.time` instance from a (seconds, microseconds) pair.
97119 Seconds being offset from epoch.
@@ -106,9 +128,7 @@ def interval_pack(x):
106128 Create a (months, days, (seconds, microseconds)) tuple from a
107129 `datetime.timedelta` instance.
108130 """
109- return (
110- 0 , x .days , (x .seconds , x .microseconds )
111- )
131+ return (0 , x .days , (x .seconds , x .microseconds ))
112132
113133def interval_unpack (mds , timedelta = datetime .timedelta ):
114134 """
@@ -117,6 +137,7 @@ def interval_unpack(mds, timedelta = datetime.timedelta):
117137 """
118138 months , days , seconds_ms = mds
119139 if months != 0 :
140+ # XXX: Should this raise an exception?
120141 w = pg_exc .TypeConversionWarning (
121142 "datetime.timedelta cannot represent relative intervals" ,
122143 details = {
@@ -153,6 +174,13 @@ def timetz_unpack(tstz):
153174NoDay = True
154175WithDay = False
155176
177+ # Used to handle the special cases: infinity and -infinity.
178+ def proc_when_not_in (proc , dict ):
179+ def _proc (x ):
180+ r = dict .get (x )
181+ return r or proc (x )
182+ return _proc
183+
156184id_to_io = {
157185 (FloatTimes , TIMEOID ) : (
158186 compose ((time_pack , lib .time_pack )),
@@ -165,13 +193,13 @@ def timetz_unpack(tstz):
165193 datetime .time
166194 ),
167195 (FloatTimes , TIMESTAMPOID ) : (
168- compose ((timestamp_pack , lib .time_pack )),
169- compose ((lib .time_unpack , timestamp_unpack )),
196+ proc_when_not_in ( compose ((timestamp_pack , lib .time_pack )), time_pack_constants ),
197+ proc_when_not_in ( compose ((lib .time_unpack , timestamp_unpack )), time_unpack_constants ),
170198 datetime .datetime
171199 ),
172200 (FloatTimes , TIMESTAMPTZOID ) : (
173- compose ((convert_to_utc , remove_tzinfo , timestamp_pack , lib .time_pack )),
174- compose ((lib .time_unpack , timestamp_unpack , set_as_utc )),
201+ proc_when_not_in ( compose ((convert_to_utc , remove_tzinfo , timestamp_pack , lib .time_pack )), time_pack_constants ),
202+ proc_when_not_in ( compose ((lib .time_unpack , timestamp_unpack , set_as_utc )), time_unpack_constants ),
175203 datetime .datetime
176204 ),
177205 (FloatTimes , WithDay , INTERVALOID ): (
@@ -196,13 +224,13 @@ def timetz_unpack(tstz):
196224 datetime .time
197225 ),
198226 (IntTimes , TIMESTAMPOID ) : (
199- compose ((timestamp_pack , lib .time64_pack )),
200- compose ((lib .time64_unpack , timestamp_unpack )),
227+ proc_when_not_in ( compose ((timestamp_pack , lib .time64_pack )), time64_pack_constants ),
228+ proc_when_not_in ( compose ((lib .time64_unpack , timestamp_unpack )), time64_unpack_constants ),
201229 datetime .datetime
202230 ),
203231 (IntTimes , TIMESTAMPTZOID ) : (
204- compose ((convert_to_utc , remove_tzinfo , timestamp_pack , lib .time64_pack )),
205- compose ((lib .time64_unpack , timestamp_unpack , set_as_utc )),
232+ proc_when_not_in ( compose ((convert_to_utc , remove_tzinfo , timestamp_pack , lib .time64_pack )), time64_pack_constants ),
233+ proc_when_not_in ( compose ((lib .time64_unpack , timestamp_unpack , set_as_utc )), time64_unpack_constants ),
206234 datetime .datetime
207235 ),
208236 (IntTimes , WithDay , INTERVALOID ) : (
@@ -233,10 +261,14 @@ def select_format(oid, typio, get = id_to_io.__getitem__):
233261 return get ((time_type (typio ), oid ))
234262
235263def select_day_format (oid , typio , get = id_to_io .__getitem__ ):
236- return get ((time_type (typio ), typio .database .version_info <= (8 ,0 ), oid ))
264+ return get ((time_type (typio ), typio .database .version_info [: 2 ] <= (8 ,0 ), oid ))
237265
238266oid_to_io = {
239- DATEOID : (date_pack , date_unpack , datetime .date ),
267+ DATEOID : (
268+ proc_when_not_in (date_pack , date_pack_constants ),
269+ proc_when_not_in (date_unpack , date_unpack_constants ),
270+ datetime .date ,
271+ ),
240272 TIMEOID : select_format ,
241273 TIMETZOID : select_format ,
242274 TIMESTAMPOID : select_format ,
0 commit comments