nautilus_core/python/
datetime.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Date/time utility wrappers exposed to Python.
17
18use pyo3::prelude::*;
19use pyo3_stub_gen::derive::gen_stub_pyfunction;
20
21use super::to_pyvalue_err;
22use crate::{
23    UnixNanos,
24    datetime::{
25        is_within_last_24_hours, last_weekday_nanos, micros_to_nanos, millis_to_nanos,
26        nanos_to_micros, nanos_to_millis, nanos_to_secs, secs_to_millis, secs_to_nanos,
27        unix_nanos_to_iso8601, unix_nanos_to_iso8601_millis,
28    },
29};
30
31/// Return round nanoseconds (ns) converted from the given seconds.
32///
33/// Parameters
34/// ----------
35/// secs : float
36///     The seconds to convert.
37///
38/// Returns
39/// -------
40/// int
41#[must_use]
42#[gen_stub_pyfunction]
43#[pyfunction(name = "secs_to_nanos")]
44pub fn py_secs_to_nanos(secs: f64) -> u64 {
45    secs_to_nanos(secs)
46}
47
48/// Return round milliseconds (ms) converted from the given seconds.
49///
50/// Parameters
51/// ----------
52/// secs : float
53///     The seconds to convert.
54///
55/// Returns
56/// -------
57/// int
58#[must_use]
59#[gen_stub_pyfunction]
60#[pyfunction(name = "secs_to_millis")]
61pub fn py_secs_to_millis(secs: f64) -> u64 {
62    secs_to_millis(secs)
63}
64
65/// Return round nanoseconds (ns) converted from the given milliseconds (ms).
66///
67/// Parameters
68/// ----------
69/// millis : float
70///     The milliseconds to convert.
71///
72/// Returns
73/// -------
74/// int
75#[must_use]
76#[pyfunction(name = "millis_to_nanos")]
77pub fn py_millis_to_nanos(millis: f64) -> u64 {
78    millis_to_nanos(millis)
79}
80
81/// Return round nanoseconds (ns) converted from the given microseconds (μs).
82///
83/// Parameters
84/// ----------
85/// micros : float
86///     The microseconds to convert.
87///
88/// Returns
89/// -------
90/// int
91#[must_use]
92#[pyfunction(name = "micros_to_nanos")]
93pub fn py_micros_to_nanos(micros: f64) -> u64 {
94    micros_to_nanos(micros)
95}
96
97/// Return seconds converted from the given nanoseconds (ns).
98///
99/// Parameters
100/// ----------
101/// nanos : int
102///     The nanoseconds to convert.
103///
104/// Returns
105/// -------
106/// float
107#[must_use]
108#[pyfunction(name = "nanos_to_secs")]
109pub fn py_nanos_to_secs(nanos: u64) -> f64 {
110    nanos_to_secs(nanos)
111}
112
113/// Return round milliseconds (ms) converted from the given nanoseconds (ns).
114///
115/// Parameters
116/// ----------
117/// nanos : int
118///     The nanoseconds to convert.
119///
120/// Returns
121/// -------
122/// int
123#[must_use]
124#[pyfunction(name = "nanos_to_millis")]
125pub const fn py_nanos_to_millis(nanos: u64) -> u64 {
126    nanos_to_millis(nanos)
127}
128
129/// Return round microseconds (μs) converted from the given nanoseconds (ns).
130///
131/// Parameters
132/// ----------
133/// nanos : int
134///     The nanoseconds to convert.
135///
136/// Returns
137/// -------
138/// int
139#[must_use]
140#[pyfunction(name = "nanos_to_micros")]
141pub const fn py_nanos_to_micros(nanos: u64) -> u64 {
142    nanos_to_micros(nanos)
143}
144
145/// Return UNIX nanoseconds as an ISO 8601 (RFC 3339) format string.
146///
147/// Parameters
148/// ----------
149/// `timestamp_ns` : int
150///     The UNIX timestamp (nanoseconds).
151/// `nanos_precision` : bool, default True
152///     If True, use nanosecond precision. If False, use millisecond precision.
153///
154/// Returns
155/// -------
156/// str
157///
158/// Raises
159/// ------
160/// `ValueError`
161///     If `timestamp_ns` is invalid.
162#[must_use]
163#[pyfunction(name = "unix_nanos_to_iso8601", signature = (timestamp_ns, nanos_precision=true))]
164pub fn py_unix_nanos_to_iso8601(timestamp_ns: u64, nanos_precision: Option<bool>) -> String {
165    let unix_nanos = timestamp_ns.into();
166    if nanos_precision.unwrap_or(true) {
167        unix_nanos_to_iso8601(unix_nanos)
168    } else {
169        unix_nanos_to_iso8601_millis(unix_nanos)
170    }
171}
172
173/// Return UNIX nanoseconds at midnight (UTC) of the last weekday (Mon-Fri).
174///
175/// Parameters
176/// ----------
177/// year : int
178///     The year from the datum date.
179/// month : int
180///     The month from the datum date.
181/// day : int
182///     The day from the datum date.
183///
184/// Returns
185/// -------
186/// int
187///
188/// Raises
189/// ------
190/// `ValueError`
191///     If given an invalid date.
192///
193/// # Errors
194///
195/// Returns a `PyErr` if the provided date is invalid.
196#[pyfunction(name = "last_weekday_nanos")]
197pub fn py_last_weekday_nanos(year: i32, month: u32, day: u32) -> PyResult<u64> {
198    Ok(last_weekday_nanos(year, month, day)
199        .map_err(to_pyvalue_err)?
200        .as_u64())
201}
202
203/// Return whether the given UNIX nanoseconds timestamp is within the last 24 hours.
204///
205/// Parameters
206/// ----------
207/// `timestamp_ns` : int
208///     The UNIX nanoseconds timestamp datum.
209///
210/// Returns
211/// -------
212/// bool
213///
214/// Raises
215/// ------
216/// `ValueError`
217///     If `timestamp` is invalid.
218///
219/// # Errors
220///
221/// Returns a `PyErr` if the provided timestamp is invalid.
222#[pyfunction(name = "is_within_last_24_hours")]
223pub fn py_is_within_last_24_hours(timestamp_ns: u64) -> PyResult<bool> {
224    is_within_last_24_hours(UnixNanos::from(timestamp_ns)).map_err(to_pyvalue_err)
225}