[pve-devel] [PATCH proxmox-backup] avoid chrono dependency, depend on proxmox 0.3.8

Fabian Grünbichler f.gruenbichler at proxmox.com
Tue Sep 15 08:39:58 CEST 2020


some comments/nits inline, looks good to me otherwise.

On September 15, 2020 7:30 am, Dietmar Maurer wrote:
> - remove chrono dependency
> 
> - depend on proxmox 0.3.8
> 
> - remove epoch_now, epoch_now_u64 and epoch_now_f64
> 
> - remove tm_editor (moved to proxmox crate)
> 
> - use new helpers from proxmox 0.3.8
>   * epoch_i64 and epoch_f64
>   * parse_rfc3339
>   * epoch_to_rfc3339_utc
>   * strftime_local
> 
> - BackupDir changes:
>   * store epoch and rfc3339 string instead of DateTime
>   * backup_time_to_string now return a Result
>   * remove unnecessary TryFrom<(BackupGroup, i64)> for BackupDir
> 
> - DynamicIndexHeader: change ctime to i64
> 
> - FixedIndexHeader: change ctime to i64
> ---
>  Cargo.toml                                 |   3 +-
>  examples/download-speed.rs                 |   4 +-
>  examples/upload-speed.rs                   |   2 +-
>  src/api2/admin/datastore.rs                |  12 +--
>  src/api2/node/rrd.rs                       |   3 +-
>  src/api2/node/time.rs                      |  10 +-
>  src/api2/reader.rs                         |   3 +-
>  src/api2/status.rs                         |   3 +-
>  src/auth_helpers.rs                        |   5 +-
>  src/backup/backup_info.rs                  |  77 +++++++------
>  src/backup/catalog.rs                      |   9 +-
>  src/backup/crypt_config.rs                 |   5 +-
>  src/backup/datastore.rs                    |   3 +-
>  src/backup/dynamic_index.rs                |  14 +--
>  src/backup/fixed_index.rs                  |  29 +++--
>  src/backup/key_derivation.rs               |  17 ++-
>  src/backup/manifest.rs                     |   2 +-
>  src/backup/prune.rs                        |  61 +++++------
>  src/bin/proxmox-backup-client.rs           |  62 +++++------
>  src/bin/proxmox-backup-proxy.rs            |  41 +++----
>  src/bin/proxmox_backup_client/benchmark.rs |   3 +-
>  src/bin/proxmox_backup_client/key.rs       |   5 +-
>  src/client/backup_reader.rs                |   5 +-
>  src/client/backup_writer.rs                |   5 +-
>  src/client/http_client.rs                  |   5 +-
>  src/config/jobstate.rs                     |   5 +-
>  src/pxar/tools.rs                          |   8 +-
>  src/rrd/cache.rs                           |   3 +-
>  src/server/upid.rs                         |   3 +-
>  src/server/worker_task.rs                  |  10 +-
>  src/tools.rs                               |  14 ---
>  src/tools/file_logger.rs                   |   6 +-
>  src/tools/format.rs                        |   8 +-
>  src/tools/systemd.rs                       |   1 -
>  src/tools/systemd/time.rs                  |   5 +-
>  src/tools/systemd/tm_editor.rs             | 119 ---------------------
>  src/tools/ticket.rs                        |   8 +-
>  37 files changed, 198 insertions(+), 380 deletions(-)
>  delete mode 100644 src/tools/systemd/tm_editor.rs
> 
> diff --git a/Cargo.toml b/Cargo.toml
> index 0625ffb..9d24242 100644
> --- a/Cargo.toml
> +++ b/Cargo.toml
> @@ -18,7 +18,6 @@ apt-pkg-native = "0.3.1" # custom patched version
>  base64 = "0.12"
>  bitflags = "1.2.1"
>  bytes = "0.5"
> -chrono = "0.4" # Date and time library for Rust
>  crc32fast = "1"
>  endian_trait = { version = "0.6", features = ["arrays"] }
>  anyhow = "1.0"
> @@ -39,7 +38,7 @@ pam-sys = "0.5"
>  percent-encoding = "2.1"
>  pin-utils = "0.1.0"
>  pathpatterns = "0.1.2"
> -proxmox = { version = "0.3.5", features = [ "sortable-macro", "api-macro", "websocket" ] }
> +proxmox = { version = "0.3.8", features = [ "sortable-macro", "api-macro", "websocket" ] }
>  #proxmox = { git = "ssh://gitolite3@proxdev.maurer-it.com/rust/proxmox", version = "0.1.2", features = [ "sortable-macro", "api-macro" ] }
>  #proxmox = { path = "../proxmox/proxmox", features = [ "sortable-macro", "api-macro", "websocket" ] }
>  proxmox-fuse = "0.1.0"
> diff --git a/examples/download-speed.rs b/examples/download-speed.rs
> index 694c55d..0ec6cb6 100644
> --- a/examples/download-speed.rs
> +++ b/examples/download-speed.rs
> @@ -2,8 +2,6 @@ use std::io::Write;
>  
>  use anyhow::{Error};
>  
> -use chrono::{DateTime, Utc};
> -
>  use proxmox_backup::api2::types::Userid;
>  use proxmox_backup::client::{HttpClient, HttpClientOptions, BackupReader};
>  
> @@ -36,7 +34,7 @@ async fn run() -> Result<(), Error> {
>  
>      let client = HttpClient::new(host, username, options)?;
>  
> -    let backup_time = "2019-06-28T10:49:48Z".parse::<DateTime<Utc>>()?;
> +    let backup_time = proxmox::tools::time::parse_rfc3339("2019-06-28T10:49:48Z")?;
>  
>      let client = BackupReader::start(client, None, "store2", "host", "elsa", backup_time, true)
>          .await?;
> diff --git a/examples/upload-speed.rs b/examples/upload-speed.rs
> index d67e9da..896e31e 100644
> --- a/examples/upload-speed.rs
> +++ b/examples/upload-speed.rs
> @@ -16,7 +16,7 @@ async fn upload_speed() -> Result<f64, Error> {
>  
>      let client = HttpClient::new(host, username, options)?;
>  
> -    let backup_time = chrono::Utc::now();
> +    let backup_time = proxmox::tools::time::epoch_i64();
>  
>      let client = BackupWriter::start(client, None, datastore, "host", "speedtest", backup_time, false, true).await?;
>  
> diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs
> index be2796d..af3af0a 100644
> --- a/src/api2/admin/datastore.rs
> +++ b/src/api2/admin/datastore.rs
> @@ -172,7 +172,7 @@ fn list_groups(
>          let result_item = GroupListItem {
>              backup_type: group.backup_type().to_string(),
>              backup_id: group.backup_id().to_string(),
> -            last_backup: info.backup_dir.backup_time().timestamp(),
> +            last_backup: info.backup_dir.backup_time(),
>              backup_count: list.len() as u64,
>              files: info.files.clone(),
>              owner: Some(owner),
> @@ -403,7 +403,7 @@ pub fn list_snapshots (
>          let result_item = SnapshotListItem {
>              backup_type: group.backup_type().to_string(),
>              backup_id: group.backup_id().to_string(),
> -            backup_time: info.backup_dir.backup_time().timestamp(),
> +            backup_time: info.backup_dir.backup_time(),
>              comment,
>              verification,
>              files,
> @@ -673,7 +673,7 @@ fn prune(
>              prune_result.push(json!({
>                  "backup-type": group.backup_type(),
>                  "backup-id": group.backup_id(),
> -                "backup-time": backup_time.timestamp(),
> +                "backup-time": backup_time,
>                  "keep": keep,
>              }));
>          }
> @@ -697,7 +697,7 @@ fn prune(
>              if keep_all { keep = true; }
>  
>              let backup_time = info.backup_dir.backup_time();
> -            let timestamp = BackupDir::backup_time_to_string(backup_time);
> +            let timestamp = info.backup_dir.backup_time_string();
>              let group = info.backup_dir.group();
>  
>  
> @@ -714,7 +714,7 @@ fn prune(
>              prune_result.push(json!({
>                  "backup-type": group.backup_type(),
>                  "backup-id": group.backup_id(),
> -                "backup-time": backup_time.timestamp(),
> +                "backup-time": backup_time,
>                  "keep": keep,
>              }));
>  
> @@ -1097,7 +1097,7 @@ fn upload_backup_log(
>          }
>  
>          println!("Upload backup log to {}/{}/{}/{}/{}", store,
> -                 backup_type, backup_id, BackupDir::backup_time_to_string(backup_dir.backup_time()), file_name);
> +                 backup_type, backup_id, backup_dir.backup_time_string(), file_name);
>  
>          let data = req_body
>              .map_err(Error::from)
> diff --git a/src/api2/node/rrd.rs b/src/api2/node/rrd.rs
> index b857cd3..9988146 100644
> --- a/src/api2/node/rrd.rs
> +++ b/src/api2/node/rrd.rs
> @@ -4,7 +4,6 @@ use serde_json::{Value, json};
>  use proxmox::api::{api, Router};
>  
>  use crate::api2::types::*;
> -use crate::tools::epoch_now_f64;
>  use crate::rrd::{extract_cached_data, RRD_DATA_ENTRIES};
>  
>  pub fn create_value_from_rrd(
> @@ -15,7 +14,7 @@ pub fn create_value_from_rrd(
>  ) -> Result<Value, Error> {
>  
>      let mut result = Vec::new();
> -    let now = epoch_now_f64()?;
> +    let now = proxmox::tools::time::epoch_f64();
>  
>      for name in list {
>          let (start, reso, list) = match extract_cached_data(basedir, name, now, timeframe, cf) {
> diff --git a/src/api2/node/time.rs b/src/api2/node/time.rs
> index a5afa40..3c8e6ab 100644
> --- a/src/api2/node/time.rs
> +++ b/src/api2/node/time.rs
> @@ -1,4 +1,3 @@
> -use chrono::prelude::*;
>  use anyhow::{bail, format_err, Error};
>  use serde_json::{json, Value};
>  
> @@ -57,10 +56,11 @@ fn read_etc_localtime() -> Result<String, Error> {
>  )]
>  /// Read server time and time zone settings.
>  fn get_time(_param: Value) -> Result<Value, Error> {
> -    let datetime = Local::now();
> -    let offset = datetime.offset();
> -    let time = datetime.timestamp();
> -    let localtime = time + (offset.fix().local_minus_utc() as i64);
> +    let time = proxmox::tools::time::epoch_i64();
> +    let tm = proxmox::tools::time::localtime(time)?;
> +    let offset = tm.tm_gmtoff;
> +
> +    let localtime = time + offset;
>  
>      Ok(json!({
>          "timezone": read_etc_localtime()?,
> diff --git a/src/api2/reader.rs b/src/api2/reader.rs
> index 5252d2e..0a72e34 100644
> --- a/src/api2/reader.rs
> +++ b/src/api2/reader.rs
> @@ -1,4 +1,3 @@
> -//use chrono::{Local, TimeZone};
>  use anyhow::{bail, format_err, Error};
>  use futures::*;
>  use hyper::header::{self, HeaderValue, UPGRADE};
> @@ -88,7 +87,7 @@ fn upgrade_to_backup_reader_protocol(
>  
>          //let files = BackupInfo::list_files(&path, &backup_dir)?;
>  
> -        let worker_id = format!("{}_{}_{}_{:08X}", store, backup_type, backup_id, backup_dir.backup_time().timestamp());
> +        let worker_id = format!("{}_{}_{}_{:08X}", store, backup_type, backup_id, backup_dir.backup_time());
>  
>          WorkerTask::spawn("reader", Some(worker_id), userid.clone(), true, move |worker| {
>              let mut env = ReaderEnvironment::new(
> diff --git a/src/api2/status.rs b/src/api2/status.rs
> index 10ae8b3..eb8c43c 100644
> --- a/src/api2/status.rs
> +++ b/src/api2/status.rs
> @@ -23,7 +23,6 @@ use crate::api2::types::{
>  use crate::server;
>  use crate::backup::{DataStore};
>  use crate::config::datastore;
> -use crate::tools::epoch_now_f64;
>  use crate::tools::statistics::{linear_regression};
>  use crate::config::cached_user_info::CachedUserInfo;
>  use crate::config::acl::{
> @@ -110,7 +109,7 @@ fn datastore_status(
>          });
>  
>          let rrd_dir = format!("datastore/{}", store);
> -        let now = epoch_now_f64()?;
> +        let now = proxmox::tools::time::epoch_f64();
>          let rrd_resolution = RRDTimeFrameResolution::Month;
>          let rrd_mode = RRDMode::Average;
>  
> diff --git a/src/auth_helpers.rs b/src/auth_helpers.rs
> index 54c5d4d..fca9015 100644
> --- a/src/auth_helpers.rs
> +++ b/src/auth_helpers.rs
> @@ -11,7 +11,6 @@ use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions};
>  use proxmox::try_block;
>  
>  use crate::api2::types::Userid;
> -use crate::tools::epoch_now_u64;
>  
>  fn compute_csrf_secret_digest(
>      timestamp: i64,
> @@ -32,7 +31,7 @@ pub fn assemble_csrf_prevention_token(
>      userid: &Userid,
>  ) -> String {
>  
> -    let epoch = epoch_now_u64().unwrap() as i64;
> +    let epoch = proxmox::tools::time::epoch_i64();
>  
>      let digest = compute_csrf_secret_digest(epoch, secret, userid);
>  
> @@ -69,7 +68,7 @@ pub fn verify_csrf_prevention_token(
>              bail!("invalid signature.");
>          }
>  
> -        let now = epoch_now_u64()? as i64;
> +        let now = proxmox::tools::time::epoch_i64();
>  
>          let age = now - ttime;
>          if age < min_age {
> diff --git a/src/backup/backup_info.rs b/src/backup/backup_info.rs
> index 023625f..ddc0eb8 100644
> --- a/src/backup/backup_info.rs
> +++ b/src/backup/backup_info.rs
> @@ -2,11 +2,8 @@ use crate::tools;
>  
>  use anyhow::{bail, format_err, Error};
>  use regex::Regex;
> -use std::convert::TryFrom;
>  use std::os::unix::io::RawFd;
>  
> -use chrono::{DateTime, LocalResult, TimeZone, SecondsFormat, Utc};
> -
>  use std::path::{PathBuf, Path};
>  use lazy_static::lazy_static;
>  
> @@ -106,8 +103,8 @@ impl BackupGroup {
>          tools::scandir(libc::AT_FDCWD, &path, &BACKUP_DATE_REGEX, |l2_fd, backup_time, file_type| {
>              if file_type != nix::dir::Type::Directory { return Ok(()); }
>  
> -            let dt = backup_time.parse::<DateTime<Utc>>()?;
> -            let backup_dir = BackupDir::new(self.backup_type.clone(), self.backup_id.clone(), dt.timestamp())?;
> +            let timestamp = proxmox::tools::time::parse_rfc3339(backup_time)?;
> +            let backup_dir = BackupDir::new(self.backup_type.clone(), self.backup_id.clone(), timestamp)?;
>              let files = list_backup_files(l2_fd, backup_time)?;

this maps backup_type, _id &str's and RFC3339 backup_time to a new 
BackupDir and lists its files

>  
>              list.push(BackupInfo { backup_dir, files });
> @@ -117,7 +114,7 @@ impl BackupGroup {
>          Ok(list)
>      }
>  
> -    pub fn last_successful_backup(&self,  base_path: &Path) -> Result<Option<DateTime<Utc>>, Error> {
> +    pub fn last_successful_backup(&self,  base_path: &Path) -> Result<Option<i64>, Error> {
>  
>          let mut last = None;
>  
> @@ -143,11 +140,11 @@ impl BackupGroup {
>                  }
>              }
>  
> -            let dt = backup_time.parse::<DateTime<Utc>>()?;
> -            if let Some(last_dt) = last {
> -                if dt > last_dt { last = Some(dt); }
> +            let timestamp = proxmox::tools::time::parse_rfc3339(backup_time)?;
> +            if let Some(last_timestamp) = last {
> +                if timestamp > last_timestamp { last = Some(timestamp); }
>              } else {
> -                last = Some(dt);
> +                last = Some(timestamp);
>              }
>  
>              Ok(())
> @@ -204,48 +201,51 @@ pub struct BackupDir {
>      /// Backup group
>      group: BackupGroup,
>      /// Backup timestamp
> -    backup_time: DateTime<Utc>,
> +    backup_time: i64,
> +    // backup_time as rfc3339
> +    backup_time_string: String
>  }
>  
>  impl BackupDir {
>  
> -    pub fn new<T, U>(backup_type: T, backup_id: U, timestamp: i64) -> Result<Self, Error>
> +    pub fn new<T, U>(backup_type: T, backup_id: U, backup_time: i64) -> Result<Self, Error>
>      where
>          T: Into<String>,
>          U: Into<String>,
>      {
>          let group = BackupGroup::new(backup_type.into(), backup_id.into());
> -        BackupDir::new_with_group(group, timestamp)
> +        BackupDir::new_with_group(group, backup_time)
>      }
>  
> -    pub fn new_with_group(group: BackupGroup, timestamp: i64) -> Result<Self, Error> {
> -        let backup_time = match Utc.timestamp_opt(timestamp, 0) {
> -            LocalResult::Single(time) => time,
> -            _ => bail!("can't create BackupDir with invalid backup time {}", timestamp),
> -        };
> -
> -        Ok(Self { group, backup_time })
> +    pub fn new_with_group(group: BackupGroup, backup_time: i64) -> Result<Self, Error> {
> +        let backup_time_string = Self::backup_time_to_string(backup_time)?;
> +        Ok(Self { group, backup_time, backup_time_string })
>      }
>  
>      pub fn group(&self) -> &BackupGroup {
>          &self.group
>      }
>  
> -    pub fn backup_time(&self) -> DateTime<Utc> {
> +    pub fn backup_time(&self) -> i64 {
>          self.backup_time
>      }
>  
> +    pub fn backup_time_string(&self) -> &str {
> +        &self.backup_time_string
> +    }
> +
>      pub fn relative_path(&self) ->  PathBuf  {
>  
>          let mut relative_path = self.group.group_path();
>  
> -        relative_path.push(Self::backup_time_to_string(self.backup_time));
> +        relative_path.push(self.backup_time_string.clone());
>  
>          relative_path
>      }
>  
> -    pub fn backup_time_to_string(backup_time: DateTime<Utc>) -> String {
> -        backup_time.to_rfc3339_opts(SecondsFormat::Secs, true)
> +    pub fn backup_time_to_string(backup_time: i64) -> Result<String, Error> {
> +        // fixme: can this fail? (avoid unwrap)
> +        proxmox::tools::time::epoch_to_rfc3339_utc(backup_time)
>      }
>  }
>  
> @@ -260,8 +260,9 @@ impl std::str::FromStr for BackupDir {
>              .ok_or_else(|| format_err!("unable to parse backup snapshot path '{}'", path))?;
>  
>          let group = BackupGroup::new(cap.get(1).unwrap().as_str(), cap.get(2).unwrap().as_str());
> -        let backup_time = cap.get(3).unwrap().as_str().parse::<DateTime<Utc>>()?;
> -        BackupDir::try_from((group, backup_time.timestamp()))
> +        let backup_time_string = cap.get(3).unwrap().as_str().to_owned();
> +        let backup_time = proxmox::tools::time::parse_rfc3339(&backup_time_string)?;
> +        Ok(BackupDir { group, backup_time, backup_time_string })

this creates a new BackupDir from backup_type &str, backup_id &str and 
RFC3339 backup_time (via creating a group, which is not re-used)

>      }
>  }
>  
> @@ -269,16 +270,7 @@ impl std::fmt::Display for BackupDir {
>      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
>          let backup_type = self.group.backup_type();
>          let id = self.group.backup_id();
> -        let time = Self::backup_time_to_string(self.backup_time);
> -        write!(f, "{}/{}/{}", backup_type, id, time)
> -    }
> -}
> -
> -impl TryFrom<(BackupGroup, i64)> for BackupDir {
> -    type Error = Error;
> -
> -    fn try_from((group, timestamp): (BackupGroup, i64)) -> Result<Self, Error> {
> -        BackupDir::new_with_group(group, timestamp)
> +        write!(f, "{}/{}/{}", backup_type, id, self.backup_time_string)
>      }
>  }
>  
> @@ -336,13 +328,18 @@ impl BackupInfo {
>              if file_type != nix::dir::Type::Directory { return Ok(()); }
>              tools::scandir(l0_fd, backup_type, &BACKUP_ID_REGEX, |l1_fd, backup_id, file_type| {
>                  if file_type != nix::dir::Type::Directory { return Ok(()); }
> -                tools::scandir(l1_fd, backup_id, &BACKUP_DATE_REGEX, |l2_fd, backup_time, file_type| {
> +                tools::scandir(l1_fd, backup_id, &BACKUP_DATE_REGEX, |l2_fd, backup_time_string, file_type| {
>                      if file_type != nix::dir::Type::Directory { return Ok(()); }
>  
> -                    let dt = backup_time.parse::<DateTime<Utc>>()?;
> -                    let backup_dir = BackupDir::new(backup_type, backup_id, dt.timestamp())?;
> +                    let backup_time = proxmox::tools::time::parse_rfc3339(backup_time_string)?;
> +
> +                    let backup_dir = BackupDir {
> +                        group: BackupGroup::new(backup_type, backup_id),
> +                        backup_time,
> +                        backup_time_string: backup_time_string.to_owned(),
> +                    };
>  
> -                    let files = list_backup_files(l2_fd, backup_time)?;
> +                    let files = list_backup_files(l2_fd, backup_time_string)?;

this creates a new BackupDir from backup_type, backup_id and RFC3339 
backup_time and lists its backup snapshots.

maybe we want a constructor that allows us to do

let backup_dir: BackupDir = (backup_type, backup_id, backup_time_rfc3339);

to cut down the redundancy here..

>  
>                      list.push(BackupInfo { backup_dir, files });
>  
> diff --git a/src/backup/catalog.rs b/src/backup/catalog.rs
> index 85a3262..d1f519e 100644
> --- a/src/backup/catalog.rs
> +++ b/src/backup/catalog.rs
> @@ -5,7 +5,6 @@ use std::io::{Read, Write, Seek, SeekFrom};
>  use std::os::unix::ffi::OsStrExt;
>  
>  use anyhow::{bail, format_err, Error};
> -use chrono::offset::{TimeZone, Local, LocalResult};
>  
>  use pathpatterns::{MatchList, MatchType};
>  use proxmox::tools::io::ReadExt;
> @@ -533,10 +532,10 @@ impl <R: Read + Seek> CatalogReader<R> {
>                      self.dump_dir(&path, pos)?;
>                  }
>                  CatalogEntryType::File => {
> -                    let mtime_string = match Local.timestamp_opt(mtime as i64, 0) {
> -                        LocalResult::Single(time) => time.to_rfc3339_opts(chrono::SecondsFormat::Secs, false),
> -                        _ => (mtime as i64).to_string(),
> -                    };
> +                    let mut mtime_string = mtime.to_string();
> +                    if let Ok(s) = proxmox::tools::time::strftime_local("%FT%TZ", mtime as i64) {
> +                        mtime_string = s;
> +                    }
>  
>                      println!(
>                          "{} {:?} {} {}",
> diff --git a/src/backup/crypt_config.rs b/src/backup/crypt_config.rs
> index c30fb5f..4be728d 100644
> --- a/src/backup/crypt_config.rs
> +++ b/src/backup/crypt_config.rs
> @@ -10,7 +10,6 @@
>  use std::io::Write;
>  
>  use anyhow::{bail, Error};
> -use chrono::{Local, DateTime};
>  use openssl::hash::MessageDigest;
>  use openssl::pkcs5::pbkdf2_hmac;
>  use openssl::symm::{decrypt_aead, Cipher, Crypter, Mode};
> @@ -216,10 +215,10 @@ impl CryptConfig {
>      pub fn generate_rsa_encoded_key(
>          &self,
>          rsa: openssl::rsa::Rsa<openssl::pkey::Public>,
> -        created: DateTime<Local>,
> +        created: i64,
>      ) -> Result<Vec<u8>, Error> {
>  
> -        let modified = Local::now();
> +        let modified = proxmox::tools::time::epoch_i64();
>          let key_config = super::KeyConfig { kdf: None, created, modified, data: self.enc_key.to_vec() };
>          let data = serde_json::to_string(&key_config)?.as_bytes().to_vec();
>  
> diff --git a/src/backup/datastore.rs b/src/backup/datastore.rs
> index ebe4748..46a5069 100644
> --- a/src/backup/datastore.rs
> +++ b/src/backup/datastore.rs
> @@ -6,7 +6,6 @@ use std::convert::TryFrom;
>  
>  use anyhow::{bail, format_err, Error};
>  use lazy_static::lazy_static;
> -use chrono::{DateTime, Utc};
>  use serde_json::Value;
>  
>  use proxmox::tools::fs::{replace_file, CreateOptions};
> @@ -242,7 +241,7 @@ impl DataStore {
>      /// Returns the time of the last successful backup
>      ///
>      /// Or None if there is no backup in the group (or the group dir does not exist).
> -    pub fn last_successful_backup(&self, backup_group: &BackupGroup) -> Result<Option<DateTime<Utc>>, Error> {
> +    pub fn last_successful_backup(&self, backup_group: &BackupGroup) -> Result<Option<i64>, Error> {
>          let base_path = self.base_path();
>          let mut group_path = base_path.clone();
>          group_path.push(backup_group.group_path());
> diff --git a/src/backup/dynamic_index.rs b/src/backup/dynamic_index.rs
> index f70aa44..1cc4e53 100644
> --- a/src/backup/dynamic_index.rs
> +++ b/src/backup/dynamic_index.rs
> @@ -21,14 +21,14 @@ use super::read_chunk::ReadChunk;
>  use super::Chunker;
>  use super::IndexFile;
>  use super::{DataBlob, DataChunkBuilder};
> -use crate::tools::{self, epoch_now_u64};
> +use crate::tools;
>  
>  /// Header format definition for dynamic index files (`.dixd`)
>  #[repr(C)]
>  pub struct DynamicIndexHeader {
>      pub magic: [u8; 8],
>      pub uuid: [u8; 16],
> -    pub ctime: u64,
> +    pub ctime: i64,
>      /// Sha256 over the index ``SHA256(offset1||digest1||offset2||digest2||...)``
>      pub index_csum: [u8; 32],
>      reserved: [u8; 4032], // overall size is one page (4096 bytes)
> @@ -77,7 +77,7 @@ pub struct DynamicIndexReader {
>      pub size: usize,
>      index: Mmap<DynamicEntry>,
>      pub uuid: [u8; 16],
> -    pub ctime: u64,
> +    pub ctime: i64,
>      pub index_csum: [u8; 32],
>  }
>  
> @@ -107,7 +107,7 @@ impl DynamicIndexReader {
>              bail!("got unknown magic number");
>          }
>  
> -        let ctime = u64::from_le(header.ctime);
> +        let ctime = proxmox::tools::time::epoch_i64();
>  
>          let rawfd = file.as_raw_fd();
>  
> @@ -480,7 +480,7 @@ pub struct DynamicIndexWriter {
>      tmp_filename: PathBuf,
>      csum: Option<openssl::sha::Sha256>,
>      pub uuid: [u8; 16],
> -    pub ctime: u64,
> +    pub ctime: i64,
>  }
>  
>  impl Drop for DynamicIndexWriter {
> @@ -506,13 +506,13 @@ impl DynamicIndexWriter {
>  
>          let mut writer = BufWriter::with_capacity(1024 * 1024, file);
>  
> -        let ctime = epoch_now_u64()?;
> +        let ctime = proxmox::tools::time::epoch_i64();
>  
>          let uuid = Uuid::generate();
>  
>          let mut header = DynamicIndexHeader::zeroed();
>          header.magic = super::DYNAMIC_SIZED_CHUNK_INDEX_1_0;
> -        header.ctime = u64::to_le(ctime);
> +        header.ctime = i64::to_le(ctime);
>          header.uuid = *uuid.as_bytes();
>          // header.index_csum = [0u8; 32];
>          writer.write_all(header.as_bytes())?;
> diff --git a/src/backup/fixed_index.rs b/src/backup/fixed_index.rs
> index 309f450..a2317f0 100644
> --- a/src/backup/fixed_index.rs
> +++ b/src/backup/fixed_index.rs
> @@ -4,9 +4,8 @@ use std::io::{Seek, SeekFrom};
>  use super::chunk_stat::*;
>  use super::chunk_store::*;
>  use super::{IndexFile, ChunkReadInfo};
> -use crate::tools::{self, epoch_now_u64};
> +use crate::tools;
>  
> -use chrono::{Local, LocalResult, TimeZone};
>  use std::fs::File;
>  use std::io::Write;
>  use std::os::unix::io::AsRawFd;
> @@ -23,7 +22,7 @@ use proxmox::tools::Uuid;
>  pub struct FixedIndexHeader {
>      pub magic: [u8; 8],
>      pub uuid: [u8; 16],
> -    pub ctime: u64,
> +    pub ctime: i64,
>      /// Sha256 over the index ``SHA256(digest1||digest2||...)``
>      pub index_csum: [u8; 32],
>      pub size: u64,
> @@ -41,7 +40,7 @@ pub struct FixedIndexReader {
>      index_length: usize,
>      index: *mut u8,
>      pub uuid: [u8; 16],
> -    pub ctime: u64,
> +    pub ctime: i64,
>      pub index_csum: [u8; 32],
>  }
>  
> @@ -82,7 +81,7 @@ impl FixedIndexReader {
>          }
>  
>          let size = u64::from_le(header.size);
> -        let ctime = u64::from_le(header.ctime);
> +        let ctime = i64::from_le(header.ctime);
>          let chunk_size = u64::from_le(header.chunk_size);
>  
>          let index_length = ((size + chunk_size - 1) / chunk_size) as usize;
> @@ -148,13 +147,13 @@ impl FixedIndexReader {
>      pub fn print_info(&self) {
>          println!("Size: {}", self.size);
>          println!("ChunkSize: {}", self.chunk_size);
> -        println!(
> -            "CTime: {}",
> -            match Local.timestamp_opt(self.ctime as i64, 0) {
> -                LocalResult::Single(ctime) => ctime.format("%c").to_string(),
> -                _ => (self.ctime as i64).to_string(),
> -            }
> -        );
> +
> +        let mut ctime_str = self.ctime.to_string();
> +        if let Ok(s) = proxmox::tools::time::strftime_local("%c",self.ctime) {
> +            ctime_str = s;
> +        }
> +
> +        println!("CTime: {}", ctime_str);
>          println!("UUID: {:?}", self.uuid);
>      }
>  }
> @@ -231,7 +230,7 @@ pub struct FixedIndexWriter {
>      index_length: usize,
>      index: *mut u8,
>      pub uuid: [u8; 16],
> -    pub ctime: u64,
> +    pub ctime: i64,
>  }
>  
>  // `index` is mmap()ed which cannot be thread-local so should be sendable
> @@ -274,7 +273,7 @@ impl FixedIndexWriter {
>              panic!("got unexpected header size");
>          }
>  
> -        let ctime = epoch_now_u64()?;
> +        let ctime = proxmox::tools::time::epoch_i64();
>  
>          let uuid = Uuid::generate();
>  
> @@ -282,7 +281,7 @@ impl FixedIndexWriter {
>          let header = unsafe { &mut *(buffer.as_ptr() as *mut FixedIndexHeader) };
>  
>          header.magic = super::FIXED_SIZED_CHUNK_INDEX_1_0;
> -        header.ctime = u64::to_le(ctime);
> +        header.ctime = i64::to_le(ctime);
>          header.size = u64::to_le(size as u64);
>          header.chunk_size = u64::to_le(chunk_size as u64);
>          header.uuid = *uuid.as_bytes();
> diff --git a/src/backup/key_derivation.rs b/src/backup/key_derivation.rs
> index ac27da6..2c80772 100644
> --- a/src/backup/key_derivation.rs
> +++ b/src/backup/key_derivation.rs
> @@ -1,7 +1,6 @@
>  use anyhow::{bail, format_err, Context, Error};
>  
>  use serde::{Deserialize, Serialize};
> -use chrono::{Local, DateTime};
>  
>  use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions};
>  use proxmox::try_block;
> @@ -61,10 +60,10 @@ impl KeyDerivationConfig {
>  #[derive(Deserialize, Serialize, Debug)]
>  pub struct KeyConfig {
>      pub kdf: Option<KeyDerivationConfig>,
> -    #[serde(with = "proxmox::tools::serde::date_time_as_rfc3339")]
> -    pub created: DateTime<Local>,
> -    #[serde(with = "proxmox::tools::serde::date_time_as_rfc3339")]
> -    pub modified: DateTime<Local>,
> +    #[serde(with = "proxmox::tools::serde::epoch_as_rfc3339")]
> +    pub created: i64,
> +    #[serde(with = "proxmox::tools::serde::epoch_as_rfc3339")]
> +    pub modified: i64,
>      #[serde(with = "proxmox::tools::serde::bytes_as_base64")]
>      pub data: Vec<u8>,
>   }
> @@ -136,7 +135,7 @@ pub fn encrypt_key_with_passphrase(
>      enc_data.extend_from_slice(&tag);
>      enc_data.extend_from_slice(&encrypted_key);
>  
> -    let created = Local::now();
> +    let created = proxmox::tools::time::epoch_i64();
>  
>      Ok(KeyConfig {
>          kdf: Some(kdf),
> @@ -149,7 +148,7 @@ pub fn encrypt_key_with_passphrase(
>  pub fn load_and_decrypt_key(
>      path: &std::path::Path,
>      passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
> -) -> Result<([u8;32], DateTime<Local>), Error> {
> +) -> Result<([u8;32], i64), Error> {
>      do_load_and_decrypt_key(path, passphrase)
>          .with_context(|| format!("failed to load decryption key from {:?}", path))
>  }
> @@ -157,14 +156,14 @@ pub fn load_and_decrypt_key(
>  fn do_load_and_decrypt_key(
>      path: &std::path::Path,
>      passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
> -) -> Result<([u8;32], DateTime<Local>), Error> {
> +) -> Result<([u8;32], i64), Error> {
>      decrypt_key(&file_get_contents(&path)?, passphrase)
>  }
>  
>  pub fn decrypt_key(
>      mut keydata: &[u8],
>      passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
> -) -> Result<([u8;32], DateTime<Local>), Error> {
> +) -> Result<([u8;32], i64), Error> {
>      let key_config: KeyConfig = serde_json::from_reader(&mut keydata)?;
>  
>      let raw_data = key_config.data;
> diff --git a/src/backup/manifest.rs b/src/backup/manifest.rs
> index ed5e2a2..609cc99 100644
> --- a/src/backup/manifest.rs
> +++ b/src/backup/manifest.rs
> @@ -103,7 +103,7 @@ impl BackupManifest {
>          Self {
>              backup_type: snapshot.group().backup_type().into(),
>              backup_id: snapshot.group().backup_id().into(),
> -            backup_time: snapshot.backup_time().timestamp(),
> +            backup_time: snapshot.backup_time(),
>              files: Vec::new(),
>              unprotected: json!({}),
>              signature: None,
> diff --git a/src/backup/prune.rs b/src/backup/prune.rs
> index f7a87c5..bc0a75a 100644
> --- a/src/backup/prune.rs
> +++ b/src/backup/prune.rs
> @@ -2,18 +2,16 @@ use anyhow::{Error};
>  use std::collections::{HashMap, HashSet};
>  use std::path::PathBuf;
>  
> -use chrono::{DateTime, Timelike, Datelike, Local};
> -
> -use super::{BackupDir, BackupInfo};
> +use super::BackupInfo;
>  
>  enum PruneMark { Keep, KeepPartial, Remove }
>  
> -fn mark_selections<F: Fn(DateTime<Local>, &BackupInfo) -> String> (
> +fn mark_selections<F: Fn(&BackupInfo) -> Result<String, Error>> (
>      mark: &mut HashMap<PathBuf, PruneMark>,
>      list: &Vec<BackupInfo>,
>      keep: usize,
>      select_id: F,
> -) {
> +) -> Result<(), Error> {
>  
>      let mut include_hash = HashSet::new();
>  
> @@ -21,8 +19,7 @@ fn mark_selections<F: Fn(DateTime<Local>, &BackupInfo) -> String> (
>      for info in list {
>          let backup_id = info.backup_dir.relative_path();
>          if let Some(PruneMark::Keep) = mark.get(&backup_id) {
> -            let local_time = info.backup_dir.backup_time().with_timezone(&Local);
> -            let sel_id: String = select_id(local_time, &info);
> +            let sel_id: String = select_id(&info)?;
>              already_included.insert(sel_id);
>          }
>      }
> @@ -30,8 +27,7 @@ fn mark_selections<F: Fn(DateTime<Local>, &BackupInfo) -> String> (
>      for info in list {
>          let backup_id = info.backup_dir.relative_path();
>          if let Some(_) = mark.get(&backup_id) { continue; }
> -        let local_time = info.backup_dir.backup_time().with_timezone(&Local);
> -        let sel_id: String = select_id(local_time, &info);
> +        let sel_id: String = select_id(&info)?;
>  
>          if already_included.contains(&sel_id) { continue; }
>  
> @@ -43,6 +39,8 @@ fn mark_selections<F: Fn(DateTime<Local>, &BackupInfo) -> String> (
>              mark.insert(backup_id, PruneMark::Remove);
>          }
>      }
> +
> +    Ok(())
>  }
>  
>  fn remove_incomplete_snapshots(
> @@ -182,44 +180,43 @@ pub fn compute_prune_info(
>      remove_incomplete_snapshots(&mut mark, &list);
>  
>      if let Some(keep_last) = options.keep_last {
> -        mark_selections(&mut mark, &list, keep_last as usize, |_local_time, info| {
> -            BackupDir::backup_time_to_string(info.backup_dir.backup_time())
> -        });
> +        mark_selections(&mut mark, &list, keep_last as usize, |info| {
> +            Ok(info.backup_dir.backup_time_string().to_owned())
> +        })?;
>      }
>  
> +    use proxmox::tools::time::strftime_local;
> +
>      if let Some(keep_hourly) = options.keep_hourly {
> -        mark_selections(&mut mark, &list, keep_hourly as usize, |local_time, _info| {
> -            format!("{}/{}/{}/{}", local_time.year(), local_time.month(),
> -                    local_time.day(), local_time.hour())
> -        });
> +        mark_selections(&mut mark, &list, keep_hourly as usize, |info| {
> +            strftime_local("%Y/%m/%d/%H", info.backup_dir.backup_time())
> +        })?;
>      }
>  
>      if let Some(keep_daily) = options.keep_daily {
> -        mark_selections(&mut mark, &list, keep_daily as usize, |local_time, _info| {
> -            format!("{}/{}/{}", local_time.year(), local_time.month(), local_time.day())
> -        });
> +        mark_selections(&mut mark, &list, keep_daily as usize, |info| {
> +            strftime_local("%Y/%m/%d", info.backup_dir.backup_time())
> +        })?;
>      }
>  
>      if let Some(keep_weekly) = options.keep_weekly {
> -        mark_selections(&mut mark, &list, keep_weekly as usize, |local_time, _info| {
> -            let iso_week = local_time.iso_week();
> -            let week = iso_week.week();
> -            // Note: This year number might not match the calendar year number.
> -            let iso_week_year = iso_week.year();
> -            format!("{}/{}", iso_week_year, week)
> -        });
> +        mark_selections(&mut mark, &list, keep_weekly as usize, |info| {
> +            // Note: Use iso-week year/week here. This year number
> +            // might not match the calendar year number.
> +            strftime_local("%G/%V", info.backup_dir.backup_time())
> +        })?;
>      }
>  
>      if let Some(keep_monthly) = options.keep_monthly {
> -        mark_selections(&mut mark, &list, keep_monthly as usize, |local_time, _info| {
> -            format!("{}/{}", local_time.year(), local_time.month())
> -        });
> +        mark_selections(&mut mark, &list, keep_monthly as usize, |info| {
> +            strftime_local("%Y/%m", info.backup_dir.backup_time())
> +        })?;
>      }
>  
>      if let Some(keep_yearly) = options.keep_yearly {
> -        mark_selections(&mut mark, &list, keep_yearly as usize, |local_time, _info| {
> -            format!("{}/{}", local_time.year(), local_time.year())
> -        });
> +        mark_selections(&mut mark, &list, keep_yearly as usize, |info| {
> +            strftime_local("%Y", info.backup_dir.backup_time())
> +        })?;
>      }
>  
>      let prune_info: Vec<(BackupInfo, bool)> = list.into_iter()
> diff --git a/src/bin/proxmox-backup-client.rs b/src/bin/proxmox-backup-client.rs
> index fa13203..c0cd86a 100644
> --- a/src/bin/proxmox-backup-client.rs
> +++ b/src/bin/proxmox-backup-client.rs
> @@ -8,7 +8,6 @@ use std::sync::{Arc, Mutex};
>  use std::task::Context;
>  
>  use anyhow::{bail, format_err, Error};
> -use chrono::{Local, LocalResult, DateTime, Utc, TimeZone};
>  use futures::future::FutureExt;
>  use futures::stream::{StreamExt, TryStreamExt};
>  use serde_json::{json, Value};
> @@ -16,11 +15,20 @@ use tokio::sync::mpsc;
>  use xdg::BaseDirectories;
>  
>  use pathpatterns::{MatchEntry, MatchType, PatternFlag};
> -use proxmox::tools::fs::{file_get_contents, file_get_json, replace_file, CreateOptions, image_size};
> -use proxmox::api::{ApiHandler, ApiMethod, RpcEnvironment};
> -use proxmox::api::schema::*;
> -use proxmox::api::cli::*;
> -use proxmox::api::api;
> +use proxmox::{
> +    tools::{
> +        time::{strftime_local, epoch_i64},
> +        fs::{file_get_contents, file_get_json, replace_file, CreateOptions, image_size},
> +    },
> +    api::{
> +        api,
> +        ApiHandler,
> +        ApiMethod,
> +        RpcEnvironment,
> +        schema::*,
> +        cli::*,
> +    },
> +};
>  use pxar::accessor::{MaybeReady, ReadAt, ReadAtOperation};
>  
>  use proxmox_backup::tools;
> @@ -246,7 +254,7 @@ pub async fn api_datastore_latest_snapshot(
>      client: &HttpClient,
>      store: &str,
>      group: BackupGroup,
> -) -> Result<(String, String, DateTime<Utc>), Error> {
> +) -> Result<(String, String, i64), Error> {
>  
>      let list = api_datastore_list_snapshots(client, store, Some(group.clone())).await?;
>      let mut list: Vec<SnapshotListItem> = serde_json::from_value(list)?;
> @@ -257,11 +265,7 @@ pub async fn api_datastore_latest_snapshot(
>  
>      list.sort_unstable_by(|a, b| b.backup_time.cmp(&a.backup_time));
>  
> -    let backup_time = match Utc.timestamp_opt(list[0].backup_time, 0) {
> -        LocalResult::Single(time) => time,
> -        _ => bail!("last snapshot of backup group {:?} has invalid timestmap {}.",
> -                   group.group_path(), list[0].backup_time),
> -    };
> +    let backup_time = list[0].backup_time;
>  
>      Ok((group.backup_type().to_owned(), group.backup_id().to_owned(), backup_time))
>  }
> @@ -506,7 +510,7 @@ async fn forget_snapshots(param: Value) -> Result<Value, Error> {
>      let result = client.delete(&path, Some(json!({
>          "backup-type": snapshot.group().backup_type(),
>          "backup-id": snapshot.group().backup_id(),
> -        "backup-time": snapshot.backup_time().timestamp(),
> +        "backup-time": snapshot.backup_time(),
>      }))).await?;
>  
>      record_repository(&repo);
> @@ -643,7 +647,7 @@ async fn list_snapshot_files(param: Value) -> Result<Value, Error> {
>      let mut result = client.get(&path, Some(json!({
>          "backup-type": snapshot.group().backup_type(),
>          "backup-id": snapshot.group().backup_id(),
> -        "backup-time": snapshot.backup_time().timestamp(),
> +        "backup-time": snapshot.backup_time(),
>      }))).await?;
>  
>      record_repository(&repo);
> @@ -990,26 +994,18 @@ async fn create_backup(
>          }
>      }
>  
> -    let backup_time = match backup_time_opt {
> -        Some(timestamp) => {
> -            match Utc.timestamp_opt(timestamp, 0) {
> -                LocalResult::Single(time) => time,
> -                _ => bail!("Invalid backup-time parameter: {}", timestamp),
> -            }
> -        },
> -        _ => Utc::now(),
> -    };
> +    let backup_time = backup_time_opt.unwrap_or_else(|| epoch_i64());
>  
>      let client = connect(repo.host(), repo.user())?;
>      record_repository(&repo);
>  
> -    println!("Starting backup: {}/{}/{}", backup_type, backup_id, BackupDir::backup_time_to_string(backup_time));
> +    println!("Starting backup: {}/{}/{}", backup_type, backup_id, BackupDir::backup_time_to_string(backup_time)?);
>  
>      println!("Client name: {}", proxmox::tools::nodename());
>  
> -    let start_time = Local::now();
> +    let start_time = std::time::Instant::now();
>  
> -    println!("Starting protocol: {}", start_time.to_rfc3339_opts(chrono::SecondsFormat::Secs, false));
> +    println!("Starting backup protocol: {}", strftime_local("%c", epoch_i64())?);
>  
>      let (crypt_config, rsa_encrypted_key) = match keydata {
>          None => (None, None),
> @@ -1047,7 +1043,7 @@ async fn create_backup(
>          None
>      };
>  
> -    let snapshot = BackupDir::new(backup_type, backup_id, backup_time.timestamp())?;
> +    let snapshot = BackupDir::new(backup_type, backup_id, backup_time)?;
>      let mut manifest = BackupManifest::new(snapshot);
>  
>      let mut catalog = None;
> @@ -1162,11 +1158,11 @@ async fn create_backup(
>  
>      client.finish().await?;
>  
> -    let end_time = Local::now();
> -    let elapsed = end_time.signed_duration_since(start_time);
> -    println!("Duration: {}", elapsed);
> +    let end_time = std::time::Instant::now();
> +    let elapsed = end_time.duration_since(start_time);
> +    println!("Duration: {:.2}s", elapsed.as_secs_f64());
>  
> -    println!("End Time: {}", end_time.to_rfc3339_opts(chrono::SecondsFormat::Secs, false));
> +    println!("End Time: {}", strftime_local("%c", epoch_i64())?);
>  
>      Ok(Value::Null)
>  }
> @@ -1504,7 +1500,7 @@ async fn upload_log(param: Value) -> Result<Value, Error> {
>      let args = json!({
>          "backup-type": snapshot.group().backup_type(),
>          "backup-id":  snapshot.group().backup_id(),
> -        "backup-time": snapshot.backup_time().timestamp(),
> +        "backup-time": snapshot.backup_time(),
>      });
>  
>      let body = hyper::Body::from(raw_data);
> @@ -1800,7 +1796,7 @@ async fn complete_server_file_name_do(param: &HashMap<String, String>) -> Vec<St
>      let query = tools::json_object_to_query(json!({
>          "backup-type": snapshot.group().backup_type(),
>          "backup-id": snapshot.group().backup_id(),
> -        "backup-time": snapshot.backup_time().timestamp(),
> +        "backup-time": snapshot.backup_time(),
>      })).unwrap();
>  
>      let path = format!("api2/json/admin/datastore/{}/files?{}", repo.store(), query);
> diff --git a/src/bin/proxmox-backup-proxy.rs b/src/bin/proxmox-backup-proxy.rs
> index 5844e63..4e49615 100644
> --- a/src/bin/proxmox-backup-proxy.rs
> +++ b/src/bin/proxmox-backup-proxy.rs
> @@ -13,7 +13,7 @@ use proxmox_backup::api2::types::Userid;
>  use proxmox_backup::configdir;
>  use proxmox_backup::buildcfg;
>  use proxmox_backup::server;
> -use proxmox_backup::tools::{daemon, epoch_now, epoch_now_u64};
> +use proxmox_backup::tools::daemon;
>  use proxmox_backup::server::{ApiConfig, rest::*};
>  use proxmox_backup::auth_helpers::*;
>  use proxmox_backup::tools::disks::{ DiskManage, zfs_pool_stats };
> @@ -144,11 +144,12 @@ fn start_task_scheduler() {
>      tokio::spawn(task.map(|_| ()));
>  }
>  
> -use std::time:: {Instant, Duration};
> +use std::time::{SystemTime, Instant, Duration, UNIX_EPOCH};
>  
>  fn next_minute() -> Result<Instant, Error> {
> -    let epoch_now = epoch_now()?;
> -    let epoch_next = Duration::from_secs((epoch_now.as_secs()/60 + 1)*60);
> +    let now = SystemTime::now();
> +    let epoch_now = now.duration_since(UNIX_EPOCH)?;
> +    let epoch_next = Duration::from_secs((epoch_now.as_secs()/60  + 1)*60);
>      Ok(Instant::now() + epoch_next - epoch_now)

this looks weird.. wouldn't

Ok(Instant::now() + Duration::from_secs(60))

serve the same purpose, since epoch_now and epoch_next are always 60s 
apart?

>  }
>  
> @@ -308,13 +309,8 @@ async fn schedule_datastore_garbage_collection() {
>              }
>          };
>  
> -        let now = match epoch_now_u64() {
> -            Ok(epoch_now) => epoch_now as i64,
> -            Err(err) => {
> -                eprintln!("query system time failed - {}", err);
> -                continue;
> -            }
> -        };
> +        let now = proxmox::tools::time::epoch_i64();
> +
>          if next > now  { continue; }
>  
>          let store2 = store.clone();
> @@ -338,7 +334,7 @@ async fn schedule_datastore_garbage_collection() {
>  async fn schedule_datastore_prune() {
>  
>      use proxmox_backup::backup::{
> -        PruneOptions, DataStore, BackupGroup, BackupDir, compute_prune_info};
> +        PruneOptions, DataStore, BackupGroup, compute_prune_info};
>      use proxmox_backup::server::{WorkerTask};
>      use proxmox_backup::config::datastore::{self, DataStoreConfig};
>      use proxmox_backup::tools::systemd::time::{
> @@ -420,13 +416,8 @@ async fn schedule_datastore_prune() {
>              }
>          };
>  
> -        let now = match epoch_now_u64() {
> -            Ok(epoch_now) => epoch_now as i64,
> -            Err(err) => {
> -                eprintln!("query system time failed - {}", err);
> -                continue;
> -            }
> -        };
> +        let now = proxmox::tools::time::epoch_i64();
> +
>          if next > now  { continue; }
>  
>          let store2 = store.clone();
> @@ -457,8 +448,7 @@ async fn schedule_datastore_prune() {
>                              "{} {}/{}/{}",
>                              if keep { "keep" } else { "remove" },
>                              group.backup_type(), group.backup_id(),
> -                            BackupDir::backup_time_to_string(info.backup_dir.backup_time())));
> -
> +                            info.backup_dir.backup_time_string()));
>                          if !keep {
>                              datastore.remove_backup_dir(&info.backup_dir, true)?;
>                          }
> @@ -529,13 +519,8 @@ async fn schedule_datastore_sync_jobs() {
>              }
>          };
>  
> -        let now = match epoch_now_u64() {
> -            Ok(epoch_now) => epoch_now as i64,
> -            Err(err) => {
> -                eprintln!("query system time failed - {}", err);
> -                continue;
> -            }
> -        };
> +        let now = proxmox::tools::time::epoch_i64();
> +
>          if next > now  { continue; }
>  
>          let job = match Job::new(worker_type, &job_id) {
> diff --git a/src/bin/proxmox_backup_client/benchmark.rs b/src/bin/proxmox_backup_client/benchmark.rs
> index 9830c18..f8f989d 100644
> --- a/src/bin/proxmox_backup_client/benchmark.rs
> +++ b/src/bin/proxmox_backup_client/benchmark.rs
> @@ -3,7 +3,6 @@ use std::sync::Arc;
>  
>  use anyhow::{Error};
>  use serde_json::Value;
> -use chrono::Utc;
>  use serde::Serialize;
>  
>  use proxmox::api::{ApiMethod, RpcEnvironment};
> @@ -212,7 +211,7 @@ async fn test_upload_speed(
>      verbose: bool,
>  ) -> Result<(), Error> {
>  
> -    let backup_time = Utc::now();
> +    let backup_time = proxmox::tools::time::epoch_i64();
>  
>      let client = connect(repo.host(), repo.user())?;
>      record_repository(&repo);
> diff --git a/src/bin/proxmox_backup_client/key.rs b/src/bin/proxmox_backup_client/key.rs
> index 30afa4e..f60687e 100644
> --- a/src/bin/proxmox_backup_client/key.rs
> +++ b/src/bin/proxmox_backup_client/key.rs
> @@ -1,7 +1,6 @@
>  use std::path::PathBuf;
>  
>  use anyhow::{bail, format_err, Error};
> -use chrono::Local;
>  use serde::{Deserialize, Serialize};
>  
>  use proxmox::api::api;
> @@ -112,7 +111,7 @@ fn create(kdf: Option<Kdf>, path: Option<String>) -> Result<(), Error> {
>  
>      match kdf {
>          Kdf::None => {
> -            let created = Local::now();
> +            let created = proxmox::tools::time::epoch_i64();
>  
>              store_key_config(
>                  &path,
> @@ -180,7 +179,7 @@ fn change_passphrase(kdf: Option<Kdf>, path: Option<String>) -> Result<(), Error
>  
>      match kdf {
>          Kdf::None => {
> -            let modified = Local::now();
> +            let modified = proxmox::tools::time::epoch_i64();
>  
>              store_key_config(
>                  &path,
> diff --git a/src/client/backup_reader.rs b/src/client/backup_reader.rs
> index d418571..5f002e2 100644
> --- a/src/client/backup_reader.rs
> +++ b/src/client/backup_reader.rs
> @@ -4,7 +4,6 @@ use std::fs::File;
>  use std::sync::Arc;
>  use std::os::unix::fs::OpenOptionsExt;
>  
> -use chrono::{DateTime, Utc};
>  use futures::future::AbortHandle;
>  use serde_json::{json, Value};
>  
> @@ -41,14 +40,14 @@ impl BackupReader {
>          datastore: &str,
>          backup_type: &str,
>          backup_id: &str,
> -        backup_time: DateTime<Utc>,
> +        backup_time: i64,
>          debug: bool,
>      ) -> Result<Arc<BackupReader>, Error> {
>  
>          let param = json!({
>              "backup-type": backup_type,
>              "backup-id": backup_id,
> -            "backup-time": backup_time.timestamp(),
> +            "backup-time": backup_time,
>              "store": datastore,
>              "debug": debug,
>          });
> diff --git a/src/client/backup_writer.rs b/src/client/backup_writer.rs
> index 64c3cf2..e071911 100644
> --- a/src/client/backup_writer.rs
> +++ b/src/client/backup_writer.rs
> @@ -4,7 +4,6 @@ use std::sync::atomic::{AtomicUsize, Ordering};
>  use std::sync::{Arc, Mutex};
>  
>  use anyhow::{bail, format_err, Error};
> -use chrono::{DateTime, Utc};
>  use futures::*;
>  use futures::stream::Stream;
>  use futures::future::AbortHandle;
> @@ -51,7 +50,7 @@ impl BackupWriter {
>          datastore: &str,
>          backup_type: &str,
>          backup_id: &str,
> -        backup_time: DateTime<Utc>,
> +        backup_time: i64,
>          debug: bool,
>          benchmark: bool
>      ) -> Result<Arc<BackupWriter>, Error> {
> @@ -59,7 +58,7 @@ impl BackupWriter {
>          let param = json!({
>              "backup-type": backup_type,
>              "backup-id": backup_id,
> -            "backup-time": backup_time.timestamp(),
> +            "backup-time": backup_time,
>              "store": datastore,
>              "debug": debug,
>              "benchmark": benchmark
> diff --git a/src/client/http_client.rs b/src/client/http_client.rs
> index ae3704d..3fd9fb7 100644
> --- a/src/client/http_client.rs
> +++ b/src/client/http_client.rs
> @@ -2,7 +2,6 @@ use std::io::Write;
>  use std::task::{Context, Poll};
>  use std::sync::{Arc, Mutex};
>  
> -use chrono::Utc;
>  use anyhow::{bail, format_err, Error};
>  use futures::*;
>  use http::Uri;
> @@ -199,7 +198,7 @@ fn store_ticket_info(prefix: &str, server: &str, username: &str, ticket: &str, t
>  
>      let mut data = file_get_json(&path, Some(json!({})))?;
>  
> -    let now = Utc::now().timestamp();
> +    let now = proxmox::tools::time::epoch_i64();
>  
>      data[server][username] = json!({ "timestamp": now, "ticket": ticket, "token": token});
>  
> @@ -230,7 +229,7 @@ fn load_ticket_info(prefix: &str, server: &str, userid: &Userid) -> Option<(Stri
>      // usually /run/user/<uid>/...
>      let path = base.place_runtime_file("tickets").ok()?;
>      let data = file_get_json(&path, None).ok()?;
> -    let now = Utc::now().timestamp();
> +    let now = proxmox::tools::time::epoch_i64();
>      let ticket_lifetime = tools::ticket::TICKET_LIFETIME - 60;
>      let uinfo = data[server][userid.as_str()].as_object()?;
>      let timestamp = uinfo["timestamp"].as_i64()?;
> diff --git a/src/config/jobstate.rs b/src/config/jobstate.rs
> index d9bffbb..b35973a 100644
> --- a/src/config/jobstate.rs
> +++ b/src/config/jobstate.rs
> @@ -48,7 +48,6 @@ use proxmox::tools::fs::{
>  use serde::{Deserialize, Serialize};
>  
>  use crate::server::{upid_read_status, worker_is_active_local, TaskState, UPID};
> -use crate::tools::epoch_now_u64;
>  
>  #[serde(rename_all = "kebab-case")]
>  #[derive(Serialize, Deserialize)]
> @@ -178,7 +177,7 @@ impl JobState {
>              }
>          } else {
>              Ok(JobState::Created {
> -                time: epoch_now_u64()? as i64 - 30,
> +                time: proxmox::tools::time::epoch_i64() - 30,
>              })
>          }
>      }
> @@ -199,7 +198,7 @@ impl Job {
>              jobtype: jobtype.to_string(),
>              jobname: jobname.to_string(),
>              state: JobState::Created {
> -                time: epoch_now_u64()? as i64,
> +                time: proxmox::tools::time::epoch_i64(),
>              },
>              _lock,
>          })
> diff --git a/src/pxar/tools.rs b/src/pxar/tools.rs
> index 2eed0f8..3fd0fc0 100644
> --- a/src/pxar/tools.rs
> +++ b/src/pxar/tools.rs
> @@ -115,12 +115,10 @@ fn mode_string(entry: &Entry) -> String {
>  }
>  
>  fn format_mtime(mtime: &StatxTimestamp) -> String {
> -    use chrono::offset::TimeZone;
> -
> -    match chrono::Local.timestamp_opt(mtime.secs, mtime.nanos) {
> -        chrono::LocalResult::Single(mtime) => mtime.format("%Y-%m-%d %H:%M:%S").to_string(),
> -        _ => format!("{}.{}", mtime.secs, mtime.nanos),
> +    if let Ok(s) = proxmox::tools::time::strftime_local("%Y-%m-%d %H:%M:%S", mtime.secs) {
> +        return s;
>      }
> +    format!("{}.{}", mtime.secs, mtime.nanos)

+    proxmox::tools::time::strftime_local("%Y-%m-%d %H:%M:%S", mtime.secs)
+        .unwrap_or_else(|_| format!("{}.{}", mtime.secs, mtime.nanos))

seems more idiomatic rust to me (and it's shorter ;))

>  }
>  
>  pub fn format_single_line_entry(entry: &Entry) -> String {
> diff --git a/src/rrd/cache.rs b/src/rrd/cache.rs
> index f08d6c9..e5e3fe0 100644
> --- a/src/rrd/cache.rs
> +++ b/src/rrd/cache.rs
> @@ -8,7 +8,6 @@ use lazy_static::lazy_static;
>  use proxmox::tools::fs::{create_path, CreateOptions};
>  
>  use crate::api2::types::{RRDMode, RRDTimeFrameResolution};
> -use crate::tools::epoch_now_f64;
>  
>  use super::*;
>  
> @@ -42,7 +41,7 @@ pub fn update_value(rel_path: &str, value: f64, dst: DST, save: bool) -> Result<
>      std::fs::create_dir_all(path.parent().unwrap())?;
>  
>      let mut map = RRD_CACHE.write().unwrap();
> -    let now = epoch_now_f64()?;
> +    let now = proxmox::tools::time::epoch_f64();
>  
>      if let Some(rrd) = map.get_mut(rel_path) {
>          rrd.update(now, value);
> diff --git a/src/server/upid.rs b/src/server/upid.rs
> index 9fc5085..f829eef 100644
> --- a/src/server/upid.rs
> +++ b/src/server/upid.rs
> @@ -1,7 +1,6 @@
>  use std::sync::atomic::{AtomicUsize, Ordering};
>  
>  use anyhow::{bail, Error};
> -use chrono::Local;
>  
>  use proxmox::api::schema::{ApiStringFormat, Schema, StringSchema};
>  use proxmox::const_regex;
> @@ -89,7 +88,7 @@ impl UPID {
>          Ok(UPID {
>              pid,
>              pstart: procfs::PidStat::read_from_pid(nix::unistd::Pid::from_raw(pid))?.starttime,
> -            starttime: Local::now().timestamp(),
> +            starttime: proxmox::tools::time::epoch_i64(),
>              task_id,
>              worker_type: worker_type.to_owned(),
>              worker_id,
> diff --git a/src/server/worker_task.rs b/src/server/worker_task.rs
> index 28e62ba..0f7e291 100644
> --- a/src/server/worker_task.rs
> +++ b/src/server/worker_task.rs
> @@ -5,7 +5,6 @@ use std::panic::UnwindSafe;
>  use std::sync::atomic::{AtomicBool, Ordering};
>  use std::sync::{Arc, Mutex};
>  
> -use chrono::Local;
>  use anyhow::{bail, format_err, Error};
>  use futures::*;
>  use lazy_static::lazy_static;
> @@ -231,9 +230,7 @@ pub fn upid_read_status(upid: &UPID) -> Result<TaskState, Error> {
>  
>      let mut iter = last_line.splitn(2, ": ");
>      if let Some(time_str) = iter.next() {
> -        if let Ok(endtime) = chrono::DateTime::parse_from_rfc3339(time_str) {
> -            let endtime = endtime.timestamp();
> -
> +        if let Ok(endtime) = proxmox::tools::time::parse_rfc3339(time_str) {
>              if let Some(rest) = iter.next().and_then(|rest| rest.strip_prefix("TASK ")) {
>                  if let Ok(state) = TaskState::from_endtime_and_message(endtime, rest) {
>                      status = state;
> @@ -364,8 +361,9 @@ fn update_active_workers(new_upid: Option<&UPID>) -> Result<Vec<TaskListInfo>, E
>                      },
>                      None => {
>                          println!("Detected stopped UPID {}", upid_str);
> +                        let now = proxmox::tools::time::epoch_i64();
>                          let status = upid_read_status(&upid)
> -                            .unwrap_or_else(|_| TaskState::Unknown { endtime: Local::now().timestamp() });
> +                            .unwrap_or_else(|_| TaskState::Unknown { endtime: now });
>                          finish_list.push(TaskListInfo {
>                              upid, upid_str, state: Some(status)
>                          });
> @@ -589,7 +587,7 @@ impl WorkerTask {
>      pub fn create_state(&self, result: &Result<(), Error>) -> TaskState {
>          let warn_count = self.data.lock().unwrap().warn_count;
>  
> -        let endtime = Local::now().timestamp();
> +        let endtime = proxmox::tools::time::epoch_i64();
>  
>          if let Err(err) = result {
>              TaskState::Error { message: err.to_string(), endtime }
> diff --git a/src/tools.rs b/src/tools.rs
> index a3fbe00..8080777 100644
> --- a/src/tools.rs
> +++ b/src/tools.rs
> @@ -8,8 +8,6 @@ use std::fs::File;
>  use std::io::{self, BufRead, ErrorKind, Read};
>  use std::os::unix::io::RawFd;
>  use std::path::Path;
> -use std::time::Duration;
> -use std::time::{SystemTime, SystemTimeError, UNIX_EPOCH};
>  
>  use anyhow::{bail, format_err, Error};
>  use serde_json::Value;
> @@ -547,18 +545,6 @@ pub fn file_get_non_comment_lines<P: AsRef<Path>>(
>      }))
>  }
>  
> -pub fn epoch_now() -> Result<Duration, SystemTimeError> {
> -    SystemTime::now().duration_since(UNIX_EPOCH)
> -}
> -
> -pub fn epoch_now_f64() -> Result<f64, SystemTimeError> {
> -    Ok(epoch_now()?.as_secs_f64())
> -}
> -
> -pub fn epoch_now_u64() -> Result<u64, SystemTimeError> {
> -    Ok(epoch_now()?.as_secs())
> -}
> -
>  pub fn setup_safe_path_env() {
>      std::env::set_var("PATH", "/sbin:/bin:/usr/sbin:/usr/bin");
>      // Make %ENV safer - as suggested by https://perldoc.perl.org/perlsec.html
> diff --git a/src/tools/file_logger.rs b/src/tools/file_logger.rs
> index c0fcab7..c41994f 100644
> --- a/src/tools/file_logger.rs
> +++ b/src/tools/file_logger.rs
> @@ -1,5 +1,4 @@
>  use anyhow::{Error};
> -use chrono::Local;
>  use std::io::Write;
>  
>  /// Log messages with timestamps into files
> @@ -56,7 +55,10 @@ impl FileLogger {
>              stdout.write_all(b"\n").unwrap();
>          }
>  
> -        let line = format!("{}: {}\n", Local::now().to_rfc3339(), msg);
> +        let now = proxmox::tools::time::epoch_i64();
> +        let rfc3339 = proxmox::tools::time::epoch_to_rfc3339(now).unwrap();
> +
> +        let line = format!("{}: {}\n", rfc3339, msg);
>          self.file.write_all(line.as_bytes()).unwrap();
>      }
>  }
> diff --git a/src/tools/format.rs b/src/tools/format.rs
> index 1e593ff..9894668 100644
> --- a/src/tools/format.rs
> +++ b/src/tools/format.rs
> @@ -1,6 +1,5 @@
>  use anyhow::{Error};
>  use serde_json::Value;
> -use chrono::{Local, TimeZone, LocalResult};
>  
>  pub fn strip_server_file_expenstion(name: &str) -> String {
>  
> @@ -25,9 +24,10 @@ pub fn render_epoch(value: &Value, _record: &Value) -> Result<String, Error> {
>      if value.is_null() { return Ok(String::new()); }
>      let text = match value.as_i64() {
>          Some(epoch) => {
> -            match Local.timestamp_opt(epoch, 0) {
> -                LocalResult::Single(epoch) => epoch.format("%c").to_string(),
> -                _ => epoch.to_string(),
> +            if let Ok(epoch_string) = proxmox::tools::time::strftime_local("%c", epoch as i64) {
> +                epoch_string
> +            } else {
> +                epoch.to_string()
>              }
>          },
>          None => {
> diff --git a/src/tools/systemd.rs b/src/tools/systemd.rs
> index 9a6439d..8f0a66d 100644
> --- a/src/tools/systemd.rs
> +++ b/src/tools/systemd.rs
> @@ -2,7 +2,6 @@ pub mod types;
>  pub mod config;
>  
>  mod parse_time;
> -pub mod tm_editor;
>  pub mod time;
>  
>  use anyhow::{bail, Error};
> diff --git a/src/tools/systemd/time.rs b/src/tools/systemd/time.rs
> index f76731f..1961d4b 100644
> --- a/src/tools/systemd/time.rs
> +++ b/src/tools/systemd/time.rs
> @@ -3,8 +3,9 @@ use std::convert::TryInto;
>  use anyhow::Error;
>  use bitflags::bitflags;
>  
> +use proxmox::tools::time::TmEditor;
> +
>  pub use super::parse_time::*;
> -use super::tm_editor::*;
>  
>  bitflags!{
>      #[derive(Default)]
> @@ -161,7 +162,7 @@ pub fn compute_next_event(
>  
>      let all_days = event.days.is_empty() || event.days.is_all();
>  
> -    let mut t = TmEditor::new(last, utc)?;
> +    let mut t = TmEditor::with_epoch(last, utc)?;
>  
>      let mut count = 0;
>  
> diff --git a/src/tools/systemd/tm_editor.rs b/src/tools/systemd/tm_editor.rs
> deleted file mode 100644
> index 770bb28..0000000
> --- a/src/tools/systemd/tm_editor.rs
> +++ /dev/null
> @@ -1,119 +0,0 @@
> -use anyhow::Error;
> -
> -use proxmox::tools::time::*;
> -
> -pub struct TmEditor {
> -    utc: bool,
> -    t: libc::tm,
> -}
> -
> -impl TmEditor {
> -
> -    pub fn new(epoch: i64, utc: bool) -> Result<Self, Error> {
> -        let t = if utc { gmtime(epoch)? } else { localtime(epoch)? };
> -        Ok(Self { utc, t })
> -    }
> -
> -    pub fn into_epoch(mut self) -> Result<i64, Error> {
> -        let epoch = if self.utc { timegm(&mut self.t)? } else { timelocal(&mut self.t)? };
> -        Ok(epoch)
> -    }
> -
> -    /// increases the year by 'years' and resets all smaller fields to their minimum
> -    pub fn add_years(&mut self, years: libc::c_int) -> Result<(), Error> {
> -        if years == 0 { return Ok(()); }
> -        self.t.tm_mon = 0;
> -        self.t.tm_mday = 1;
> -        self.t.tm_hour = 0;
> -        self.t.tm_min = 0;
> -        self.t.tm_sec = 0;
> -        self.t.tm_year += years;
> -        self.normalize_time()
> -    }
> -
> -    /// increases the month by 'months' and resets all smaller fields to their minimum
> -    pub fn add_months(&mut self, months: libc::c_int) -> Result<(), Error> {
> -        if months == 0 { return Ok(()); }
> -        self.t.tm_mday = 1;
> -        self.t.tm_hour = 0;
> -        self.t.tm_min = 0;
> -        self.t.tm_sec = 0;
> -        self.t.tm_mon += months;
> -        self.normalize_time()
> -    }
> -
> -    /// increases the day by 'days' and resets all smaller fields to their minimum
> -    pub fn add_days(&mut self, days: libc::c_int) -> Result<(), Error> {
> -        if days == 0 { return Ok(()); }
> -        self.t.tm_hour = 0;
> -        self.t.tm_min = 0;
> -        self.t.tm_sec = 0;
> -        self.t.tm_mday += days;
> -        self.normalize_time()
> -    }
> -
> -    pub fn year(&self) -> libc::c_int { self.t.tm_year + 1900 } // see man mktime
> -    pub fn month(&self) -> libc::c_int { self.t.tm_mon + 1 }
> -    pub fn day(&self) -> libc::c_int { self.t.tm_mday }
> -    pub fn hour(&self) -> libc::c_int { self.t.tm_hour }
> -    pub fn min(&self) -> libc::c_int { self.t.tm_min }
> -    pub fn sec(&self) -> libc::c_int { self.t.tm_sec }
> -
> -    // Note: tm_wday (0-6, Sunday = 0) => convert to Sunday = 6
> -    pub fn day_num(&self) -> libc::c_int {
> -        (self.t.tm_wday + 6) % 7
> -    }
> -
> -    pub fn set_time(&mut self, hour: libc::c_int, min: libc::c_int, sec: libc::c_int) -> Result<(), Error> {
> -        self.t.tm_hour = hour;
> -        self.t.tm_min = min;
> -        self.t.tm_sec = sec;
> -        self.normalize_time()
> -    }
> -
> -    pub fn set_min_sec(&mut self, min: libc::c_int, sec: libc::c_int) -> Result<(), Error> {
> -        self.t.tm_min = min;
> -        self.t.tm_sec = sec;
> -        self.normalize_time()
> -    }
> -
> -    fn normalize_time(&mut self) -> Result<(), Error> {
> -        // libc normalizes it for us
> -        if self.utc {
> -            timegm(&mut self.t)?;
> -        } else {
> -            timelocal(&mut self.t)?;
> -        }
> -        Ok(())
> -    }
> -
> -    pub fn set_sec(&mut self, v: libc::c_int) -> Result<(), Error> {
> -        self.t.tm_sec = v;
> -        self.normalize_time()
> -    }
> -
> -    pub fn set_min(&mut self, v: libc::c_int) -> Result<(), Error> {
> -        self.t.tm_min = v;
> -        self.normalize_time()
> -    }
> -
> -    pub fn set_hour(&mut self, v: libc::c_int) -> Result<(), Error> {
> -        self.t.tm_hour = v;
> -        self.normalize_time()
> -    }
> -
> -    pub fn set_mday(&mut self, v: libc::c_int) -> Result<(), Error> {
> -        self.t.tm_mday = v;
> -        self.normalize_time()
> -    }
> -
> -    pub fn set_mon(&mut self, v: libc::c_int) -> Result<(), Error> {
> -        self.t.tm_mon = v - 1;
> -        self.normalize_time()
> -    }
> -
> -    pub fn set_year(&mut self, v: libc::c_int) -> Result<(), Error> {
> -        self.t.tm_year = v - 1900;
> -        self.normalize_time()
> -    }
> -}
> diff --git a/src/tools/ticket.rs b/src/tools/ticket.rs
> index bada0ef..4462ee3 100644
> --- a/src/tools/ticket.rs
> +++ b/src/tools/ticket.rs
> @@ -11,7 +11,6 @@ use openssl::sign::{Signer, Verifier};
>  use percent_encoding::{percent_decode_str, percent_encode, AsciiSet};
>  
>  use crate::api2::types::Userid;
> -use crate::tools::epoch_now_u64;
>  
>  pub const TICKET_LIFETIME: i64 = 3600 * 2; // 2 hours
>  
> @@ -69,7 +68,7 @@ where
>          Ok(Self {
>              prefix: Cow::Borrowed(prefix),
>              data: data.to_string(),
> -            time: epoch_now_u64()? as i64,
> +            time: proxmox::tools::time::epoch_i64(),
>              signature: None,
>              _type_marker: PhantomData,
>          })
> @@ -174,7 +173,7 @@ where
>              None => bail!("invalid ticket without signature"),
>          };
>  
> -        let age = epoch_now_u64()? as i64 - self.time;
> +        let age = proxmox::tools::time::epoch_i64() - self.time;
>          if age < time_frame.start {
>              bail!("invalid ticket - timestamp newer than expected");
>          }
> @@ -272,7 +271,6 @@ mod test {
>  
>      use super::Ticket;
>      use crate::api2::types::Userid;
> -    use crate::tools::epoch_now_u64;
>  
>      fn simple_test<F>(key: &PKey<Private>, aad: Option<&str>, modify: F)
>      where
> @@ -314,7 +312,7 @@ mod test {
>              false
>          });
>          simple_test(&key, None, |t| {
> -            t.change_time(epoch_now_u64().unwrap() as i64 + 0x1000_0000);
> +            t.change_time(proxmox::tools::time::epoch_i64() + 0x1000_0000);
>              false
>          });
>      }
> -- 
> 2.20.1
> 
> 
> _______________________________________________
> pve-devel mailing list
> pve-devel at lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
> 
> 
> 





More information about the pve-devel mailing list