1 | //! Safe Rust wrappers for types defined in the Python `datetime` library |
2 | //! |
3 | //! For more details about these types, see the [Python |
4 | //! documentation](https://docs.python.org/3/library/datetime.html) |
5 | |
6 | use crate::err::PyResult; |
7 | use crate::ffi::{ |
8 | self, PyDateTime_CAPI, PyDateTime_FromTimestamp, PyDateTime_IMPORT, PyDate_FromTimestamp, |
9 | }; |
10 | use crate::ffi::{ |
11 | PyDateTime_DATE_GET_FOLD, PyDateTime_DATE_GET_HOUR, PyDateTime_DATE_GET_MICROSECOND, |
12 | PyDateTime_DATE_GET_MINUTE, PyDateTime_DATE_GET_SECOND, |
13 | }; |
14 | use crate::ffi::{ |
15 | PyDateTime_DELTA_GET_DAYS, PyDateTime_DELTA_GET_MICROSECONDS, PyDateTime_DELTA_GET_SECONDS, |
16 | }; |
17 | use crate::ffi::{PyDateTime_GET_DAY, PyDateTime_GET_MONTH, PyDateTime_GET_YEAR}; |
18 | use crate::ffi::{ |
19 | PyDateTime_TIME_GET_FOLD, PyDateTime_TIME_GET_HOUR, PyDateTime_TIME_GET_MICROSECOND, |
20 | PyDateTime_TIME_GET_MINUTE, PyDateTime_TIME_GET_SECOND, |
21 | }; |
22 | use crate::instance::PyNativeType; |
23 | use crate::types::PyTuple; |
24 | use crate::{IntoPy, Py, PyAny, Python}; |
25 | use std::os::raw::c_int; |
26 | |
27 | fn ensure_datetime_api(_py: Python<'_>) -> &'static PyDateTime_CAPI { |
28 | unsafe { |
29 | if pyo3_ffi::PyDateTimeAPI().is_null() { |
30 | PyDateTime_IMPORT() |
31 | } |
32 | |
33 | &*pyo3_ffi::PyDateTimeAPI() |
34 | } |
35 | } |
36 | |
37 | // Type Check macros |
38 | // |
39 | // These are bindings around the C API typecheck macros, all of them return |
40 | // `1` if True and `0` if False. In all type check macros, the argument (`op`) |
41 | // must not be `NULL`. The implementations here all call ensure_datetime_api |
42 | // to ensure that the PyDateTimeAPI is initialized before use |
43 | // |
44 | // |
45 | // # Safety |
46 | // |
47 | // These functions must only be called when the GIL is held! |
48 | |
49 | macro_rules! ffi_fun_with_autoinit { |
50 | ($(#[$outer:meta] unsafe fn $name: ident($arg: ident: *mut PyObject) -> $ret: ty;)*) => { |
51 | $( |
52 | #[$outer] |
53 | #[allow(non_snake_case)] |
54 | /// # Safety |
55 | /// |
56 | /// Must only be called while the GIL is held |
57 | unsafe fn $name($arg: *mut crate::ffi::PyObject) -> $ret { |
58 | |
59 | let _ = ensure_datetime_api(Python::assume_gil_acquired()); |
60 | crate::ffi::$name($arg) |
61 | } |
62 | )* |
63 | |
64 | |
65 | }; |
66 | } |
67 | |
68 | ffi_fun_with_autoinit! { |
69 | /// Check if `op` is a `PyDateTimeAPI.DateType` or subtype. |
70 | unsafe fn PyDate_Check(op: *mut PyObject) -> c_int; |
71 | |
72 | /// Check if `op` is a `PyDateTimeAPI.DateTimeType` or subtype. |
73 | unsafe fn PyDateTime_Check(op: *mut PyObject) -> c_int; |
74 | |
75 | /// Check if `op` is a `PyDateTimeAPI.TimeType` or subtype. |
76 | unsafe fn PyTime_Check(op: *mut PyObject) -> c_int; |
77 | |
78 | /// Check if `op` is a `PyDateTimeAPI.DetaType` or subtype. |
79 | unsafe fn PyDelta_Check(op: *mut PyObject) -> c_int; |
80 | |
81 | /// Check if `op` is a `PyDateTimeAPI.TZInfoType` or subtype. |
82 | unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int; |
83 | } |
84 | |
85 | // Access traits |
86 | |
87 | /// Trait for accessing the date components of a struct containing a date. |
88 | pub trait PyDateAccess { |
89 | /// Returns the year, as a positive int. |
90 | /// |
91 | /// Implementations should conform to the upstream documentation: |
92 | /// <https://docs.python.org/3/c-api/datetime.html#c.PyDateTime_GET_YEAR> |
93 | fn get_year(&self) -> i32; |
94 | /// Returns the month, as an int from 1 through 12. |
95 | /// |
96 | /// Implementations should conform to the upstream documentation: |
97 | /// <https://docs.python.org/3/c-api/datetime.html#c.PyDateTime_GET_MONTH> |
98 | fn get_month(&self) -> u8; |
99 | /// Returns the day, as an int from 1 through 31. |
100 | /// |
101 | /// Implementations should conform to the upstream documentation: |
102 | /// <https://docs.python.org/3/c-api/datetime.html#c.PyDateTime_GET_DAY> |
103 | fn get_day(&self) -> u8; |
104 | } |
105 | |
106 | /// Trait for accessing the components of a struct containing a timedelta. |
107 | /// |
108 | /// Note: These access the individual components of a (day, second, |
109 | /// microsecond) representation of the delta, they are *not* intended as |
110 | /// aliases for calculating the total duration in each of these units. |
111 | pub trait PyDeltaAccess { |
112 | /// Returns the number of days, as an int from -999999999 to 999999999. |
113 | /// |
114 | /// Implementations should conform to the upstream documentation: |
115 | /// <https://docs.python.org/3/c-api/datetime.html#c.PyDateTime_DELTA_GET_DAYS> |
116 | fn get_days(&self) -> i32; |
117 | /// Returns the number of seconds, as an int from 0 through 86399. |
118 | /// |
119 | /// Implementations should conform to the upstream documentation: |
120 | /// <https://docs.python.org/3/c-api/datetime.html#c.PyDateTime_DELTA_GET_DAYS> |
121 | fn get_seconds(&self) -> i32; |
122 | /// Returns the number of microseconds, as an int from 0 through 999999. |
123 | /// |
124 | /// Implementations should conform to the upstream documentation: |
125 | /// <https://docs.python.org/3/c-api/datetime.html#c.PyDateTime_DELTA_GET_DAYS> |
126 | fn get_microseconds(&self) -> i32; |
127 | } |
128 | |
129 | /// Trait for accessing the time components of a struct containing a time. |
130 | pub trait PyTimeAccess { |
131 | /// Returns the hour, as an int from 0 through 23. |
132 | /// |
133 | /// Implementations should conform to the upstream documentation: |
134 | /// <https://docs.python.org/3/c-api/datetime.html#c.PyDateTime_DATE_GET_HOUR> |
135 | fn get_hour(&self) -> u8; |
136 | /// Returns the minute, as an int from 0 through 59. |
137 | /// |
138 | /// Implementations should conform to the upstream documentation: |
139 | /// <https://docs.python.org/3/c-api/datetime.html#c.PyDateTime_DATE_GET_MINUTE> |
140 | fn get_minute(&self) -> u8; |
141 | /// Returns the second, as an int from 0 through 59. |
142 | /// |
143 | /// Implementations should conform to the upstream documentation: |
144 | /// <https://docs.python.org/3/c-api/datetime.html#c.PyDateTime_DATE_GET_SECOND> |
145 | fn get_second(&self) -> u8; |
146 | /// Returns the microsecond, as an int from 0 through 999999. |
147 | /// |
148 | /// Implementations should conform to the upstream documentation: |
149 | /// <https://docs.python.org/3/c-api/datetime.html#c.PyDateTime_DATE_GET_MICROSECOND> |
150 | fn get_microsecond(&self) -> u32; |
151 | /// Returns whether this date is the later of two moments with the |
152 | /// same representation, during a repeated interval. |
153 | /// |
154 | /// This typically occurs at the end of daylight savings time. Only valid if the |
155 | /// represented time is ambiguous. |
156 | /// See [PEP 495](https://www.python.org/dev/peps/pep-0495/) for more detail. |
157 | fn get_fold(&self) -> bool; |
158 | } |
159 | |
160 | /// Trait for accessing the components of a struct containing a tzinfo. |
161 | pub trait PyTzInfoAccess { |
162 | /// Returns the tzinfo (which may be None). |
163 | /// |
164 | /// Implementations should conform to the upstream documentation: |
165 | /// <https://docs.python.org/3/c-api/datetime.html#c.PyDateTime_DATE_GET_TZINFO> |
166 | /// <https://docs.python.org/3/c-api/datetime.html#c.PyDateTime_TIME_GET_TZINFO> |
167 | fn get_tzinfo(&self) -> Option<&PyTzInfo>; |
168 | } |
169 | |
170 | /// Bindings around `datetime.date` |
171 | #[repr (transparent)] |
172 | pub struct PyDate(PyAny); |
173 | pyobject_native_type!( |
174 | PyDate, |
175 | crate::ffi::PyDateTime_Date, |
176 | |py| ensure_datetime_api(py).DateType, |
177 | #module=Some("datetime" ), |
178 | #checkfunction=PyDate_Check |
179 | ); |
180 | |
181 | impl PyDate { |
182 | /// Creates a new `datetime.date`. |
183 | pub fn new(py: Python<'_>, year: i32, month: u8, day: u8) -> PyResult<&PyDate> { |
184 | unsafe { |
185 | let ptr = (ensure_datetime_api(py).Date_FromDate)( |
186 | year, |
187 | c_int::from(month), |
188 | c_int::from(day), |
189 | ensure_datetime_api(py).DateType, |
190 | ); |
191 | py.from_owned_ptr_or_err(ptr) |
192 | } |
193 | } |
194 | |
195 | /// Construct a `datetime.date` from a POSIX timestamp |
196 | /// |
197 | /// This is equivalent to `datetime.date.fromtimestamp` |
198 | pub fn from_timestamp(py: Python<'_>, timestamp: i64) -> PyResult<&PyDate> { |
199 | let time_tuple = PyTuple::new(py, [timestamp]); |
200 | |
201 | // safety ensure that the API is loaded |
202 | let _api = ensure_datetime_api(py); |
203 | |
204 | unsafe { |
205 | let ptr = PyDate_FromTimestamp(time_tuple.as_ptr()); |
206 | py.from_owned_ptr_or_err(ptr) |
207 | } |
208 | } |
209 | } |
210 | |
211 | impl PyDateAccess for PyDate { |
212 | fn get_year(&self) -> i32 { |
213 | unsafe { PyDateTime_GET_YEAR(self.as_ptr()) } |
214 | } |
215 | |
216 | fn get_month(&self) -> u8 { |
217 | unsafe { PyDateTime_GET_MONTH(self.as_ptr()) as u8 } |
218 | } |
219 | |
220 | fn get_day(&self) -> u8 { |
221 | unsafe { PyDateTime_GET_DAY(self.as_ptr()) as u8 } |
222 | } |
223 | } |
224 | |
225 | /// Bindings for `datetime.datetime` |
226 | #[repr (transparent)] |
227 | pub struct PyDateTime(PyAny); |
228 | pyobject_native_type!( |
229 | PyDateTime, |
230 | crate::ffi::PyDateTime_DateTime, |
231 | |py| ensure_datetime_api(py).DateTimeType, |
232 | #module=Some("datetime" ), |
233 | #checkfunction=PyDateTime_Check |
234 | ); |
235 | |
236 | impl PyDateTime { |
237 | /// Creates a new `datetime.datetime` object. |
238 | #[allow (clippy::too_many_arguments)] |
239 | pub fn new<'p>( |
240 | py: Python<'p>, |
241 | year: i32, |
242 | month: u8, |
243 | day: u8, |
244 | hour: u8, |
245 | minute: u8, |
246 | second: u8, |
247 | microsecond: u32, |
248 | tzinfo: Option<&PyTzInfo>, |
249 | ) -> PyResult<&'p PyDateTime> { |
250 | let api = ensure_datetime_api(py); |
251 | unsafe { |
252 | let ptr = (api.DateTime_FromDateAndTime)( |
253 | year, |
254 | c_int::from(month), |
255 | c_int::from(day), |
256 | c_int::from(hour), |
257 | c_int::from(minute), |
258 | c_int::from(second), |
259 | microsecond as c_int, |
260 | opt_to_pyobj(tzinfo), |
261 | api.DateTimeType, |
262 | ); |
263 | py.from_owned_ptr_or_err(ptr) |
264 | } |
265 | } |
266 | |
267 | /// Alternate constructor that takes a `fold` parameter. A `true` value for this parameter |
268 | /// signifies this this datetime is the later of two moments with the same representation, |
269 | /// during a repeated interval. |
270 | /// |
271 | /// This typically occurs at the end of daylight savings time. Only valid if the |
272 | /// represented time is ambiguous. |
273 | /// See [PEP 495](https://www.python.org/dev/peps/pep-0495/) for more detail. |
274 | #[allow (clippy::too_many_arguments)] |
275 | pub fn new_with_fold<'p>( |
276 | py: Python<'p>, |
277 | year: i32, |
278 | month: u8, |
279 | day: u8, |
280 | hour: u8, |
281 | minute: u8, |
282 | second: u8, |
283 | microsecond: u32, |
284 | tzinfo: Option<&PyTzInfo>, |
285 | fold: bool, |
286 | ) -> PyResult<&'p PyDateTime> { |
287 | let api = ensure_datetime_api(py); |
288 | unsafe { |
289 | let ptr = (api.DateTime_FromDateAndTimeAndFold)( |
290 | year, |
291 | c_int::from(month), |
292 | c_int::from(day), |
293 | c_int::from(hour), |
294 | c_int::from(minute), |
295 | c_int::from(second), |
296 | microsecond as c_int, |
297 | opt_to_pyobj(tzinfo), |
298 | c_int::from(fold), |
299 | api.DateTimeType, |
300 | ); |
301 | py.from_owned_ptr_or_err(ptr) |
302 | } |
303 | } |
304 | |
305 | /// Construct a `datetime` object from a POSIX timestamp |
306 | /// |
307 | /// This is equivalent to `datetime.datetime.fromtimestamp` |
308 | pub fn from_timestamp<'p>( |
309 | py: Python<'p>, |
310 | timestamp: f64, |
311 | tzinfo: Option<&PyTzInfo>, |
312 | ) -> PyResult<&'p PyDateTime> { |
313 | let args: Py<PyTuple> = (timestamp, tzinfo).into_py(py); |
314 | |
315 | // safety ensure API is loaded |
316 | let _api = ensure_datetime_api(py); |
317 | |
318 | unsafe { |
319 | let ptr = PyDateTime_FromTimestamp(args.as_ptr()); |
320 | py.from_owned_ptr_or_err(ptr) |
321 | } |
322 | } |
323 | } |
324 | |
325 | impl PyDateAccess for PyDateTime { |
326 | fn get_year(&self) -> i32 { |
327 | unsafe { PyDateTime_GET_YEAR(self.as_ptr()) } |
328 | } |
329 | |
330 | fn get_month(&self) -> u8 { |
331 | unsafe { PyDateTime_GET_MONTH(self.as_ptr()) as u8 } |
332 | } |
333 | |
334 | fn get_day(&self) -> u8 { |
335 | unsafe { PyDateTime_GET_DAY(self.as_ptr()) as u8 } |
336 | } |
337 | } |
338 | |
339 | impl PyTimeAccess for PyDateTime { |
340 | fn get_hour(&self) -> u8 { |
341 | unsafe { PyDateTime_DATE_GET_HOUR(self.as_ptr()) as u8 } |
342 | } |
343 | |
344 | fn get_minute(&self) -> u8 { |
345 | unsafe { PyDateTime_DATE_GET_MINUTE(self.as_ptr()) as u8 } |
346 | } |
347 | |
348 | fn get_second(&self) -> u8 { |
349 | unsafe { PyDateTime_DATE_GET_SECOND(self.as_ptr()) as u8 } |
350 | } |
351 | |
352 | fn get_microsecond(&self) -> u32 { |
353 | unsafe { PyDateTime_DATE_GET_MICROSECOND(self.as_ptr()) as u32 } |
354 | } |
355 | |
356 | fn get_fold(&self) -> bool { |
357 | unsafe { PyDateTime_DATE_GET_FOLD(self.as_ptr()) > 0 } |
358 | } |
359 | } |
360 | |
361 | impl PyTzInfoAccess for PyDateTime { |
362 | fn get_tzinfo(&self) -> Option<&PyTzInfo> { |
363 | let ptr: *mut PyDateTime_DateTime = self.as_ptr() as *mut ffi::PyDateTime_DateTime; |
364 | unsafe { |
365 | if (*ptr).hastzinfo != 0 { |
366 | Some(self.py().from_borrowed_ptr((*ptr).tzinfo)) |
367 | } else { |
368 | None |
369 | } |
370 | } |
371 | } |
372 | } |
373 | |
374 | /// Bindings for `datetime.time` |
375 | #[repr (transparent)] |
376 | pub struct PyTime(PyAny); |
377 | pyobject_native_type!( |
378 | PyTime, |
379 | crate::ffi::PyDateTime_Time, |
380 | |py| ensure_datetime_api(py).TimeType, |
381 | #module=Some("datetime" ), |
382 | #checkfunction=PyTime_Check |
383 | ); |
384 | |
385 | impl PyTime { |
386 | /// Creates a new `datetime.time` object. |
387 | pub fn new<'p>( |
388 | py: Python<'p>, |
389 | hour: u8, |
390 | minute: u8, |
391 | second: u8, |
392 | microsecond: u32, |
393 | tzinfo: Option<&PyTzInfo>, |
394 | ) -> PyResult<&'p PyTime> { |
395 | let api = ensure_datetime_api(py); |
396 | unsafe { |
397 | let ptr = (api.Time_FromTime)( |
398 | c_int::from(hour), |
399 | c_int::from(minute), |
400 | c_int::from(second), |
401 | microsecond as c_int, |
402 | opt_to_pyobj(tzinfo), |
403 | api.TimeType, |
404 | ); |
405 | py.from_owned_ptr_or_err(ptr) |
406 | } |
407 | } |
408 | |
409 | /// Alternate constructor that takes a `fold` argument. See [`PyDateTime::new_with_fold`]. |
410 | pub fn new_with_fold<'p>( |
411 | py: Python<'p>, |
412 | hour: u8, |
413 | minute: u8, |
414 | second: u8, |
415 | microsecond: u32, |
416 | tzinfo: Option<&PyTzInfo>, |
417 | fold: bool, |
418 | ) -> PyResult<&'p PyTime> { |
419 | let api = ensure_datetime_api(py); |
420 | unsafe { |
421 | let ptr = (api.Time_FromTimeAndFold)( |
422 | c_int::from(hour), |
423 | c_int::from(minute), |
424 | c_int::from(second), |
425 | microsecond as c_int, |
426 | opt_to_pyobj(tzinfo), |
427 | fold as c_int, |
428 | api.TimeType, |
429 | ); |
430 | py.from_owned_ptr_or_err(ptr) |
431 | } |
432 | } |
433 | } |
434 | |
435 | impl PyTimeAccess for PyTime { |
436 | fn get_hour(&self) -> u8 { |
437 | unsafe { PyDateTime_TIME_GET_HOUR(self.as_ptr()) as u8 } |
438 | } |
439 | |
440 | fn get_minute(&self) -> u8 { |
441 | unsafe { PyDateTime_TIME_GET_MINUTE(self.as_ptr()) as u8 } |
442 | } |
443 | |
444 | fn get_second(&self) -> u8 { |
445 | unsafe { PyDateTime_TIME_GET_SECOND(self.as_ptr()) as u8 } |
446 | } |
447 | |
448 | fn get_microsecond(&self) -> u32 { |
449 | unsafe { PyDateTime_TIME_GET_MICROSECOND(self.as_ptr()) as u32 } |
450 | } |
451 | |
452 | fn get_fold(&self) -> bool { |
453 | unsafe { PyDateTime_TIME_GET_FOLD(self.as_ptr()) != 0 } |
454 | } |
455 | } |
456 | |
457 | impl PyTzInfoAccess for PyTime { |
458 | fn get_tzinfo(&self) -> Option<&PyTzInfo> { |
459 | let ptr: *mut PyDateTime_Time = self.as_ptr() as *mut ffi::PyDateTime_Time; |
460 | unsafe { |
461 | if (*ptr).hastzinfo != 0 { |
462 | Some(self.py().from_borrowed_ptr((*ptr).tzinfo)) |
463 | } else { |
464 | None |
465 | } |
466 | } |
467 | } |
468 | } |
469 | |
470 | /// Bindings for `datetime.tzinfo`. |
471 | /// |
472 | /// This is an abstract base class and cannot be constructed directly. |
473 | /// For concrete time zone implementations, see [`timezone_utc`] and |
474 | /// the [`zoneinfo` module](https://docs.python.org/3/library/zoneinfo.html). |
475 | #[repr (transparent)] |
476 | pub struct PyTzInfo(PyAny); |
477 | pyobject_native_type!( |
478 | PyTzInfo, |
479 | crate::ffi::PyObject, |
480 | |py| ensure_datetime_api(py).TZInfoType, |
481 | #module=Some("datetime" ), |
482 | #checkfunction=PyTZInfo_Check |
483 | ); |
484 | |
485 | /// Equivalent to `datetime.timezone.utc` |
486 | pub fn timezone_utc(py: Python<'_>) -> &PyTzInfo { |
487 | unsafe { &*(ensure_datetime_api(py).TimeZone_UTC as *const PyTzInfo) } |
488 | } |
489 | |
490 | /// Bindings for `datetime.timedelta` |
491 | #[repr (transparent)] |
492 | pub struct PyDelta(PyAny); |
493 | pyobject_native_type!( |
494 | PyDelta, |
495 | crate::ffi::PyDateTime_Delta, |
496 | |py| ensure_datetime_api(py).DeltaType, |
497 | #module=Some("datetime" ), |
498 | #checkfunction=PyDelta_Check |
499 | ); |
500 | |
501 | impl PyDelta { |
502 | /// Creates a new `timedelta`. |
503 | pub fn new( |
504 | py: Python<'_>, |
505 | days: i32, |
506 | seconds: i32, |
507 | microseconds: i32, |
508 | normalize: bool, |
509 | ) -> PyResult<&PyDelta> { |
510 | let api: &PyDateTime_CAPI = ensure_datetime_api(py); |
511 | unsafe { |
512 | let ptr: *mut PyObject = (api.Delta_FromDelta)( |
513 | days as c_int, |
514 | seconds as c_int, |
515 | microseconds as c_int, |
516 | normalize as c_int, |
517 | api.DeltaType, |
518 | ); |
519 | py.from_owned_ptr_or_err(ptr) |
520 | } |
521 | } |
522 | } |
523 | |
524 | impl PyDeltaAccess for PyDelta { |
525 | fn get_days(&self) -> i32 { |
526 | unsafe { PyDateTime_DELTA_GET_DAYS(self.as_ptr()) } |
527 | } |
528 | |
529 | fn get_seconds(&self) -> i32 { |
530 | unsafe { PyDateTime_DELTA_GET_SECONDS(self.as_ptr()) } |
531 | } |
532 | |
533 | fn get_microseconds(&self) -> i32 { |
534 | unsafe { PyDateTime_DELTA_GET_MICROSECONDS(self.as_ptr()) } |
535 | } |
536 | } |
537 | |
538 | // Utility function which returns a borrowed reference to either |
539 | // the underlying tzinfo or None. |
540 | fn opt_to_pyobj(opt: Option<&PyTzInfo>) -> *mut ffi::PyObject { |
541 | match opt { |
542 | Some(tzi: &PyTzInfo) => tzi.as_ptr(), |
543 | None => unsafe { ffi::Py_None() }, |
544 | } |
545 | } |
546 | |
547 | #[cfg (test)] |
548 | mod tests { |
549 | use super::*; |
550 | #[cfg (feature = "macros" )] |
551 | use crate::py_run; |
552 | |
553 | #[test ] |
554 | #[cfg (feature = "macros" )] |
555 | #[cfg_attr (target_arch = "wasm32" , ignore)] // DateTime import fails on wasm for mysterious reasons |
556 | fn test_datetime_fromtimestamp() { |
557 | Python::with_gil(|py| { |
558 | let dt = PyDateTime::from_timestamp(py, 100.0, None).unwrap(); |
559 | py_run!( |
560 | py, |
561 | dt, |
562 | "import datetime; assert dt == datetime.datetime.fromtimestamp(100)" |
563 | ); |
564 | |
565 | let dt = PyDateTime::from_timestamp(py, 100.0, Some(timezone_utc(py))).unwrap(); |
566 | py_run!( |
567 | py, |
568 | dt, |
569 | "import datetime; assert dt == datetime.datetime.fromtimestamp(100, datetime.timezone.utc)" |
570 | ); |
571 | }) |
572 | } |
573 | |
574 | #[test ] |
575 | #[cfg (feature = "macros" )] |
576 | #[cfg_attr (target_arch = "wasm32" , ignore)] // DateTime import fails on wasm for mysterious reasons |
577 | fn test_date_fromtimestamp() { |
578 | Python::with_gil(|py| { |
579 | let dt = PyDate::from_timestamp(py, 100).unwrap(); |
580 | py_run!( |
581 | py, |
582 | dt, |
583 | "import datetime; assert dt == datetime.date.fromtimestamp(100)" |
584 | ); |
585 | }) |
586 | } |
587 | |
588 | #[test ] |
589 | #[cfg_attr (target_arch = "wasm32" , ignore)] // DateTime import fails on wasm for mysterious reasons |
590 | fn test_new_with_fold() { |
591 | Python::with_gil(|py| { |
592 | let a = PyDateTime::new_with_fold(py, 2021, 1, 23, 20, 32, 40, 341516, None, false); |
593 | let b = PyDateTime::new_with_fold(py, 2021, 1, 23, 20, 32, 40, 341516, None, true); |
594 | |
595 | assert!(!a.unwrap().get_fold()); |
596 | assert!(b.unwrap().get_fold()); |
597 | }); |
598 | } |
599 | |
600 | #[test ] |
601 | #[cfg_attr (target_arch = "wasm32" , ignore)] // DateTime import fails on wasm for mysterious reasons |
602 | fn test_get_tzinfo() { |
603 | crate::Python::with_gil(|py| { |
604 | let utc = timezone_utc(py); |
605 | |
606 | let dt = PyDateTime::new(py, 2018, 1, 1, 0, 0, 0, 0, Some(utc)).unwrap(); |
607 | |
608 | assert!(dt.get_tzinfo().unwrap().eq(utc).unwrap()); |
609 | |
610 | let dt = PyDateTime::new(py, 2018, 1, 1, 0, 0, 0, 0, None).unwrap(); |
611 | |
612 | assert!(dt.get_tzinfo().is_none()); |
613 | |
614 | let t = PyTime::new(py, 0, 0, 0, 0, Some(utc)).unwrap(); |
615 | |
616 | assert!(t.get_tzinfo().unwrap().eq(utc).unwrap()); |
617 | |
618 | let t = PyTime::new(py, 0, 0, 0, 0, None).unwrap(); |
619 | |
620 | assert!(t.get_tzinfo().is_none()); |
621 | }); |
622 | } |
623 | } |
624 | |