移除chrono

This commit is contained in:
tommy
2025-11-13 10:55:37 +08:00
parent 25433618b1
commit f41425bb89
9 changed files with 315 additions and 15 deletions

1
Cargo.lock generated
View File

@@ -1620,7 +1620,6 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
name = "dx-admin-template"
version = "0.1.0"
dependencies = [
"chrono",
"dioxus",
"js-sys",
]

View File

@@ -8,7 +8,6 @@ edition = "2021"
[dependencies]
dioxus = { version = "0.7.1", features = ["router", "fullstack"] }
chrono = { version = "0.4", default-features = false, features = ["std"] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys = "0.3"

View File

@@ -966,8 +966,7 @@ fn InteractiveTableSample() -> Element {
- `on_select` 返回选中的 `NaiveDate`
```rust
use crate::components::ui::Calendar;
use chrono::NaiveDate;
use crate::{components::ui::Calendar, time::NaiveDate};
use dioxus::prelude::*;
#[component]
@@ -992,8 +991,10 @@ fn CalendarSample() -> Element {
- `on_change` 返回新的区间或 `None`
```rust
use crate::components::ui::{DateRange, DateRangePicker};
use chrono::NaiveDate;
use crate::{
components::ui::{DateRange, DateRangePicker},
time::NaiveDate,
};
use dioxus::prelude::*;
#[component]

View File

@@ -1,4 +1,4 @@
use chrono::{Datelike, Duration, NaiveDate};
use crate::time::{Duration, NaiveDate};
use dioxus::prelude::*;
use super::utils::merge_class;
const WEEKDAY_LABELS: [&str; 7] = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];

View File

@@ -1,9 +1,12 @@
use super::button::{Button, ButtonSize, ButtonVariant};
use chrono::{Datelike, Duration, NaiveDate};
use super::utils::merge_class;
use crate::{
components::ui::PopoverHandle,
time::{Duration, NaiveDate},
};
use dioxus::prelude::*;
use super::utils::merge_class;#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm32"))]
use std::time::SystemTime;
use crate::components::ui::PopoverHandle;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DateRange {
@@ -77,9 +80,9 @@ fn today_date() -> NaiveDate {
#[cfg(not(target_arch = "wasm32"))]
fn today_date() -> NaiveDate {
let now = SystemTime::now();
let datetime: chrono::DateTime<chrono::Utc> = now.into();
datetime.date_naive()
NaiveDate::from_system_time(SystemTime::now())
.or_else(|| NaiveDate::from_ymd_opt(1970, 1, 1))
.expect("system time within supported range")
}
fn describe_range(range: DateRange) -> String {

View File

@@ -2,6 +2,7 @@
// need dioxus
use dioxus::prelude::*;
mod time;
use views::{Components, Home, Navbar, Orders};
/// Define a components module that contains all shared components for our app.

297
src/time.rs Normal file
View File

@@ -0,0 +1,297 @@
use std::fmt::{self, Display, Formatter, Write};
use std::ops::{Add, AddAssign, Sub, SubAssign};
use std::time::{SystemTime, UNIX_EPOCH};
const SECONDS_PER_DAY: i64 = 86_400;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NaiveDate {
days_since_epoch: i32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Duration {
days: i64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Weekday {
Monday = 0,
Tuesday = 1,
Wednesday = 2,
Thursday = 3,
Friday = 4,
Saturday = 5,
Sunday = 6,
}
impl Weekday {
pub fn num_days_from_monday(self) -> u8 {
self as u8
}
}
impl Duration {
pub fn days(days: i64) -> Self {
Self { days }
}
pub fn num_days(self) -> i64 {
self.days
}
}
#[derive(Clone, Copy)]
pub struct FormattedDate<'a> {
date: NaiveDate,
pattern: &'a str,
}
impl<'a> Display for FormattedDate<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(&self.date.format_internal(self.pattern))
}
}
impl NaiveDate {
pub fn from_ymd_opt(year: i32, month: u32, day: u32) -> Option<Self> {
if month == 0 || month > 12 {
return None;
}
if day == 0 || day > days_in_month(year, month) {
return None;
}
Some(Self {
days_since_epoch: days_from_civil(year, month, day)?,
})
}
pub fn with_day(self, day: u32) -> Option<Self> {
Self::from_ymd_opt(self.year(), self.month(), day)
}
pub fn year(self) -> i32 {
self.components().0
}
pub fn month(self) -> u32 {
self.components().1
}
pub fn day(self) -> u32 {
self.components().2
}
pub fn weekday(self) -> Weekday {
let idx = (self.days_since_epoch as i64 + 3).rem_euclid(7) as u8;
match idx {
0 => Weekday::Monday,
1 => Weekday::Tuesday,
2 => Weekday::Wednesday,
3 => Weekday::Thursday,
4 => Weekday::Friday,
5 => Weekday::Saturday,
_ => Weekday::Sunday,
}
}
pub fn format<'a>(self, pattern: &'a str) -> FormattedDate<'a> {
FormattedDate { date: self, pattern }
}
pub fn from_system_time(time: SystemTime) -> Option<Self> {
match time.duration_since(UNIX_EPOCH) {
Ok(duration) => {
let days = (duration.as_secs() as i64) / SECONDS_PER_DAY;
Self::from_days_since_epoch(days)
}
Err(err) => {
let duration = err.duration();
let days = (duration.as_secs() as i64 + SECONDS_PER_DAY - 1) / SECONDS_PER_DAY;
Self::from_days_since_epoch(-days)
}
}
}
pub fn from_days_since_epoch(days: i64) -> Option<Self> {
let value: i32 = days.try_into().ok()?;
Some(Self {
days_since_epoch: value,
})
}
fn components(self) -> (i32, u32, u32) {
civil_from_days(self.days_since_epoch)
}
fn format_internal(self, pattern: &str) -> String {
let (year, month, day) = self.components();
let mut output = String::with_capacity(pattern.len() + 8);
let mut chars = pattern.chars();
while let Some(ch) = chars.next() {
if ch == '%' {
if let Some(spec) = chars.next() {
match spec {
'Y' => {
let _ = write!(output, "{year:04}");
}
'm' => {
let _ = write!(output, "{month:02}");
}
'd' => {
let _ = write!(output, "{day:02}");
}
'B' => output.push_str(full_month(month)),
'b' => output.push_str(short_month(month)),
'%' => output.push('%'),
other => {
output.push('%');
output.push(other);
}
}
}
} else {
output.push(ch);
}
}
output
}
}
impl Add<Duration> for NaiveDate {
type Output = Self;
fn add(self, rhs: Duration) -> Self::Output {
let total = self.days_since_epoch as i64 + rhs.days;
Self::from_days_since_epoch(total).expect("date overflow")
}
}
impl AddAssign<Duration> for NaiveDate {
fn add_assign(&mut self, rhs: Duration) {
*self = *self + rhs;
}
}
impl Sub<Duration> for NaiveDate {
type Output = Self;
fn sub(self, rhs: Duration) -> Self::Output {
let total = self.days_since_epoch as i64 - rhs.days;
Self::from_days_since_epoch(total).expect("date overflow")
}
}
impl SubAssign<Duration> for NaiveDate {
fn sub_assign(&mut self, rhs: Duration) {
*self = *self - rhs;
}
}
impl Sub for NaiveDate {
type Output = Duration;
fn sub(self, rhs: NaiveDate) -> Self::Output {
Duration::days(self.days_since_epoch as i64 - rhs.days_since_epoch as i64)
}
}
fn days_in_month(year: i32, month: u32) -> u32 {
match month {
1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
4 | 6 | 9 | 11 => 30,
2 if is_leap_year(year) => 29,
2 => 28,
_ => 0,
}
}
fn is_leap_year(year: i32) -> bool {
(year % 4 == 0 && year % 100 != 0) || year % 400 == 0
}
fn days_from_civil(year: i32, month: u32, day: u32) -> Option<i32> {
let y = year - if month <= 2 { 1 } else { 0 };
let era = if y >= 0 { y } else { y - 399 } / 400;
let yoe = y - era * 400;
let mp = month as i32 + if month > 2 { -3 } else { 9 };
let doy = (153 * mp + 2) / 5 + day as i32 - 1;
let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
let days = era * 146097 + doe - 719_468;
days.try_into().ok()
}
fn civil_from_days(days: i32) -> (i32, u32, u32) {
let days = days as i64 + 719_468;
let era = if days >= 0 {
days / 146_097
} else {
(days - 146_096) / 146_097
};
let doe = days - era * 146_097;
let yoe = (doe - doe / 1_460 + doe / 36_524 - doe / 146_096) / 365;
let mut y = yoe + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let day = doy - (153 * mp + 2) / 5 + 1;
let month = mp + if mp < 10 { 3 } else { -9 };
y += if month <= 2 { 1 } else { 0 };
(y as i32, month as u32, day as u32)
}
fn full_month(month: u32) -> &'static str {
match month {
1 => "January",
2 => "February",
3 => "March",
4 => "April",
5 => "May",
6 => "June",
7 => "July",
8 => "August",
9 => "September",
10 => "October",
11 => "November",
12 => "December",
_ => "Unknown",
}
}
fn short_month(month: u32) -> &'static str {
match month {
1 => "Jan",
2 => "Feb",
3 => "Mar",
4 => "Apr",
5 => "May",
6 => "Jun",
7 => "Jul",
8 => "Aug",
9 => "Sep",
10 => "Oct",
11 => "Nov",
12 => "Dec",
_ => "Unk",
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_ymd() {
let date = NaiveDate::from_ymd_opt(2024, 6, 30).unwrap();
assert_eq!(date.year(), 2024);
assert_eq!(date.month(), 6);
assert_eq!(date.day(), 30);
assert_eq!(date.weekday().num_days_from_monday(), 6);
}
#[test]
fn add_days() {
let mut date = NaiveDate::from_ymd_opt(2024, 1, 31).unwrap();
date += Duration::days(1);
assert_eq!((date.year(), date.month(), date.day()), (2024, 2, 1));
}
}

View File

@@ -15,7 +15,7 @@ use crate::components::ui::{
TabsTrigger, Textarea, Toast, ToastViewport, Toggle, ToggleGroup, ToggleGroupItem,
ToggleGroupMode, ToggleGroupOrientation, Tooltip,
};
use chrono::NaiveDate;
use crate::time::NaiveDate;
use dioxus::html::events::FormEvent;
use dioxus::prelude::*;

View File

@@ -5,7 +5,7 @@ use crate::components::ui::{
SelectOption, Slider, Table, TableBody, TableCaption, TableCell, TableColumnConfig,
TableFooter, TableHead, TableHeader, TableRow, TableRowData,
};
use chrono::NaiveDate;
use crate::time::NaiveDate;
use dioxus::prelude::*;
const PAGE_SIZE: usize = 8;