1use chrono::format::*;
2use leptos::*;
3use std::io::Write;
4
5const MINUTE_SECONDS: u64 = 60;
6const HOUR_SECONDS: u64 = MINUTE_SECONDS * 60;
7const DAY_SECONDS: u64 = HOUR_SECONDS * 24;
8
9#[component]
10pub fn Clock(
11 #[prop(optional, into)] value: Option<MaybeSignal<std::time::Duration>>,
12 #[prop(optional, default="%H:%M:%S".into(), into)] format: MaybeSignal<String>,
13 #[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
14) -> impl IntoView {
15 let time = move || {
16 if let Some(v) = value {
17 let chrono_val = chrono::Duration::from_std(v()).unwrap();
18 let date = chrono::DateTime::UNIX_EPOCH
19 .checked_add_signed(chrono_val)
20 .unwrap();
21 date.format(&format()).to_string()
22 } else {
23 chrono::Local::now().format(&format()).to_string()
24 }
25 };
26
27 view! { <span {..attrs}>{time}</span> }
28}
29
30#[component]
31pub fn Timer(
32 #[prop(into)] value: MaybeSignal<std::time::Duration>,
33 #[prop(optional, default="%H:%M:%S".into(), into)] format: MaybeSignal<String>,
34 #[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
35) -> impl IntoView {
36 let value = create_memo(move |_| value.get());
37 let stringified = Signal::derive(move || {
39 let format = format();
40 let strf_items = StrftimeItems::new(&format);
41 let mut writer = Vec::with_capacity(32);
42 for (idx, item) in strf_items.enumerate() {
43 match item {
44 Item::Literal(l) => write!(writer, "{}", l),
45 Item::OwnedLiteral(l) => write!(writer, "{}", l),
46 Item::Space(s) => write!(writer, "{}", s),
47 Item::OwnedSpace(s) => write!(writer, "{}", s),
48 Item::Numeric(typ, pad) => {
49 let (mut val, modulo): (i128, i32) = match typ {
50 Numeric::Year => todo!(),
51 Numeric::YearDiv100 => todo!(),
52 Numeric::YearMod100 => todo!(),
53 Numeric::IsoYear => todo!(),
54 Numeric::IsoYearDiv100 => todo!(),
55 Numeric::IsoYearMod100 => todo!(),
56 Numeric::Month => todo!(),
57 Numeric::Day => ((value().as_secs() / DAY_SECONDS).into(), 28),
58 Numeric::WeekFromSun => todo!(),
59 Numeric::WeekFromMon => todo!(),
60 Numeric::IsoWeek => todo!(),
61 Numeric::NumDaysFromSun => todo!(),
62 Numeric::WeekdayFromMon => todo!(),
63 Numeric::Ordinal => todo!(),
64 Numeric::Hour => ((value().as_secs() / 3600).into(), 24),
65 Numeric::Hour12 => todo!(),
66 Numeric::Minute => ((value().as_secs() / 60).into(), 60),
67 Numeric::Second => ((value().as_secs()).into(), 60),
68 Numeric::Nanosecond => ((value().as_nanos()) as i128, 1_000_000_000),
69 Numeric::Timestamp => todo!(),
70 Numeric::Internal(_) => todo!(),
71 _ => unreachable!(),
72 };
73
74 if idx != 0 && modulo > 0 {
75 val %= modulo as i128
76 };
77
78 match pad {
79 Pad::None => write!(writer, "{}", val),
80 Pad::Zero => write!(writer, "{:0width$}", val, width = 2),
81 Pad::Space => write!(writer, "{:width$}", val, width = 2),
82 }
83 }
84 Item::Fixed(f) => match f {
85 Fixed::ShortMonthName => todo!(),
86 Fixed::LongMonthName => todo!(),
87 Fixed::ShortWeekdayName => todo!(),
88 Fixed::LongWeekdayName => todo!(),
89 Fixed::LowerAmPm => todo!(),
90 Fixed::UpperAmPm => todo!(),
91 Fixed::Nanosecond => write!(writer, ".{}", value().as_nanos() % 1_000_000_000),
92 Fixed::Nanosecond3 => {
93 write!(writer, ".{:03}", value().as_nanos() / 1_000_000 % 1_000)
94 }
95 Fixed::Nanosecond6 => {
96 write!(writer, ".{:06}", value().as_nanos() / 1_000 % 1_000_000)
97 }
98 Fixed::Nanosecond9 => {
99 write!(writer, ".{:09}", value().as_nanos() % 1_000_000_000)
100 }
101 Fixed::TimezoneName => todo!(),
102 Fixed::TimezoneOffsetColon => todo!(),
103 Fixed::TimezoneOffsetDoubleColon => todo!(),
104 Fixed::TimezoneOffsetTripleColon => todo!(),
105 Fixed::TimezoneOffsetColonZ => todo!(),
106 Fixed::TimezoneOffset => todo!(),
107 Fixed::TimezoneOffsetZ => todo!(),
108 Fixed::RFC2822 => todo!(),
109 Fixed::RFC3339 => todo!(),
110 Fixed::Internal(_) => {
111 if format.contains("%3f") {
112 write!(writer, "{:03}", value().as_nanos() / 1_000_000 % 1_000)
113 } else if format.contains("%6f") {
114 write!(writer, "{:06}", value().as_nanos() / 1_000 % 1_000_000)
115 } else {
116 write!(writer, "{:09}", value().as_nanos() % 1_000_000_000)
117 }
118 }
119 _ => unreachable!(),
120 },
121 Item::Error => todo!(),
122 }
123 .unwrap();
125 }
126
127 String::from_utf8(writer).unwrap_or_default()
128 });
129
130 view! { <span {..attrs}>{stringified}</span> }
131}