1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#![cfg(not(Py_LIMITED_API))]

use pyo3::prelude::*;
use pyo3::types::{
    PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess, PyTuple,
    PyTzInfo, PyTzInfoAccess,
};

#[pyfunction]
fn make_date(py: Python<'_>, year: i32, month: u8, day: u8) -> PyResult<Bound<'_, PyDate>> {
    PyDate::new_bound(py, year, month, day)
}

#[pyfunction]
fn get_date_tuple<'py>(d: &Bound<'py, PyDate>) -> Bound<'py, PyTuple> {
    PyTuple::new_bound(
        d.py(),
        [d.get_year(), d.get_month() as i32, d.get_day() as i32],
    )
}

#[pyfunction]
fn date_from_timestamp(py: Python<'_>, timestamp: i64) -> PyResult<Bound<'_, PyDate>> {
    PyDate::from_timestamp_bound(py, timestamp)
}

#[pyfunction]
#[pyo3(signature=(hour, minute, second, microsecond, tzinfo=None))]
fn make_time<'py>(
    py: Python<'py>,
    hour: u8,
    minute: u8,
    second: u8,
    microsecond: u32,
    tzinfo: Option<&Bound<'py, PyTzInfo>>,
) -> PyResult<Bound<'py, PyTime>> {
    PyTime::new_bound(py, hour, minute, second, microsecond, tzinfo)
}

#[pyfunction]
#[pyo3(signature = (hour, minute, second, microsecond, tzinfo, fold))]
fn time_with_fold<'py>(
    py: Python<'py>,
    hour: u8,
    minute: u8,
    second: u8,
    microsecond: u32,
    tzinfo: Option<&Bound<'py, PyTzInfo>>,
    fold: bool,
) -> PyResult<Bound<'py, PyTime>> {
    PyTime::new_bound_with_fold(py, hour, minute, second, microsecond, tzinfo, fold)
}

#[pyfunction]
fn get_time_tuple<'py>(dt: &Bound<'py, PyTime>) -> Bound<'py, PyTuple> {
    PyTuple::new_bound(
        dt.py(),
        [
            dt.get_hour() as u32,
            dt.get_minute() as u32,
            dt.get_second() as u32,
            dt.get_microsecond(),
        ],
    )
}

#[pyfunction]
fn get_time_tuple_fold<'py>(dt: &Bound<'py, PyTime>) -> Bound<'py, PyTuple> {
    PyTuple::new_bound(
        dt.py(),
        [
            dt.get_hour() as u32,
            dt.get_minute() as u32,
            dt.get_second() as u32,
            dt.get_microsecond(),
            dt.get_fold() as u32,
        ],
    )
}

#[pyfunction]
fn make_delta(
    py: Python<'_>,
    days: i32,
    seconds: i32,
    microseconds: i32,
) -> PyResult<Bound<'_, PyDelta>> {
    PyDelta::new_bound(py, days, seconds, microseconds, true)
}

#[pyfunction]
fn get_delta_tuple<'py>(delta: &Bound<'py, PyDelta>) -> Bound<'py, PyTuple> {
    PyTuple::new_bound(
        delta.py(),
        [
            delta.get_days(),
            delta.get_seconds(),
            delta.get_microseconds(),
        ],
    )
}

#[allow(clippy::too_many_arguments)]
#[pyfunction]
#[pyo3(signature=(year, month, day, hour, minute, second, microsecond, tzinfo=None))]
fn make_datetime<'py>(
    py: Python<'py>,
    year: i32,
    month: u8,
    day: u8,
    hour: u8,
    minute: u8,
    second: u8,
    microsecond: u32,
    tzinfo: Option<&Bound<'py, PyTzInfo>>,
) -> PyResult<Bound<'py, PyDateTime>> {
    PyDateTime::new_bound(
        py,
        year,
        month,
        day,
        hour,
        minute,
        second,
        microsecond,
        tzinfo,
    )
}

