use crate::earth::python::ut1::{PyEopProvider, PyEopProviderError};
use crate::time::calendar_dates::CalendarDate;
use crate::time::python::time::PyTime;
use crate::time::python::time_scales::PyTimeScale;
use crate::time::time_of_day::CivilTime;
use crate::time::utc::{Utc, UtcError};
use lox_test_utils::{ApproxEq, approx_eq};
use pyo3::exceptions::PyValueError;
use pyo3::types::PyType;
use pyo3::{Bound, PyAny, PyErr, PyResult, pyclass, pymethods};
pub struct PyUtcError(pub UtcError);
impl From<PyUtcError> for PyErr {
fn from(value: PyUtcError) -> Self {
PyValueError::new_err(value.0.to_string())
}
}
#[pyclass(name = "UTC", module = "lox_space", frozen, from_py_object)]
#[derive(Clone, Debug, Eq, PartialEq, ApproxEq)]
pub struct PyUtc(pub Utc);
#[pymethods]
impl PyUtc {
#[new]
#[pyo3(signature = (year, month, day, hour = 0, minute = 0, seconds = 0.0))]
pub fn new(
year: i64,
month: u8,
day: u8,
hour: u8,
minute: u8,
seconds: f64,
) -> PyResult<PyUtc> {
let utc = Utc::builder()
.with_ymd(year, month, day)
.with_hms(hour, minute, seconds)
.build()
.map_err(PyUtcError)?;
Ok(PyUtc(utc))
}
#[classmethod]
pub fn from_iso(_cls: &Bound<'_, PyType>, iso: &str) -> PyResult<PyUtc> {
Ok(PyUtc(iso.parse().map_err(PyUtcError)?))
}
pub fn __str__(&self) -> String {
self.0.to_string()
}
pub fn __repr__(&self) -> String {
format!(
"UTC({}, {}, {}, {}, {}, {})",
self.0.year(),
self.0.month(),
self.0.day(),
self.0.hour(),
self.0.minute(),
self.0.as_seconds_f64()
)
}
pub fn __eq__(&self, other: PyUtc) -> bool {
self.0 == other.0
}
#[pyo3(signature = (other, rel_tol = 1e-8, abs_tol = 1e-14))]
pub fn isclose(&self, other: PyUtc, rel_tol: f64, abs_tol: f64) -> bool {
approx_eq!(self, &other, rtol <= rel_tol, atol <= abs_tol)
}
pub fn year(&self) -> i64 {
self.0.year()
}
pub fn month(&self) -> u8 {
self.0.month()
}
pub fn day(&self) -> u8 {
self.0.day()
}
pub fn hour(&self) -> u8 {
self.0.hour()
}
pub fn minute(&self) -> u8 {
self.0.minute()
}
pub fn second(&self) -> u8 {
self.0.second()
}
pub fn millisecond(&self) -> u32 {
self.0.millisecond()
}
pub fn microsecond(&self) -> u32 {
self.0.microsecond()
}
pub fn nanosecond(&self) -> u32 {
self.0.nanosecond()
}
pub fn picosecond(&self) -> u32 {
self.0.picosecond()
}
pub fn decimal_seconds(&self) -> f64 {
self.0.as_seconds_f64()
}
#[pyo3(signature = (scale, provider=None))]
pub fn to_scale(
&self,
scale: &Bound<'_, PyAny>,
provider: Option<&Bound<'_, PyEopProvider>>,
) -> PyResult<PyTime> {
let scale: PyTimeScale = scale.try_into()?;
let provider = provider.map(|p| &p.get().0);
let time = match provider {
Some(provider) => self
.0
.to_dyn_time()
.try_to_scale(scale.0, provider)
.map_err(PyEopProviderError)?,
None => self.0.to_dyn_time().to_scale(scale.0),
};
Ok(PyTime(time))
}
}