code review:
extern crate cron;
extern crate bzip2;
extern crate clap;

use cron::Schedule;
use std::io::{self};
use std::process::Command;
use std::str::FromStr;
use std::time::Duration;
use std::time::SystemTime;
use std::{fs, thread, time::Instant};
use std::error::Error;
use clap::Parser;
use chrono::{Utc, Timelike, Datelike};

const MAX_BACKUP_AGE: Duration = Duration::new(14 * 24 * 60 * 60, 0);
const DAILY_BACKUP_INTERVAL: u64 = 86400;

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
    #[arg(short, long)]
    src_dir: String,

    #[arg(short, long)]
    dest_dir: String,

    #[arg(short, long)]
    run_now: bool,
}

fn compress_directory(src_dir: &str, dest_file: &str) -> Result<(), Box<dyn Error>> {
    let started_at = Instant::now();
    println!("Starting backup: {} -> {}", src_dir, dest_file);
    let output = Command::new("tar")
        .arg("-cjf")
        .arg(dest_file)
        .arg("-C")
        .arg(src_dir)
        .arg(".")
        .output()?;

    if !output.status.success() {
        return Err(Box::new(io::Error::new(
            io::ErrorKind::Other,
            "Failed to create archive",
        )));
    }

    println!("Backup successful: {} -> {}", src_dir, dest_file);
    println!("Took: {:?}", started_at.elapsed());
    Ok(())
}

fn generate_backup_filename(dest_dir: &str) -> String {
    let now = Utc::now();
    format!("{}/backup_{}{:02}{:02}_{:02}{:02}{:02}UTC.tar.bz2",
        dest_dir, now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second())
}

fn clean_previous_backups(dest_dir: &str) -> Result<(), Box<dyn Error>> {
    let entries = fs::read_dir(dest_dir)?;

    let now = SystemTime::now();

    for entry in entries {
        let entry = entry?;
        if entry.path().is_file() {
            let metadata = entry.metadata()?;
            if let Ok(modified) = metadata.modified() {
                if let Ok(age) = now.duration_since(modified) {
                    if age > MAX_BACKUP_AGE {
                        fs::remove_file(entry.path())?;
                    }
                }
            }
        }
    }
    Ok(())
}

fn main() -> Result<(), Box<dyn Error>> {
    let args = Args::parse();

    if args.run_now {
        let dest_file = generate_backup_filename(&args.dest_dir);
        compress_directory(&args.src_dir, &dest_file)?;
        return Ok(());
    }

    let schedule = Schedule::from_str("0 2 * * * *")?; // 02:00 am
    let mut last_time = Instant::now();

    for datetime in schedule.upcoming(Utc) {
        let now = Instant::now();
        let delay = datetime.timestamp() - Utc::now().timestamp();
        if delay > 0 {
            thread::sleep(Duration::from_secs(delay as u64));
        }

        if now.duration_since(last_time).as_secs() >= DAILY_BACKUP_INTERVAL {
            last_time = now;

            let dest_file = generate_backup_filename(&args.dest_dir);
            clean_previous_backups(&args.dest_dir)?;
            match compress_directory(&args.src_dir, &dest_file) {
                Ok(_) => println!("Scheduled backup successful at {}", Utc::now()),
                Err(e) => eprintln!("Backup failed: {}", e),
            }
        }
    }

    Ok(())
}

is this ok to open source?