diff --git a/CHANGELOG.md b/CHANGELOG.md index 266e38eab..2ba668b86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased ### Changes +- Don't use deprecated `chrono` functions #3798 ### API-Changes diff --git a/src/imex.rs b/src/imex.rs index a2ca6529c..809c7958e 100644 --- a/src/imex.rs +++ b/src/imex.rs @@ -478,7 +478,8 @@ async fn import_backup( /// it can be renamed to dest_path. This guarantees that the backup is complete. fn get_next_backup_path(folder: &Path, backup_time: i64) -> Result<(PathBuf, PathBuf, PathBuf)> { let folder = PathBuf::from(folder); - let stem = chrono::NaiveDateTime::from_timestamp(backup_time, 0) + let stem = chrono::NaiveDateTime::from_timestamp_opt(backup_time, 0) + .context("can't get next backup path")? // Don't change this file name format, in `dc_imex_has_backup` we use string comparison to determine which backup is newer: .format("delta-chat-backup-%Y-%m-%d") .to_string(); diff --git a/src/location.rs b/src/location.rs index c3c8e59ac..f99838d9e 100644 --- a/src/location.rs +++ b/src/location.rs @@ -476,7 +476,8 @@ pub async fn get_kml(context: &Context, chat_id: ChatId) -> Result<(String, u32) fn get_kml_timestamp(utc: i64) -> String { // Returns a string formatted as YYYY-MM-DDTHH:MM:SSZ. The trailing `Z` indicates UTC. - chrono::NaiveDateTime::from_timestamp(utc, 0) + chrono::NaiveDateTime::from_timestamp_opt(utc, 0) + .unwrap() .format("%Y-%m-%dT%H:%M:%SZ") .to_string() } diff --git a/src/mimefactory.rs b/src/mimefactory.rs index d9e6bcedb..5f6f33adb 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -531,7 +531,10 @@ impl<'a> MimeFactory<'a> { .push(Header::new("Subject".into(), encoded_subject)); let date = chrono::Utc - .from_local_datetime(&chrono::NaiveDateTime::from_timestamp(self.timestamp, 0)) + .from_local_datetime( + &chrono::NaiveDateTime::from_timestamp_opt(self.timestamp, 0) + .context("can't convert timestamp to NativeDateTime")?, + ) .unwrap() .to_rfc2822(); headers.unprotected.push(Header::new("Date".into(), date)); @@ -1336,17 +1339,27 @@ async fn build_body_file( // are normally not needed and contain timestamps, running numbers // etc. let filename_to_send: String = match msg.viewtype { - Viewtype::Voice => chrono::Utc - .timestamp(msg.timestamp_sort, 0) - .format(&format!("voice-message_%Y-%m-%d_%H-%M-%S.{}", &suffix)) - .to_string(), + Viewtype::Voice => format!( + "voice-messsage_{}.{}", + chrono::Utc + .timestamp_opt(msg.timestamp_sort, 0) + .single() + .map_or_else( + || "YY-mm-dd_hh:mm:ss".to_string(), + |ts| ts.format("%Y-%m-%d_%H-%M-%S").to_string() + ), + &suffix + ), Viewtype::Image | Viewtype::Gif => format!( - "{}.{}", + "image_{}.{}", if base_name.is_empty() { chrono::Utc - .timestamp(msg.timestamp_sort, 0) - .format("image_%Y-%m-%d_%H-%M-%S") - .to_string() + .timestamp_opt(msg.timestamp_sort, 0) + .single() + .map_or_else( + || "YY-mm-dd_hh:mm:ss".to_string(), + |ts| ts.format("%Y-%m-%d_%H-%M-%S").to_string(), + ) } else { base_name.to_string() }, @@ -1355,8 +1368,12 @@ async fn build_body_file( Viewtype::Video => format!( "video_{}.{}", chrono::Utc - .timestamp(msg.timestamp_sort, 0) - .format("%Y-%m-%d_%H-%M-%S"), + .timestamp_opt(msg.timestamp_sort, 0) + .single() + .map_or_else( + || "YY-mm-dd_hh:mm:ss".to_string(), + |ts| ts.format("%Y-%m-%d_%H-%M-%S").to_string() + ), &suffix ), _ => blob.as_file_name().to_string(), diff --git a/src/provider.rs b/src/provider.rs index aad3e18a9..fb36982a6 100644 --- a/src/provider.rs +++ b/src/provider.rs @@ -184,7 +184,9 @@ pub fn get_provider_by_id(id: &str) -> Option<&'static Provider> { // returns update timestamp in seconds, UTC, compatible for comparison with time() and database times pub fn get_provider_update_timestamp() -> i64 { - NaiveDateTime::new(*PROVIDER_UPDATED, NaiveTime::from_hms(0, 0, 0)).timestamp_millis() / 1_000 + NaiveDateTime::new(*PROVIDER_UPDATED, NaiveTime::from_hms_opt(0, 0, 0).unwrap()) + .timestamp_millis() + / 1_000 } #[cfg(test)] @@ -260,8 +262,8 @@ mod tests { #[test] fn test_get_provider_update_timestamp() { let timestamp_past = NaiveDateTime::new( - NaiveDate::from_ymd(2020, 9, 9), - NaiveTime::from_hms(0, 0, 0), + NaiveDate::from_ymd_opt(2020, 9, 9).unwrap(), + NaiveTime::from_hms_opt(0, 0, 0).unwrap(), ) .timestamp_millis() / 1_000; diff --git a/src/provider/data.rs b/src/provider/data.rs index 87e367fb5..3b8b4b642 100644 --- a/src/provider/data.rs +++ b/src/provider/data.rs @@ -1891,4 +1891,4 @@ pub(crate) static PROVIDER_IDS: Lazy> = }); pub static PROVIDER_UPDATED: Lazy = - Lazy::new(|| chrono::NaiveDate::from_ymd(2022, 7, 5)); + Lazy::new(|| chrono::NaiveDate::from_ymd_opt(2022, 7, 5).unwrap()); diff --git a/src/tools.rs b/src/tools.rs index 8d68e32b1..ed04bc25a 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -112,9 +112,14 @@ pub(crate) fn truncate_by_lines( * date/time tools ******************************************************************************/ +/// Converts Unix time in seconds to a local timestamp string. pub fn timestamp_to_str(wanted: i64) -> String { - let ts = Local.timestamp(wanted, 0); - ts.format("%Y.%m.%d %H:%M:%S").to_string() + if let Some(ts) = Local.timestamp_opt(wanted, 0).single() { + ts.format("%Y.%m.%d %H:%M:%S").to_string() + } else { + // Out of range number of seconds. + "??.??.?? ??:??:??".to_string() + } } pub fn duration_to_str(duration: Duration) -> String { @@ -206,27 +211,31 @@ async fn maybe_warn_on_bad_time(context: &Context, now: i64, known_past_timestam msg.text = Some( stock_str::bad_time_msg_body( context, - &Local - .timestamp(now, 0) - .format("%Y-%m-%d %H:%M:%S") - .to_string(), + &Local.timestamp_opt(now, 0).single().map_or_else( + || "YY-MM-DD hh:mm:ss".to_string(), + |ts| ts.format("%Y-%m-%d %H:%M:%S").to_string(), + ), ) .await, ); - add_device_msg_with_importance( - context, - Some( - format!( - "bad-time-warning-{}", - chrono::NaiveDateTime::from_timestamp(now, 0).format("%Y-%m-%d") // repeat every day - ) - .as_str(), - ), - Some(&mut msg), - true, - ) - .await - .ok(); + if let Some(timestamp) = chrono::NaiveDateTime::from_timestamp_opt(now, 0) { + add_device_msg_with_importance( + context, + Some( + format!( + "bad-time-warning-{}", + timestamp.format("%Y-%m-%d") // repeat every day + ) + .as_str(), + ), + Some(&mut msg), + true, + ) + .await + .ok(); + } else { + warn!(context, "Can't convert current timestamp"); + } return true; } false @@ -236,19 +245,21 @@ async fn maybe_warn_on_outdated(context: &Context, now: i64, approx_compile_time if now > approx_compile_time + DC_OUTDATED_WARNING_DAYS * 24 * 60 * 60 { let mut msg = Message::new(Viewtype::Text); msg.text = Some(stock_str::update_reminder_msg_body(context).await); - add_device_msg( - context, - Some( - format!( - "outdated-warning-{}", - chrono::NaiveDateTime::from_timestamp(now, 0).format("%Y-%m") // repeat every month - ) - .as_str(), - ), - Some(&mut msg), - ) - .await - .ok(); + if let Some(timestamp) = chrono::NaiveDateTime::from_timestamp_opt(now, 0) { + add_device_msg( + context, + Some( + format!( + "outdated-warning-{}", + timestamp.format("%Y-%m") // repeat every month + ) + .as_str(), + ), + Some(&mut msg), + ) + .await + .ok(); + } } } @@ -648,11 +659,14 @@ pub(crate) fn parse_receive_header(header: &str) -> String { if let Ok(date) = dateparse(&header) { // In tests, use the UTC timezone so that the test is reproducible #[cfg(test)] - let date_obj = chrono::Utc.timestamp(date, 0); + let date_obj = chrono::Utc.timestamp_opt(date, 0).single(); #[cfg(not(test))] - let date_obj = Local.timestamp(date, 0); + let date_obj = Local.timestamp_opt(date, 0).single(); - hop_info += &format!("Date: {}", date_obj.to_rfc2822()); + hop_info += &format!( + "Date: {}", + date_obj.map_or_else(|| "?".to_string(), |x| x.to_rfc2822()) + ); }; hop_info @@ -1153,8 +1167,8 @@ DKIM Results: Passed=true, Works=true, Allow_Keychange=true"; let timestamp_now = time(); let timestamp_future = timestamp_now + 60 * 60 * 24 * 7; let timestamp_past = NaiveDateTime::new( - NaiveDate::from_ymd(2020, 9, 1), - NaiveTime::from_hms(0, 0, 0), + NaiveDate::from_ymd_opt(2020, 9, 1).unwrap(), + NaiveTime::from_hms_opt(0, 0, 0).unwrap(), ) .timestamp_millis() / 1_000;