cron.hpp 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. #pragma once
  2. #include <chrono>
  3. #include <optional>
  4. #include <string>
  5. #include "common/error.hpp"
  6. namespace smartbotic::common {
  7. // A five-field cron expression: minute hour day-of-month month day-of-week.
  8. //
  9. // Supports the forms people actually write - "*", "5", "1,15", "2-6", "*/15",
  10. // "2-8/2" - and Sunday as either 0 or 7. Seconds are not a field: the scheduler
  11. // ticks in minutes, and offering a resolution it cannot honour would be a lie.
  12. //
  13. // This exists because the schedule-trigger node has offered a cron expression
  14. // and a timezone since it was written, and nothing ever read them. A workflow
  15. // set to "0 2 * * 1" ran every N minutes instead, and nothing said so.
  16. class CronSchedule {
  17. public:
  18. // Parses, or explains what is wrong with the expression. The message names
  19. // the offending field, because "invalid cron expression" tells somebody
  20. // staring at five numbers nothing they did not already know.
  21. static Result<CronSchedule> parse(const std::string& expression);
  22. // The first matching time strictly after `after`, in the named timezone.
  23. //
  24. // Returns nothing when the expression can never match - "30 4 31 2 *", the
  25. // 31st of February - rather than searching for ever. A schedule that cannot
  26. // fire is a mistake worth reporting, not a workflow that quietly never runs.
  27. //
  28. // An unknown timezone falls back to UTC and is reported by the caller; DST
  29. // is the standard library's problem, not this class's.
  30. std::optional<std::chrono::system_clock::time_point> nextAfter(
  31. std::chrono::system_clock::time_point after,
  32. const std::string& timezone) const;
  33. const std::string& expression() const { return expression_; }
  34. private:
  35. // One bit per possible value, so matching is a lookup rather than a search.
  36. bool minutes_[60] = {};
  37. bool hours_[24] = {};
  38. bool days_of_month_[32] = {}; // 1-31, index 0 unused
  39. bool months_[13] = {}; // 1-12, index 0 unused
  40. bool days_of_week_[7] = {}; // 0-6, Sunday is 0
  41. // Cron's day rule is an OR when both day fields are restricted, and an AND
  42. // when only one is - the historical behaviour every implementation copies.
  43. bool day_of_month_restricted_ = false;
  44. bool day_of_week_restricted_ = false;
  45. std::string expression_;
  46. };
  47. } // namespace smartbotic::common