proto/finger: parse timezones
This commit is contained in:
Generated
+35
@@ -187,6 +187,16 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.6.1"
|
||||
@@ -747,6 +757,24 @@ version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.17"
|
||||
@@ -831,6 +859,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"caps",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"futures-util",
|
||||
@@ -974,6 +1003,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.12"
|
||||
|
||||
@@ -38,6 +38,7 @@ tokio-util = "0.7.18"
|
||||
caps = "0.5.6"
|
||||
users = { version = "0.11.0", default-features = false }
|
||||
tracing-journald = "0.3.2"
|
||||
chrono-tz = "0.10.4"
|
||||
|
||||
[features]
|
||||
default = ["systemd"]
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use std::path::PathBuf;
|
||||
use std::{path::PathBuf, str::FromStr};
|
||||
|
||||
use chrono::{DateTime, Datelike, Duration, NaiveDate, NaiveTime, TimeDelta, Utc, Weekday};
|
||||
use anyhow::Context;
|
||||
use chrono::{
|
||||
DateTime, Datelike, Duration, NaiveDate, NaiveTime, TimeDelta, TimeZone, Timelike, Utc, Weekday,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -132,9 +135,13 @@ fn parse_bsd_finger_time(time: &str) -> anyhow::Result<DateTime<Utc>> {
|
||||
let time_parts: Vec<_> = time.split_ascii_whitespace().collect();
|
||||
|
||||
let time = &time_parts[..time_parts.len() - 1].join(" ");
|
||||
let _timezone = time_parts[time_parts.len() - 1];
|
||||
let timezone = time_parts[time_parts.len() - 1]
|
||||
.trim_start_matches('(')
|
||||
.trim_end_matches(')');
|
||||
|
||||
let tz = chrono_tz::Tz::from_str(timezone)
|
||||
.context(format!("Failed to parse timezone in login time: {}", time))?;
|
||||
|
||||
let now = Utc::now();
|
||||
let mut parts = time.split_whitespace();
|
||||
|
||||
let weekday = match parts.next() {
|
||||
@@ -181,12 +188,13 @@ fn parse_bsd_finger_time(time: &str) -> anyhow::Result<DateTime<Utc>> {
|
||||
)
|
||||
})?;
|
||||
|
||||
let now = Utc::now();
|
||||
const MAX_YEARS_BACK: i32 = 10;
|
||||
|
||||
let mut year = None;
|
||||
for offset in 0..=MAX_YEARS_BACK {
|
||||
let year = now.year() - offset;
|
||||
let year_ = now.year() - offset;
|
||||
|
||||
let date = match NaiveDate::from_ymd_opt(year, month, day) {
|
||||
let date = match NaiveDate::from_ymd_opt(year_, month, day) {
|
||||
Some(d) => d,
|
||||
None => continue,
|
||||
};
|
||||
@@ -195,22 +203,26 @@ fn parse_bsd_finger_time(time: &str) -> anyhow::Result<DateTime<Utc>> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let dt = date.and_time(clock);
|
||||
|
||||
if dt <= now.naive_utc() {
|
||||
// TODO: apply timezone if we are able to parse it.
|
||||
// if not, try to get the local timezone offset.
|
||||
// if not, assume UTC.
|
||||
|
||||
return Ok(DateTime::<Utc>::from_naive_utc_and_offset(dt, Utc));
|
||||
}
|
||||
year = Some(year_);
|
||||
}
|
||||
|
||||
Err(anyhow::anyhow!(
|
||||
"Could not infer year for login time {} within {} years",
|
||||
time,
|
||||
MAX_YEARS_BACK
|
||||
))
|
||||
if year.is_none() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Could not find a valid year for login time {} within {} years",
|
||||
time,
|
||||
MAX_YEARS_BACK
|
||||
));
|
||||
}
|
||||
|
||||
tz.with_ymd_and_hms(year.unwrap(), month, day, clock.hour(), clock.minute(), 0)
|
||||
.single()
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"Failed to convert login time to timezone-aware datetime: {}",
|
||||
time
|
||||
)
|
||||
})
|
||||
.map(|dt| dt.with_timezone(&Utc))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
@@ -763,6 +775,7 @@ mod tests {
|
||||
"Tue Feb 28 23:59 (UTC)",
|
||||
"Wed Dec 31 00:00 (UTC)",
|
||||
"Wed Dec 31 00:00 (GMT)",
|
||||
"Wed Dec 31 00:00 (Asia/Tokyo)",
|
||||
];
|
||||
|
||||
for input in cases {
|
||||
|
||||
Reference in New Issue
Block a user