For this particular requirement I would go with 2 every time. As you say, it doesn't need to be minute-accurate and moreover you know the windows for each operation so it doesn't even need to fire up every 5 mins (eg. not between 23:00 and 05:00 and not between 09:00 and 15:00 (lat/long/TZ depending).
The other option like 3 is not to use cron but to use at instead. The script can calculate the time of next op and submit itself as an at job for that time. I have used Schedule::At for precisely this in the past.