|
| 1 | +cimport cpython.datetime |
| 2 | +from cpython.datetime cimport PyDateTimeAPI, datetime, datetime_tzinfo, timedelta_new |
| 3 | +from libc.stdint cimport uint32_t |
| 4 | +from libc.string cimport memcpy |
| 5 | + |
| 6 | + |
| 7 | +cdef inline void datetime_zero(IProtoDateTime *dt): |
| 8 | + dt.seconds = 0 |
| 9 | + dt.nsec = 0 |
| 10 | + dt.tzoffset = 0 |
| 11 | + dt.tzindex = 0 |
| 12 | + |
| 13 | +cdef inline uint32_t datetime_len(IProtoDateTime *dt): |
| 14 | + cdef uint32_t sz |
| 15 | + sz = sizeof(int64_t) |
| 16 | + if dt.nsec != 0 or dt.tzoffset != 0 or dt.tzindex != 0: |
| 17 | + return sz + DATETIME_TAIL_SZ |
| 18 | + return sz |
| 19 | + |
| 20 | +cdef char *datetime_encode(char *p, IProtoDateTime *dt) except NULL: |
| 21 | + store_u64(p, dt.seconds) |
| 22 | + p += sizeof(dt.seconds) |
| 23 | + if dt.nsec != 0 or dt.tzoffset != 0 or dt.tzindex != 0: |
| 24 | + memcpy(p, &dt.nsec, DATETIME_TAIL_SZ) |
| 25 | + p += DATETIME_TAIL_SZ |
| 26 | + return p |
| 27 | + |
| 28 | +cdef int datetime_decode( |
| 29 | + const char ** p, |
| 30 | + uint32_t length, |
| 31 | + IProtoDateTime *dt |
| 32 | +) except -1: |
| 33 | + delta = None |
| 34 | + tz = None |
| 35 | + |
| 36 | + dt.seconds = load_u64(p[0]) |
| 37 | + p[0] += sizeof(dt.seconds) |
| 38 | + length -= sizeof(dt.seconds) |
| 39 | + |
| 40 | + if length == 0: |
| 41 | + return 0 |
| 42 | + |
| 43 | + if length != DATETIME_TAIL_SZ: |
| 44 | + raise ValueError("invalid datetime size. got {} extra bytes".format( |
| 45 | + length |
| 46 | + )) |
| 47 | + |
| 48 | + dt.nsec = load_u32(p[0]) |
| 49 | + p[0] += 4 |
| 50 | + dt.tzoffset = load_u16(p[0]) |
| 51 | + p[0] += 2 |
| 52 | + dt.tzindex = load_u16(p[0]) |
| 53 | + p[0] += 2 |
| 54 | + |
| 55 | +cdef void datetime_from_py(datetime ob, IProtoDateTime *dt): |
| 56 | + cdef: |
| 57 | + double ts |
| 58 | + int offset |
| 59 | + ts = <double> ob.timestamp() |
| 60 | + dt.seconds = <int64_t> ts |
| 61 | + dt.nsec = <int32_t> ((ts - <double> dt.seconds) * 1000000) * 1000 |
| 62 | + if dt.nsec < 0: |
| 63 | + # correction for negative dates |
| 64 | + dt.seconds -= 1 |
| 65 | + dt.nsec += 1000000000 |
| 66 | + |
| 67 | + if datetime_tzinfo(ob) is not None: |
| 68 | + offset = ob.utcoffset().total_seconds() |
| 69 | + dt.tzoffset = <int16_t> (offset / 60) |
| 70 | + |
| 71 | +cdef object datetime_to_py(IProtoDateTime *dt): |
| 72 | + cdef: |
| 73 | + double timestamp |
| 74 | + object tz |
| 75 | + |
| 76 | + tz = None |
| 77 | + |
| 78 | + if dt.tzoffset != 0: |
| 79 | + delta = timedelta_new(0, <int> dt.tzoffset * 60, 0) |
| 80 | + tz = timezone_new(delta) |
| 81 | + |
| 82 | + timestamp = dt.seconds + (<double> dt.nsec) / 1e9 |
| 83 | + return PyDateTimeAPI.DateTime_FromTimestamp( |
| 84 | + <PyObject *>PyDateTimeAPI.DateTimeType, |
| 85 | + (timestamp,) if tz is None else (timestamp, tz), |
| 86 | + NULL, |
| 87 | + ) |
0 commit comments