nautilus_core/python/datetime.rs
1// -------------------------------------------------------------------------------------------------
2// Copyright (C) 2015-2025 Posei Systems Pty Ltd. All rights reserved.
3// https://poseitrader.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::*;
19
20use super::to_pyvalue_err;
21use crate::{
22 UnixNanos,
23 datetime::{
24 is_within_last_24_hours, last_weekday_nanos, micros_to_nanos, millis_to_nanos,
25 nanos_to_micros, nanos_to_millis, nanos_to_secs, secs_to_millis, secs_to_nanos,
26 unix_nanos_to_iso8601, unix_nanos_to_iso8601_millis,
27 },
28};
29
30/// Return round nanoseconds (ns) converted from the given seconds.
31///
32/// Parameters
33/// ----------
34/// secs : float
35/// The seconds to convert.
36///
37/// Returns
38/// -------
39/// int
40#[must_use]
41#[pyfunction(name = "secs_to_nanos")]
42pub fn py_secs_to_nanos(secs: f64) -> u64 {
43 secs_to_nanos(secs)
44}
45
46/// Return round milliseconds (ms) converted from the given seconds.
47///
48/// Parameters
49/// ----------
50/// secs : float
51/// The seconds to convert.
52///
53/// Returns
54/// -------
55/// int
56#[must_use]
57#[pyfunction(name = "secs_to_millis")]
58pub fn py_secs_to_millis(secs: f64) -> u64 {
59 secs_to_millis(secs)
60}
61
62/// Return round nanoseconds (ns) converted from the given milliseconds (ms).
63///
64/// Parameters
65/// ----------
66/// millis : float
67/// The milliseconds to convert.
68///
69/// Returns
70/// -------
71/// int
72#[must_use]
73#[pyfunction(name = "millis_to_nanos")]
74pub fn py_millis_to_nanos(millis: f64) -> u64 {
75 millis_to_nanos(millis)
76}
77
78/// Return round nanoseconds (ns) converted from the given microseconds (μs).
79///
80/// Parameters
81/// ----------
82/// micros : float
83/// The microseconds to convert.
84///
85/// Returns
86/// -------
87/// int
88#[must_use]
89#[pyfunction(name = "micros_to_nanos")]
90pub fn py_micros_to_nanos(micros: f64) -> u64 {
91 micros_to_nanos(micros)
92}
93
94/// Return seconds converted from the given nanoseconds (ns).
95///
96/// Parameters
97/// ----------
98/// nanos : int
99/// The nanoseconds to convert.
100///
101/// Returns
102/// -------
103/// float
104#[must_use]
105#[pyfunction(name = "nanos_to_secs")]
106pub fn py_nanos_to_secs(nanos: u64) -> f64 {
107 nanos_to_secs(nanos)
108}
109
110/// Return round milliseconds (ms) converted from the given nanoseconds (ns).
111///
112/// Parameters
113/// ----------
114/// nanos : int
115/// The nanoseconds to convert.
116///
117/// Returns
118/// -------
119/// int
120#[must_use]
121#[pyfunction(name = "nanos_to_millis")]
122pub const fn py_nanos_to_millis(nanos: u64) -> u64 {
123 nanos_to_millis(nanos)
124}
125
126/// Return round microseconds (μs) converted from the given nanoseconds (ns).
127///
128/// Parameters
129/// ----------
130/// nanos : int
131/// The nanoseconds to convert.
132///
133/// Returns
134/// -------
135/// int
136#[must_use]
137#[pyfunction(name = "nanos_to_micros")]
138pub const fn py_nanos_to_micros(nanos: u64) -> u64 {
139 nanos_to_micros(nanos)
140}
141
142/// Return UNIX nanoseconds as an ISO 8601 (RFC 3339) format string.
143///
144/// Parameters
145/// ----------
146/// `timestamp_ns` : int
147/// The UNIX timestamp (nanoseconds).
148/// `nanos_precision` : bool, default True
149/// If True, use nanosecond precision. If False, use millisecond precision.
150///
151/// Returns
152/// -------
153/// str
154///
155/// Raises
156/// ------
157/// `ValueError`
158/// If `timestamp_ns` is invalid.
159#[must_use]
160#[pyfunction(name = "unix_nanos_to_iso8601", signature = (timestamp_ns, nanos_precision=true))]
161pub fn py_unix_nanos_to_iso8601(timestamp_ns: u64, nanos_precision: Option<bool>) -> String {
162 let unix_nanos = timestamp_ns.into();
163 if nanos_precision.unwrap_or(true) {
164 unix_nanos_to_iso8601(unix_nanos)
165 } else {
166 unix_nanos_to_iso8601_millis(unix_nanos)
167 }
168}
169
170/// Return UNIX nanoseconds at midnight (UTC) of the last weekday (Mon-Fri).
171///
172/// Parameters
173/// ----------
174/// year : int
175/// The year from the datum date.
176/// month : int
177/// The month from the datum date.
178/// day : int
179/// The day from the datum date.
180///
181/// Returns
182/// -------
183/// int
184///
185/// Raises
186/// ------
187/// `ValueError`
188/// If given an invalid date.
189///
190/// # Errors
191///
192/// Returns a `PyErr` if the provided date is invalid.
193#[pyfunction(name = "last_weekday_nanos")]
194pub fn py_last_weekday_nanos(year: i32, month: u32, day: u32) -> PyResult<u64> {
195 Ok(last_weekday_nanos(year, month, day)
196 .map_err(to_pyvalue_err)?
197 .as_u64())
198}
199
200/// Return whether the given UNIX nanoseconds timestamp is within the last 24 hours.
201///
202/// Parameters
203/// ----------
204/// `timestamp_ns` : int
205/// The UNIX nanoseconds timestamp datum.
206///
207/// Returns
208/// -------
209/// bool
210///
211/// Raises
212/// ------
213/// `ValueError`
214/// If `timestamp` is invalid.
215///
216/// # Errors
217///
218/// Returns a `PyErr` if the provided timestamp is invalid.
219#[pyfunction(name = "is_within_last_24_hours")]
220pub fn py_is_within_last_24_hours(timestamp_ns: u64) -> PyResult<bool> {
221 is_within_last_24_hours(UnixNanos::from(timestamp_ns)).map_err(to_pyvalue_err)
222}