Skip to content

Commit 8118dae

Browse files
committed
Properly format history timestmap
This Pr addresses #1472. When the session history is displayed the timestmap isn't in readable form. This Pr changes the time from unix seconds into proper human readbale date/time.
1 parent f7b415a commit 8118dae

2 files changed

Lines changed: 60 additions & 18 deletions

File tree

payjoin-cli/src/app/v2/mod.rs

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,52 @@ fn print_header() {
9696
);
9797
}
9898

99+
/// Format a Unix timestamp (seconds since epoch) as `YYYY-MM-DD HH:MM:SS`
100+
/// using Howard Hinnant's civil_from_days algorithm — no external dependencies.
101+
fn format_unix_secs(secs: u64) -> String {
102+
fn civil_from_days(z: i64) -> (i32, u32, u32) {
103+
let z = z + 719_468;
104+
let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
105+
let doe = (z - era * 146_097) as u64;
106+
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146_096) / 365;
107+
let y = yoe as i64 + era * 400;
108+
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
109+
let mp = (5 * doy + 2) / 153;
110+
let d = doy - (153 * mp + 2) / 5 + 1;
111+
let m = if mp < 10 { mp + 3 } else { mp - 9 };
112+
let y = if m <= 2 { y + 1 } else { y };
113+
(y as i32, m as u32, d as u32)
114+
}
115+
116+
let days = (secs / 86_400) as i64;
117+
let tod = secs % 86_400;
118+
let (year, month, day) = civil_from_days(days);
119+
format!(
120+
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
121+
year,
122+
month,
123+
day,
124+
tod / 3600,
125+
(tod % 3600) / 60,
126+
tod % 60
127+
)
128+
}
129+
130+
/// Reformat error messages that contain a raw `Time(Time(timestamp))` debug repr,
131+
/// replacing the opaque token with a human-readable UTC datetime.
132+
fn format_session_error(msg: &str) -> String {
133+
const PREFIX: &str = "Session expired at Time(Time(";
134+
const SUFFIX: &str = "))";
135+
if let Some(rest) = msg.strip_prefix(PREFIX) {
136+
if let Some(num_str) = rest.strip_suffix(SUFFIX) {
137+
if let Ok(secs) = num_str.parse::<u64>() {
138+
return format!("Session expired at {} UTC", format_unix_secs(secs));
139+
}
140+
}
141+
}
142+
msg.to_string()
143+
}
144+
99145
enum Role {
100146
Sender,
101147
Receiver,
@@ -113,7 +159,7 @@ struct SessionHistoryRow<Status> {
113159
session_id: SessionId,
114160
role: Role,
115161
status: Status,
116-
completed_at: Option<u64>,
162+
completed_at: Option<String>,
117163
error_message: Option<String>,
118164
}
119165

@@ -124,13 +170,7 @@ impl<Status: StatusText> fmt::Display for SessionHistoryRow<Status> {
124170
"{:<W_ID$} {:<W_ROLE$} {:<W_DONE$} {:<W_STATUS$}",
125171
self.session_id.to_string(),
126172
self.role.as_str(),
127-
match self.completed_at {
128-
None => "Not Completed".to_string(),
129-
Some(secs) => {
130-
// TODO: human readable time
131-
secs.to_string()
132-
}
133-
},
173+
self.completed_at.as_deref().unwrap_or("Not Completed"),
134174
self.error_message.as_deref().unwrap_or(self.status.status_text())
135175
)
136176
}
@@ -362,7 +402,7 @@ impl AppTrait for App {
362402
role: Role::Sender,
363403
status: SendSession::Closed(SenderSessionOutcome::Failure),
364404
completed_at: None,
365-
error_message: Some(e.to_string()),
405+
error_message: Some(format_session_error(&e.to_string())),
366406
};
367407
send_rows.push(row);
368408
}
@@ -388,7 +428,7 @@ impl AppTrait for App {
388428
role: Role::Receiver,
389429
status: ReceiveSession::Closed(ReceiverSessionOutcome::Failure),
390430
completed_at: None,
391-
error_message: Some(e.to_string()),
431+
error_message: Some(format_session_error(&e.to_string())),
392432
};
393433
recv_rows.push(row);
394434
}
@@ -415,7 +455,7 @@ impl AppTrait for App {
415455
role: Role::Sender,
416456
status: SendSession::Closed(SenderSessionOutcome::Failure),
417457
completed_at: Some(completed_at),
418-
error_message: Some(e.to_string()),
458+
error_message: Some(format_session_error(&e.to_string())),
419459
};
420460
send_rows.push(row);
421461
}
@@ -443,7 +483,7 @@ impl AppTrait for App {
443483
role: Role::Receiver,
444484
status: ReceiveSession::Closed(ReceiverSessionOutcome::Failure),
445485
completed_at: Some(completed_at),
446-
error_message: Some(e.to_string()),
486+
error_message: Some(format_session_error(&e.to_string())),
447487
};
448488
recv_rows.push(row);
449489
}

payjoin-cli/src/db/v2.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -230,14 +230,15 @@ impl Database {
230230
Ok(HpkePublicKey::from_compressed_bytes(&receiver_pubkey).expect("Valid receiver pubkey"))
231231
}
232232

233-
pub(crate) fn get_inactive_send_session_ids(&self) -> Result<Vec<(SessionId, u64)>> {
233+
pub(crate) fn get_inactive_send_session_ids(&self) -> Result<Vec<(SessionId, String)>> {
234234
let conn = self.get_connection()?;
235235
let mut stmt = conn.prepare(
236-
"SELECT session_id, completed_at FROM send_sessions WHERE completed_at IS NOT NULL",
236+
"SELECT session_id, strftime('%Y-%m-%d %H:%M:%S', completed_at, 'unixepoch') \
237+
FROM send_sessions WHERE completed_at IS NOT NULL",
237238
)?;
238239
let session_rows = stmt.query_map([], |row| {
239240
let session_id: i64 = row.get(0)?;
240-
let completed_at: u64 = row.get(1)?;
241+
let completed_at: String = row.get(1)?;
241242
Ok((SessionId(session_id), completed_at))
242243
})?;
243244

@@ -249,14 +250,15 @@ impl Database {
249250
Ok(session_ids)
250251
}
251252

252-
pub(crate) fn get_inactive_recv_session_ids(&self) -> Result<Vec<(SessionId, u64)>> {
253+
pub(crate) fn get_inactive_recv_session_ids(&self) -> Result<Vec<(SessionId, String)>> {
253254
let conn = self.get_connection()?;
254255
let mut stmt = conn.prepare(
255-
"SELECT session_id, completed_at FROM receive_sessions WHERE completed_at IS NOT NULL",
256+
"SELECT session_id, strftime('%Y-%m-%d %H:%M:%S', completed_at, 'unixepoch') \
257+
FROM receive_sessions WHERE completed_at IS NOT NULL",
256258
)?;
257259
let session_rows = stmt.query_map([], |row| {
258260
let session_id: i64 = row.get(0)?;
259-
let completed_at: u64 = row.get(1)?;
261+
let completed_at: String = row.get(1)?;
260262
Ok((SessionId(session_id), completed_at))
261263
})?;
262264

0 commit comments

Comments
 (0)