@@ -178,76 +178,77 @@ impl<'p> Serialize for SerializePyObject {
178178 serializer. serialize_seq ( None ) . unwrap ( ) . end ( )
179179 }
180180 } else if unsafe { obj_ptr == DATETIME_PTR } {
181- if unsafe {
182- ( * ( self . ptr as * mut pyo3:: ffi:: PyDateTime_DateTime ) ) . hastzinfo == 0
183- && !self . opts & NAIVE_UTC == NAIVE_UTC
184- } {
185- return Err ( ser:: Error :: custom (
186- "datetime.datetime must have tzinfo set; use datetime.timezone.utc if UTC" ,
187- ) ) ;
188- }
189- let tzinfo = unsafe { pyo3:: ffi:: PyDateTime_DATE_GET_TZINFO ( self . ptr ) } ;
181+ let has_tz =
182+ unsafe { ( * ( self . ptr as * mut pyo3:: ffi:: PyDateTime_DateTime ) ) . hastzinfo == 1 } ;
190183 let offset_day: i32 ;
191184 let mut offset_second: i32 ;
192- if unsafe { ( * ( self . ptr as * mut pyo3:: ffi:: PyDateTime_DateTime ) ) . hastzinfo == 1 } {
193- if unsafe { pyo3:: ffi:: PyObject_HasAttr ( tzinfo, CONVERT_METHOD_STR ) == 1 } {
194- // pendulum
195- let offset = unsafe {
196- pyo3:: ffi:: PyObject_CallMethodObjArgs (
197- self . ptr ,
198- UTCOFFSET_METHOD_STR ,
199- std:: ptr:: null_mut ( ) as * mut pyo3:: ffi:: PyObject ,
200- )
201- } ;
202- // test_datetime_partial_second_pendulum_not_supported
203- if offset. is_null ( ) {
204- return Err ( ser:: Error :: custom (
205- "datetime does not support timezones with offsets that are not even minutes" ,
206- ) ) ;
207- }
208- offset_second =
209- unsafe { pyo3:: ffi:: PyDateTime_DELTA_GET_SECONDS ( offset) as i32 } ;
210- offset_day = unsafe { pyo3:: ffi:: PyDateTime_DELTA_GET_DAYS ( offset) } ;
211- } else if unsafe {
212- pyo3:: ffi:: PyObject_HasAttr ( tzinfo, NORMALIZE_METHOD_STR ) == 1
185+ if !has_tz {
186+ offset_second = 0 ;
187+ offset_day = 0 ;
188+ } else {
189+ let tzinfo = unsafe { pyo3:: ffi:: PyDateTime_DATE_GET_TZINFO ( self . ptr ) } ;
190+ if unsafe {
191+ ( * ( self . ptr as * mut pyo3:: ffi:: PyDateTime_DateTime ) ) . hastzinfo == 1
213192 } {
214- // pytz
215- let offset = unsafe {
216- pyo3:: ffi:: PyObject_CallMethodObjArgs (
193+ if unsafe { pyo3:: ffi:: PyObject_HasAttr ( tzinfo, CONVERT_METHOD_STR ) == 1 } {
194+ // pendulum
195+ let offset = unsafe {
196+ pyo3:: ffi:: PyObject_CallMethodObjArgs (
197+ self . ptr ,
198+ UTCOFFSET_METHOD_STR ,
199+ std:: ptr:: null_mut ( ) as * mut pyo3:: ffi:: PyObject ,
200+ )
201+ } ;
202+ // test_datetime_partial_second_pendulum_not_supported
203+ if offset. is_null ( ) {
204+ return Err ( ser:: Error :: custom (
205+ "datetime does not support timezones with offsets that are not even minutes" ,
206+ ) ) ;
207+ }
208+ offset_second =
209+ unsafe { pyo3:: ffi:: PyDateTime_DELTA_GET_SECONDS ( offset) as i32 } ;
210+ offset_day = unsafe { pyo3:: ffi:: PyDateTime_DELTA_GET_DAYS ( offset) } ;
211+ } else if unsafe {
212+ pyo3:: ffi:: PyObject_HasAttr ( tzinfo, NORMALIZE_METHOD_STR ) == 1
213+ } {
214+ // pytz
215+ let offset = unsafe {
216+ pyo3:: ffi:: PyObject_CallMethodObjArgs (
217+ pyo3:: ffi:: PyObject_CallMethodObjArgs (
218+ tzinfo,
219+ NORMALIZE_METHOD_STR ,
220+ self . ptr ,
221+ std:: ptr:: null_mut ( ) as * mut pyo3:: ffi:: PyObject ,
222+ ) ,
223+ UTCOFFSET_METHOD_STR ,
224+ std:: ptr:: null_mut ( ) as * mut pyo3:: ffi:: PyObject ,
225+ )
226+ } ;
227+ offset_second =
228+ unsafe { pyo3:: ffi:: PyDateTime_DELTA_GET_SECONDS ( offset) as i32 } ;
229+ offset_day = unsafe { pyo3:: ffi:: PyDateTime_DELTA_GET_DAYS ( offset) } ;
230+ } else if unsafe { pyo3:: ffi:: PyObject_HasAttr ( tzinfo, DST_STR ) == 1 } {
231+ // dateutil/arrow, datetime.timezone.utc
232+ let offset = unsafe {
217233 pyo3:: ffi:: PyObject_CallMethodObjArgs (
218234 tzinfo,
219- NORMALIZE_METHOD_STR ,
235+ UTCOFFSET_METHOD_STR ,
220236 self . ptr ,
221237 std:: ptr:: null_mut ( ) as * mut pyo3:: ffi:: PyObject ,
222- ) ,
223- UTCOFFSET_METHOD_STR ,
224- std:: ptr:: null_mut ( ) as * mut pyo3:: ffi:: PyObject ,
225- )
226- } ;
227- offset_second =
228- unsafe { pyo3:: ffi:: PyDateTime_DELTA_GET_SECONDS ( offset) as i32 } ;
229- offset_day = unsafe { pyo3:: ffi:: PyDateTime_DELTA_GET_DAYS ( offset) } ;
230- } else if unsafe { pyo3:: ffi:: PyObject_HasAttr ( tzinfo, DST_STR ) == 1 } {
231- // dateutil/arrow, datetime.timezone.utc
232- let offset = unsafe {
233- pyo3:: ffi:: PyObject_CallMethodObjArgs (
234- tzinfo,
235- UTCOFFSET_METHOD_STR ,
236- self . ptr ,
237- std:: ptr:: null_mut ( ) as * mut pyo3:: ffi:: PyObject ,
238- )
239- } ;
240- offset_second =
241- unsafe { pyo3:: ffi:: PyDateTime_DELTA_GET_SECONDS ( offset) as i32 } ;
242- offset_day = unsafe { pyo3:: ffi:: PyDateTime_DELTA_GET_DAYS ( offset) } ;
238+ )
239+ } ;
240+ offset_second =
241+ unsafe { pyo3:: ffi:: PyDateTime_DELTA_GET_SECONDS ( offset) as i32 } ;
242+ offset_day = unsafe { pyo3:: ffi:: PyDateTime_DELTA_GET_DAYS ( offset) } ;
243+ } else {
244+ return Err ( ser:: Error :: custom (
245+ "datetime's timezone library is not supported: use datetime.timezone.utc, pendulum, pytz, or dateutil" ,
246+ ) ) ;
247+ }
243248 } else {
244- return Err ( ser:: Error :: custom (
245- "datetime's timezone library is not supported: use datetime.timezone.utc, pendulum, pytz, or dateutil" ,
246- ) ) ;
249+ offset_second = 0 ;
250+ offset_day = 0 ;
247251 }
248- } else {
249- offset_second = 0 ;
250- offset_day = 0 ;
251252 } ;
252253
253254 let mut dt: SmallVec < [ u8 ; 32 ] > = SmallVec :: with_capacity ( 32 ) ;
@@ -316,49 +317,51 @@ impl<'p> Serialize for SerializePyObject {
316317 dt. extend ( itoa:: Buffer :: new ( ) . format ( microsecond) . bytes ( ) ) ;
317318 }
318319 }
319- if offset_second == 0 {
320- dt. push ( PLUS ) ;
321- dt. push ( ZERO ) ;
322- dt. push ( ZERO ) ;
323- dt. push ( COLON ) ;
324- dt. push ( ZERO ) ;
325- dt. push ( ZERO ) ;
326- } else {
327- if offset_day == -1 {
328- // datetime.timedelta(days=-1, seconds=68400) -> -05:00
329- dt. push ( HYPHEN ) ;
330- offset_second = 86400 - offset_second
331- } else {
332- // datetime.timedelta(seconds=37800) -> +10:30
320+ if has_tz || self . opts & NAIVE_UTC == NAIVE_UTC {
321+ if offset_second == 0 {
333322 dt. push ( PLUS ) ;
334- }
335- {
336- let offset_minute = offset_second / 60 ;
337- let offset_hour = offset_minute / 60 ;
338- if offset_hour < 10 {
339- dt. push ( ZERO ) ;
340- }
341- dt. extend ( itoa:: Buffer :: new ( ) . format ( offset_hour) . bytes ( ) ) ;
323+ dt. push ( ZERO ) ;
324+ dt. push ( ZERO ) ;
342325 dt. push ( COLON ) ;
326+ dt. push ( ZERO ) ;
327+ dt. push ( ZERO ) ;
328+ } else {
329+ if offset_day == -1 {
330+ // datetime.timedelta(days=-1, seconds=68400) -> -05:00
331+ dt. push ( HYPHEN ) ;
332+ offset_second = 86400 - offset_second
333+ } else {
334+ // datetime.timedelta(seconds=37800) -> +10:30
335+ dt. push ( PLUS ) ;
336+ }
337+ {
338+ let offset_minute = offset_second / 60 ;
339+ let offset_hour = offset_minute / 60 ;
340+ if offset_hour < 10 {
341+ dt. push ( ZERO ) ;
342+ }
343+ dt. extend ( itoa:: Buffer :: new ( ) . format ( offset_hour) . bytes ( ) ) ;
344+ dt. push ( COLON ) ;
343345
344- let mut offset_minute_print = offset_minute % 60 ;
346+ let mut offset_minute_print = offset_minute % 60 ;
345347
346- {
347- // https://tools.ietf.org/html/rfc3339#section-5.8
348- // "exactly 19 minutes and 32.13 seconds ahead of UTC"
349- // "closest representable UTC offset"
350- // "+20:00"
351- let offset_excess_second =
352- offset_second - ( offset_minute_print * 60 + offset_hour * 3600 ) ;
353- if offset_excess_second >= 30 {
354- offset_minute_print += 1 ;
348+ {
349+ // https://tools.ietf.org/html/rfc3339#section-5.8
350+ // "exactly 19 minutes and 32.13 seconds ahead of UTC"
351+ // "closest representable UTC offset"
352+ // "+20:00"
353+ let offset_excess_second =
354+ offset_second - ( offset_minute_print * 60 + offset_hour * 3600 ) ;
355+ if offset_excess_second >= 30 {
356+ offset_minute_print += 1 ;
357+ }
355358 }
356- }
357359
358- if offset_minute_print < 10 {
359- dt. push ( ZERO ) ;
360+ if offset_minute_print < 10 {
361+ dt. push ( ZERO ) ;
362+ }
363+ dt. extend ( itoa:: Buffer :: new ( ) . format ( offset_minute_print) . bytes ( ) ) ;
360364 }
361- dt. extend ( itoa:: Buffer :: new ( ) . format ( offset_minute_print) . bytes ( ) ) ;
362365 }
363366 }
364367 serializer. serialize_str ( unsafe {
0 commit comments