A systemd timer needs two files, not one. Pick a schedule and this builds the .timer and its matching .service together, plus the commands to install and check them.
A timer beats a cron job when you care about what happened. Timer runs are real systemd units, so they get journal entries, exit codes, dependency ordering, and resource limits. Persistent=true catches up a missed run after the machine was off, which cron cannot do at all. The cost is two files instead of one line. Use RandomizedDelaySec on anything that hits a shared server, so twenty machines do not all start at 03:00:00 exactly.
This systemd timer generator writes both halves of a scheduled task: the .timer that defines when it runs and the .service that defines what runs. It covers OnCalendar expressions, Persistent catch-up, randomised delay, user units, and the systemctl commands to install, enable, and inspect the result. If you are migrating from cron, the crontab generator covers the other side.
systemd separates scheduling from work. The .timer says when, the .service says what. That split is why a timer can reuse an existing service, why you can trigger the same job manually with systemctl start, and why the run shows up in the journal as a normal unit.
It records when the timer last fired. If the machine was off or suspended when a run was due, the job executes shortly after the next boot instead of being skipped. cron has no equivalent, which is why laptop backups scheduled with cron so often never run.
Run systemd-analyze calendar 'your-expression'. It prints the normalised form and the next several trigger times. Always do this before installing, because a syntactically valid expression can easily mean something other than you intended.
Almost always PATH or environment. systemd services run with a minimal environment and no shell profile, so use absolute paths for every binary, and set Environment= or EnvironmentFile= for anything the script expects. Check journalctl -u yourunit.service for the actual error.