mirror of
https://github.com/lune-org/lune.git
synced 2025-04-07 20:10:55 +01:00
170 lines
4.9 KiB
Rust
170 lines
4.9 KiB
Rust
use mlua::prelude::*;
|
|
|
|
use chrono::prelude::*;
|
|
|
|
use lune_utils::TableBuilder;
|
|
|
|
use super::result::{DateTimeError, DateTimeResult};
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct DateTimeValues {
|
|
pub year: i32,
|
|
pub month: u32,
|
|
pub day: u32,
|
|
pub hour: u32,
|
|
pub minute: u32,
|
|
pub second: u32,
|
|
pub millisecond: u32,
|
|
}
|
|
|
|
impl DateTimeValues {
|
|
/**
|
|
Verifies that all of the date & time values are within allowed ranges:
|
|
|
|
| Name | Range |
|
|
|---------------|----------------|
|
|
| `year` | `1400 -> 9999` |
|
|
| `month` | `1 -> 12` |
|
|
| `day` | `1 -> 31` |
|
|
| `hour` | `0 -> 23` |
|
|
| `minute` | `0 -> 59` |
|
|
| `second` | `0 -> 60` |
|
|
| `millisecond` | `0 -> 999` |
|
|
*/
|
|
pub fn verify(self) -> DateTimeResult<Self> {
|
|
verify_in_range("year", self.year, 1400, 9999)?;
|
|
verify_in_range("month", self.month, 1, 12)?;
|
|
verify_in_range("day", self.day, 1, 31)?;
|
|
verify_in_range("hour", self.hour, 0, 23)?;
|
|
verify_in_range("minute", self.minute, 0, 59)?;
|
|
verify_in_range("second", self.second, 0, 60)?;
|
|
verify_in_range("millisecond", self.millisecond, 0, 999)?;
|
|
Ok(self)
|
|
}
|
|
}
|
|
|
|
fn verify_in_range<T>(name: &'static str, value: T, min: T, max: T) -> DateTimeResult<T>
|
|
where
|
|
T: PartialOrd + std::fmt::Display,
|
|
{
|
|
assert!(max > min);
|
|
if value < min || value > max {
|
|
Err(DateTimeError::OutOfRange {
|
|
name,
|
|
min: min.to_string(),
|
|
max: max.to_string(),
|
|
value: value.to_string(),
|
|
})
|
|
} else {
|
|
Ok(value)
|
|
}
|
|
}
|
|
|
|
/**
|
|
Conversion methods between `DateTimeValues` and plain lua tables
|
|
|
|
Note that the `IntoLua` implementation here uses a read-only table,
|
|
since we generally want to convert into lua when we know we have
|
|
a fixed point in time, and we guarantee that it doesn't change
|
|
*/
|
|
|
|
impl FromLua<'_> for DateTimeValues {
|
|
fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
|
|
if !value.is_table() {
|
|
return Err(LuaError::FromLuaConversionError {
|
|
from: value.type_name(),
|
|
to: "DateTimeValues",
|
|
message: Some("value must be a table".to_string()),
|
|
});
|
|
};
|
|
|
|
let value = value.as_table().unwrap();
|
|
let values = Self {
|
|
year: value.get("year")?,
|
|
month: value.get("month")?,
|
|
day: value.get("day")?,
|
|
hour: value.get("hour")?,
|
|
minute: value.get("minute")?,
|
|
second: value.get("second")?,
|
|
millisecond: value.get("millisecond").unwrap_or(0),
|
|
};
|
|
|
|
match values.verify() {
|
|
Ok(dt) => Ok(dt),
|
|
Err(e) => Err(LuaError::FromLuaConversionError {
|
|
from: "table",
|
|
to: "DateTimeValues",
|
|
message: Some(e.to_string()),
|
|
}),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl IntoLua<'_> for DateTimeValues {
|
|
fn into_lua(self, lua: &Lua) -> LuaResult<LuaValue> {
|
|
let tab = TableBuilder::new(lua)?
|
|
.with_value("year", self.year)?
|
|
.with_values(vec![
|
|
("month", self.month),
|
|
("day", self.day),
|
|
("hour", self.hour),
|
|
("minute", self.minute),
|
|
("second", self.second),
|
|
("millisecond", self.millisecond),
|
|
])?
|
|
.build_readonly()?;
|
|
Ok(LuaValue::Table(tab))
|
|
}
|
|
}
|
|
|
|
/**
|
|
Conversion methods between chrono's timezone-aware `DateTime` to
|
|
and from our non-timezone-aware `DateTimeValues` values struct
|
|
*/
|
|
|
|
impl<T: TimeZone> From<DateTime<T>> for DateTimeValues {
|
|
fn from(value: DateTime<T>) -> Self {
|
|
Self {
|
|
year: value.year(),
|
|
month: value.month(),
|
|
day: value.day(),
|
|
hour: value.hour(),
|
|
minute: value.minute(),
|
|
second: value.second(),
|
|
millisecond: value.timestamp_subsec_millis(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TryFrom<DateTimeValues> for DateTime<Utc> {
|
|
type Error = DateTimeError;
|
|
fn try_from(value: DateTimeValues) -> Result<Self, Self::Error> {
|
|
Utc.with_ymd_and_hms(
|
|
value.year,
|
|
value.month,
|
|
value.day,
|
|
value.hour,
|
|
value.minute,
|
|
value.second,
|
|
)
|
|
.single()
|
|
.ok_or(DateTimeError::Ambiguous)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<DateTimeValues> for DateTime<Local> {
|
|
type Error = DateTimeError;
|
|
fn try_from(value: DateTimeValues) -> Result<Self, Self::Error> {
|
|
Local
|
|
.with_ymd_and_hms(
|
|
value.year,
|
|
value.month,
|
|
value.day,
|
|
value.hour,
|
|
value.minute,
|
|
value.second,
|
|
)
|
|
.single()
|
|
.ok_or(DateTimeError::Ambiguous)
|
|
}
|
|
}
|