Skip to main content

plot/lib/logic/
tag_time.rs

1use chrono::{Datelike, Local, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Weekday};
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct TagTime(pub String);
5
6// =====================================================================
7// ZASADY / REGUŁY (Czysta matematyka czasu)
8// =====================================================================
9
10/// Konwertuje nanosekundy (0 - 999_999_999) na Tercje i Qwadry.
11/// Zwraca krotkę: (tercja, qwadra)
12fn nanos_to_tq(nanos: u32) -> (u32, u32) {
13	let nanos_u64 = nanos as u64;
14	let t = (nanos_u64 * 60 / 1_000_000_000) as u32;
15	let q = ((nanos_u64 * 3600 / 1_000_000_000) % 60) as u32;
16	(t, q)
17}
18
19/// Konwertuje Tercje i Qwadry z powrotem na nanosekundy.
20/// Używane podczas rekonstrukcji czasu z tagu.
21fn tq_to_nanos(t: u32, q: u32) -> u32 {
22	let total_q = (t as u64 * 60) + q as u64;
23	// Odwrócenie wzoru z zachowaniem maksymalnej precyzji:
24	((total_q * 1_000_000_000) / 3600) as u32
25}
26
27// =====================================================================
28// CZYSTE FUNKCJE MAPUJĄCE (Pure Functions)
29// =====================================================================
30
31/// Generuje TagTime na podstawie dowolnego obiektu czasu.
32/// Akceptuje wszystko, co implementuje Datelike i Timelike (np. DateTime<Local>, NaiveDateTime).
33pub fn tag_from_time<T: Datelike + Timelike>(time: &T) -> TagTime {
34	// Używamy roku ISO, aby utworzona z tygodniem ISO para (Rok, Tydzień) była jednoznaczna.
35	let r = time.iso_week().year();
36	let w = time.iso_week().week();
37	let d = time.weekday().number_from_monday();
38
39	let h = time.hour();
40	let m = time.minute();
41	let s = time.second();
42
43	let (t, q) = nanos_to_tq(time.nanosecond());
44
45	TagTime(format!("R{r:04}W{w:02}D{d}H{h:02}M{m:02}S{s:02}T{t:02}Q{q:02}"))
46}
47
48/// Wygodny wrapper: Pobiera aktualny czas systemowy i od razu zwraca TagTime.
49pub fn tag_from_time_now() -> TagTime {
50	let now = Local::now();
51	tag_from_time(&now)
52}
53
54/// Parsuje TagTime z powrotem do naiwnego obiektu czasu (NaiveDateTime).
55/// Ponieważ sam tag nie przechowuje strefy czasowej, zwracamy czas "naiwny".
56pub fn tag_to_time(tag: &TagTime) -> Option<NaiveDateTime> {
57	let s = &tag.0;
58
59	// RRRRWWDHHMMSSTTQQ + litery = dokładnie 25 znaków
60	// np. "R2023W42D1H14M30S15T20Q15"
61	if s.len() != 25 {
62		return None;
63	}
64
65	// Bezpieczne, pozycyjne wyciąganie danych z gwarantowanej struktury ASCII
66	let r: i32 = s[1..5].parse().ok()?;
67	let w: u32 = s[6..8].parse().ok()?;
68	let d: u32 = s[9..10].parse().ok()?;
69	let h: u32 = s[11..13].parse().ok()?;
70	let m: u32 = s[14..16].parse().ok()?;
71	let sec: u32 = s[17..19].parse().ok()?;
72	let t: u32 = s[20..22].parse().ok()?;
73	let q: u32 = s[23..25].parse().ok()?;
74
75	// Odtworzenie daty (Rok ISO, Tydzień ISO, Dzień tygodnia)
76	let weekday = match d {
77		1 => Weekday::Mon,
78		2 => Weekday::Tue,
79		3 => Weekday::Wed,
80		4 => Weekday::Thu,
81		5 => Weekday::Fri,
82		6 => Weekday::Sat,
83		7 => Weekday::Sun,
84		_ => return None,
85	};
86	let date = NaiveDate::from_isoywd_opt(r, w, weekday)?;
87
88	// Odtworzenie czasu
89	let nanos = tq_to_nanos(t, q);
90	let time = NaiveTime::from_hms_nano_opt(h, m, sec, nanos)?;
91
92	Some(NaiveDateTime::new(date, time))
93}