Skip to content

Commit 08330c7

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 08330c7

File tree

2 files changed

+52
-18
lines changed

2 files changed

+52
-18
lines changed

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

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,44 @@ 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!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}", year, month, day, tod / 3600, (tod % 3600) / 60, tod % 60)
120+
}
121+
122+
/// Reformat error messages that contain a raw `Time(Time(timestamp))` debug repr,
123+
/// replacing the opaque token with a human-readable UTC datetime.
124+
fn format_session_error(msg: &str) -> String {
125+
const PREFIX: &str = "Session expired at Time(Time(";
126+
const SUFFIX: &str = "))";
127+
if let Some(rest) = msg.strip_prefix(PREFIX) {
128+
if let Some(num_str) = rest.strip_suffix(SUFFIX) {
129+
if let Ok(secs) = num_str.parse::<u64>() {
130+
return format!("Session expired at {} UTC", format_unix_secs(secs));
131+
}
132+
}
133+
}
134+
msg.to_string()
135+
}
136+
99137
enum Role {
100138
Sender,
101139
Receiver,
@@ -113,7 +151,7 @@ struct SessionHistoryRow<Status> {
113151
session_id: SessionId,
114152
role: Role,
115153
status: Status,
116-
completed_at: Option<u64>,
154+
completed_at: Option<String>,
117155
error_message: Option<String>,
118156
}
119157

@@ -124,13 +162,7 @@ impl<Status: StatusText> fmt::Display for SessionHistoryRow<Status> {
124162
"{:<W_ID$} {:<W_ROLE$} {:<W_DONE$} {:<W_STATUS$}",
125163
self.session_id.to_string(),
126164
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-
},
165+
self.completed_at.as_deref().unwrap_or("Not Completed"),
134166
self.error_message.as_deref().unwrap_or(self.status.status_text())
135167
)
136168
}
@@ -362,7 +394,7 @@ impl AppTrait for App {
362394
role: Role::Sender,
363395
status: SendSession::Closed(SenderSessionOutcome::Failure),
364396
completed_at: None,
365-
error_message: Some(e.to_string()),
397+
error_message: Some(format_session_error(&e.to_string())),
366398
};
367399
send_rows.push(row);
368400
}
@@ -388,7 +420,7 @@ impl AppTrait for App {
388420
role: Role::Receiver,
389421
status: ReceiveSession::Closed(ReceiverSessionOutcome::Failure),
390422
completed_at: None,
391-
error_message: Some(e.to_string()),
423+
error_message: Some(format_session_error(&e.to_string())),
392424
};
393425
recv_rows.push(row);
394426
}
@@ -415,7 +447,7 @@ impl AppTrait for App {
415447
role: Role::Sender,
416448
status: SendSession::Closed(SenderSessionOutcome::Failure),
417449
completed_at: Some(completed_at),
418-
error_message: Some(e.to_string()),
450+
error_message: Some(format_session_error(&e.to_string())),
419451
};
420452
send_rows.push(row);
421453
}
@@ -443,7 +475,7 @@ impl AppTrait for App {
443475
role: Role::Receiver,
444476
status: ReceiveSession::Closed(ReceiverSessionOutcome::Failure),
445477
completed_at: Some(completed_at),
446-
error_message: Some(e.to_string()),
478+
error_message: Some(format_session_error(&e.to_string())),
447479
};
448480
recv_rows.push(row);
449481
}

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)