This is part of the Chronos embedded calendar event library tutorial and API docs.
What Chronos calls “marks” are descriptions of repeating timeline points like “every Sunday at 15h00” or “every first of the month“. From such marks you can generate an infinite set of specific matching occurrences, like “Sunday, January 9th 2016, 15:00:00“, “Sunday, January 16th 2016, 15:00:00“, … for that first example.
Time marks are of little use, on their own, but combined with DateTimes or the calendar Events they offer a flexible way of dealing with all manner of repeating events. The specific marks types currently supported are:
- Chronos::Mark::Daily: something that happens every day at a given time;
- Chronos::Mark::Weekly: happens every week on a given weekday;
- Chronos::Mark::Monthly: once a month on matching month date (e.g. the “4th”); and
- Chronos::Mark::Yearly: once a year on matching month and date (e.g. “July 4th”);
The marks may be used to create repeating events added to calendars:
// weekly class, repeating every monday for one hour
// an Event created using a repeating Mark, and a Span to
// define its duration:
MyCalendar.add(
Chronos::Event(MY_CLASS_ID,
Chronos::Mark::Weekly(Chronos::Weekday::Monday, 10, 30, 0),
Chronos::Span::Hours(1)));
Or used directly on DateTime’s previous/next() methods:
// returns the next matching DateTime object
curDateTime.next(Chronos::Mark::Weekly(Chronos::Weekday::Monday));
The constructors for each type vary in the parameters required. The Daily marks obviously require an hour and minute (and optionally seconds) whereas every other type can be created with or without the actual time (but always with other relevant parameters, e.g. the weekday for Weekly).
When you specify an optional hour/minute/seconds for a mark, you are saying that you consider the time a strict requirement and are hard-set on that specific time. If omitted, it means you care more about the general idea represented by type of mark. As an example:
// my birthday is a yearly event, but time isn't that important
Chronos::Mark::Yearly myBDay(9, 28); // Sept 28th
// for comparison, let's put "time of birth" in there too
Chronos::Mark::Yearly myBDayExact(9, 28, 10, 22, 3); // at exactly 10:22:03
// now the deltas:
Chronos::DateTime justNow(Chronos::DateTime::now());
Chronos::Span::Absolute timeUntilBday = justNow.next(myBDay) - justNow;
Chronos::Span::Absolute timeUntilBdayExact = justNow.next(myBDayExact) - justNow;
/* timeUntilBday, now has the length of time until the ~next time it's Sept 28th
* whereas timeUntilBdayExact includes the exact amount of time, down to the second
* until it's Sept 28th @ 10:22:03. If we ran this, say, on 2015-12-28 17:30 then
* timeUntilBday would be: 275 days, exactly.
* timeUntilBdayExact: 274 days, 16 hours, 52 minutes and 3 seconds.
*/
The thing to remember is that if you specify all params (include a specific time) when creating your Mark, you’ll get a calculation down to the second. If you omit the time, the resulting occurrence will contain whatever the caller time was when applying the previous/next rule (so in this case, it’s the same as the time for “justNow”) which gives nice round results as you’d hope when you don’t care about the specific time.
Sample Code
There’s a functional sample program using time marks, included in the library.
Chronos::Mark classes API
The time mark classes aren’t normally used on their own other than to pass to other objects, like DateTimes or calendar Events.
Constructors
Parameters for constructors always include a specific hour, which is optional in every case but for Daily marks (as described above). The constructors are:
Daily
Chronos::Mark::Daily(Hours hour, Minutes min, Seconds sec=0);
You must always provide the time at which daily marks are set for them to make any sense.
Weekly
Chronos::Mark::Weekly(WeekDay day, Hours hour, Minutes min, Seconds secs=0);
Chronos::Mark::Weekly(WeekDay day);
Here, the weekday is specified using an integer with the same values as those used in the Time library (Sunday is 1, Monday is 2 … Saturday is 7). Specifying the hour and minutes indicates a “strict time” as described above.
Chronos::Mark::Weekly(Chronos::Weekday::Day day, Hours hour, Minutes mins, Seconds secs=0);
Chronos::Mark::Weekly(Chronos::Weekday::Day day);
In this second set, the weekday is specified using an enum that makes code easier to grok (e.g. Chronos::Weekday::Friday).
Monthly
Chronos::Mark::Monthly(Day day);
Chronos::Mark::Monthly(Day day, Hours hour, Minutes min, Seconds secs=0);
Monthly marks specify the month-day (1-31) on which they occur. NOTE: as it stands, the “day” term is applied strictly so marks only match on the exact day number. Thus if it’s January and you set your mark for the 31st, the next one will indeed be on the 31st of Jan. However, the match after that will skip over february and only return a match for the following March 31st. Be aware of this behaviour if using Chronos::Mark::Monthly at/near the end of months.
Yearly
Chronos::Mark::Yearly(Month month, Day day);
Chronos::Mark::Yearly(Month month, Day day,
Hours hour, Minutes mins, Seconds secs=0);
Yearly marks a pretty evident, the month and day follow Time lib’s conventions (1 <= month <= 12 and 1 <= day <= 31).
NOTE: validation is currently minimal, so if you set something crazy like Feb 31, you’re gonna have a bad time.