keepass/db/types/times.rs
1use chrono::NaiveDateTime;
2
3/// Timestamps for a [Group][crate::db::Group] or [Entry][crate::db::Entry]
4///
5/// As the KeePass file format does not store time zone information and does not store sub-second
6/// precision, all times are stored as [NaiveDateTime] with second precision.
7#[derive(Debug, Default, PartialEq, Eq, Clone)]
8#[non_exhaustive]
9#[cfg_attr(feature = "serialization", derive(serde::Serialize))]
10pub struct Times {
11 /// The time of creation
12 pub creation: Option<NaiveDateTime>,
13
14 /// The time of the last modification
15 pub last_modification: Option<NaiveDateTime>,
16
17 /// The time of the last access
18 pub last_access: Option<NaiveDateTime>,
19
20 /// The time of expiration
21 pub expiry: Option<NaiveDateTime>,
22
23 /// The time of the last location change, which is updated when an entry is moved to a different group.
24 pub location_changed: Option<NaiveDateTime>,
25
26 /// Whether the entry or group expires.
27 ///
28 /// A `None` value indicates that the expiration status is not set
29 pub expires: Option<bool>,
30
31 /// The number of times the entry or group has been accessed.
32 pub usage_count: Option<usize>,
33}
34
35// On non-browser targets — including wasm32-wasip1 and wasm32-wasip2 —
36// chrono::Utc::now() works (wasi clocks via WASI 0.1+ are supported by
37// chrono's `clock` feature). The `js_sys::Date::now()` path is only
38// usable in true browser contexts (target_arch = "wasm32" with target_os
39// = "unknown"), where wasm-bindgen post-processes the binary to resolve
40// the placeholder imports. Targeting "wasm32" alone unintentionally
41// catches WASI builds and breaks linking.
42#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
43fn now_timestamp() -> i64 {
44 chrono::Utc::now().timestamp()
45}
46
47#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
48fn now_timestamp() -> i64 {
49 // Use JS Date.now() to get the current time in milliseconds, then convert it to seconds.
50 let millis = js_sys::Date::now();
51 (millis / 1000.0) as i64
52}
53
54impl Times {
55 /// Returns the current time, without the nanoseconds since the last leap second.
56 #[allow(clippy::unwrap_used, clippy::missing_panics_doc)] // current time should always be valid
57 pub fn now() -> NaiveDateTime {
58 let now = now_timestamp();
59 chrono::DateTime::from_timestamp(now, 0).unwrap().naive_utc()
60 }
61
62 /// Returns the epoch time (January 1, 1970, 00:00:00 UTC) as a `NaiveDateTime`.
63 #[allow(clippy::unwrap_used, clippy::missing_panics_doc)] // epoch time is always valid
64 pub fn epoch() -> NaiveDateTime {
65 chrono::DateTime::from_timestamp(0, 0).unwrap().naive_utc()
66 }
67
68 /// Creates a new `Times` instance with all timestamps set to the current time, `expires` set
69 /// to `false`, and `usage_count` set to `0`.
70 pub fn new() -> Self {
71 let now = Times::now();
72 Times {
73 creation: Some(now),
74 last_modification: Some(now),
75 last_access: Some(now),
76 expiry: None,
77 location_changed: Some(now),
78 expires: Some(false),
79 usage_count: Some(0),
80 }
81 }
82}