#[pyfunction]
fn get_datetime_tuple<'py>(dt: &Bound<'py, PyDateTime>) -> Bound<'py, PyTuple> {
    PyTuple::new_bound(
        dt.py(),
        [
            dt.get_year(),
            dt.get_month() as i32,
            dt.get_day() as i32,
            dt.get_hour() as i32,
            dt.get_minute() as i32,
            dt.get_second() as i32,
            dt.get_microsecond() as i32,
        ],
    )
}

#[pyfunction]
fn get_datetime_tuple_fold<'py>(dt: &Bound<'py, PyDateTime>) -> Bound<'py, PyTuple> {
    PyTuple::new_bound(
        dt.py(),
        [
            dt.get_year(),
            dt.get_month() as i32,
            dt.get_day() as i32,
            dt.get_hour() as i32,
            dt.get_minute() as i32,
            dt.get_second() as i32,
            dt.get_microsecond() as i32,
            dt.get_fold() as i32,
        ],
    )
}

#[pyfunction]
#[pyo3(signature=(ts, tz=None))]
fn datetime_from_timestamp<'py>(
    py: Python<'py>,
    ts: f64,
    tz: Option<&Bound<'py, PyTzInfo>>,
) -> PyResult<Bound<'py, PyDateTime>> {
    PyDateTime::from_timestamp_bound(py, ts, tz)
}

#[pyfunction]
fn get_datetime_tzinfo<'py>(dt: &Bound<'py, PyDateTime>) -> Option<Bound<'py, PyTzInfo>> {
    dt.get_tzinfo_bound()
}

#[pyfunction]
fn get_time_tzinfo<'py>(dt: &Bound<'py, PyTime>) -> Option<Bound<'py, PyTzInfo>> {
    dt.get_tzinfo_bound()
}

#[pyclass(extends=PyTzInfo)]
pub struct TzClass {}

#[pymethods]
impl TzClass {
    #[new]
    fn new() -> Self {
        TzClass {}
    }

    fn utcoffset<'py>(&self, dt: &Bound<'py, PyDateTime>) -> PyResult<Bound<'py, PyDelta>> {
        PyDelta::new_bound(dt.py(), 0, 3600, 0, true)
    }

    fn tzname(&self, _dt: &Bound<'_, PyDateTime>) -> String {
        String::from("+01:00")
    }

    fn dst<'py>(&self, _dt: &Bound<'py, PyDateTime>) -> Option<Bound<'py, PyDelta>> {
        None
    }
}

#[pymodule]
pub fn datetime(m: &Bound<'_, PyModule>) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(make_date, m)?)?;
    m.add_function(wrap_pyfunction!(get_date_tuple, m)?)?;
    m.add_function(wrap_pyfunction!(date_from_timestamp, m)?)?;
    m.add_function(wrap_pyfunction!(make_time, m)?)?;
    m.add_function(wrap_pyfunction!(get_time_tuple, m)?)?;
    m.add_function(wrap_pyfunction!(make_delta, m)?)?;
    m.add_function(wrap_pyfunction!(get_delta_tuple, m)?)?;
    m.add_function(wrap_pyfunction!(make_datetime, m)?)?;
    m.add_function(wrap_pyfunction!(get_datetime_tuple, m)?)?;
    m.add_function(wrap_pyfunction!(datetime_from_timestamp, m)?)?;
    m.add_function(wrap_pyfunction!(get_datetime_tzinfo, m)?)?;
    m.add_function(wrap_pyfunction!(get_time_tzinfo, m)?)?;

    m.add_function(wrap_pyfunction!(time_with_fold, m)?)?;
    m.add_function(wrap_pyfunction!(get_time_tuple_fold, m)?)?;
    m.add_function(wrap_pyfunction!(get_datetime_tuple_fold, m)?)?;

    m.add_class::<TzClass>()?;

    Ok(())
}