Skip to main content

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}