DateTime and Spans

This is part of the Chronos embedded calendar event library tutorial and API docs.

The Chronos::DateTime and a set of Chronos::Span classes cooperate together to make single points in time easy to work with. The DateTimes are, evidently, specific calendar dates at specific clock times, like “January 1st, 2017 at 06h00“. The time spans are simply lengths of time, unbound to any specific moment, like “10 minutes“.


You may add a DateTime to a specific Span in order to create a new DateTime, further in the future:

	Chronos::DateTime later = Chronos::DateTime::now() + Chronos::Span::Minutes(10);

and subtraction works the same:

	Chronos::DateTime yesterday = Chronos::DateTime::now() - Chronos::Span::Days(1);

All the obvious arithmetic operations work, like

	someDateTime += Chronos::Span::Hours(3);

but stuff that doesn’t make sense, like subtracting a DateTime from a span, or adding two DateTimes together won’t compile.

DateTimes *can* be subtracted from one another, to figure out the difference between two DateTimes. The result will be an absolute time span:

	Chronos::Span::Absolute diff = later - yesterday;

An important caveat, and the reason the class is named “Absolute” to remind you, is that spans are never negative. They are lengths of time, and a subtraction will always result in a positive number of days/hours/minutes/seconds.


If you care about which DateTime is later or earlier, all the comparison operators will work as expected:

	if (later > yesterday)
		// ok, that makes sense... do stuff

All the standard operations are supported <, <=, >, >=, == and !=. Additionally, DateTimes have sameTimeAs()/sameDateAs() methods that are equivalent to “==” on only a time or date portion of the DateTime, respectively.

The normal comparison ops also work between time spans. Spans may also be added/subtracted and compared to each other.

	if (Chronos::Span::Days(1) != Chronos::Span::Hours(24))
		// will never happen...
		die("aaaagh! time is broken!");


DateTime have attribute accessor methods for every datum of interest (year(), month(), weekday(), day() etc, see API below). There are also setters for each attribute (setYear(), etc) and a few additional utility methods such as

	yesterday.setToStartOfDay(); // sets time to 00:00:00 in one call

The time spans also have accessors for days(), hours(), minutes() and seconds(). This isn’t too useful when you’ve constructed you’re own time span, e.g. Chronos::Span::Hours(3), but can be of use when getting a delta between DateTimes:

	Chronos::DateTime myNextBirthDay = Chronos::DateTime::now().next(Chronos::Mark::Yearly(9, 28)); 
	(myNextBirthDay - Chronos::DateTime::now()).days();

Spans have no setters, but they’re easy enough to create or modify using arithmetic that I’ve yet to find a need for them.


There are additional DateTime functions that can be interesting, such as the previous()/next() to get the prev/next occurrence of some time mark, a set of *Bounds() methods to get the beginning and end datetimes of some type of standard time unit (days, weeks, months or years), and other stuff like isWeekend()/isWeekday() that is described below.

Sample code

There’s a fully functional sample of working with DateTimes and Spans included in the library.

Chronos::DateTime API

Static (class) methods, called as Chronos::DateTime::method(…).

	static void setTime(Year year, Month month, Day day, Hours hours, Minutes minutes, Seconds secs=0);

Tell the external time-keeper to set the current date-time, e.g. the Arduino Time library.

	static DateTime now();

Returns a DateTime object representing the current instant, according to the external time keeper.

	static DateTime::Bounds dayBounds(Year year, Month month, Day day);
	static DateTime::Bounds dayBounds(const DateTime & fromDt);
	static DateTime::Bounds weekBounds(Year year, Month month, Day day);
	static DateTime::Bounds weekBounds(const DateTime & fromDt);
	static DateTime::Bounds monthBounds(Year year, Month month);
	static DateTime::Bounds monthBounds(const DateTime & fromDt);
	static DateTime::Bounds yearBounds(Year year);
	static DateTime::Bounds yearBounds(const DateTime & fromDt);

all *Bounds() methods return a DateTime::Bounds which contain the farthest start and finish DateTime objects which are still within the specified boundary: [start, finish] … note: inclusive

Any datetime that is outside the bounds will therefore be:

 	(outDate < bounds.start) || (bounds.finish < outDate)

The caveat here is that, owing to the seconds-granularity of our base clock, the delta of the bounds:

 	(bounds.finish - bounds.start) (or, equivalently, bounds.span())

will be a "second short" of intuitive value, e.g. 23:59:59 for a dayBounds(), 6 days, 23h 59m and 59 seconds for a weekBounds() etc. If you really want it to account for the lost second, you can use

bounds.spanRounded() -- a Span::Absolute object that will give 7 days, 0 hours, 0 mins, 0 secs in the week example above.

	static DateTime previous(Chronos::Weekday::Day aDay, const DateTime & fromDt);
	static DateTime previous(Chronos::Named::Month::Month month, const DateTime & fromDt);
	static DateTime next(Chronos::Weekday::Day aDay, const DateTime & fromDt);
	static DateTime next(Chronos::Named::Month::Month month, const DateTime & fromDt);

previous/next weekday or month.
Returns the previous/next ... weekday or month, a DateTime object set to the start of the relevant day. May be statically called with the relevant day/month and a DateTime object.

	static DateTime endOfTime();

Mostly for internal use, but returns a DateTime that is a the limit of what we can represent with the library... normal dates should all be < Chronos::DateTime::endOfTime()

DateTime Constructors

	DateTime(Year year, Month month, Day day, Hours hours=0, Minutes minutes=0, Seconds secs=0);
	DateTime(uint32_t atEpochSecs);


	Seconds 	second() const;
	Minutes 	minute() const;
	Hours 		hour() const;
	Day		day() const; // day of month (e.g. 22)
	WeekDay		weekday() const; // day of week (Sunday is 1)
	Month		month() const; // (month starting at 1 for january)
	Year		year() const; // (4-digit YYYY)

Getters for DateTime attributes.

	void setSecond(Seconds s);
	void setMinute(Minutes m);
	void setHour(Hours h);
	void setDay(Day d);
	void setMonth(Month m);
	void setYear(Year y);

Setters for attributes previously described.

	void setToStartOfDay();
	void setToEndOfDay();

Set the calling object to the earliest (00:00:00) or latest (23:59:59) time that is still within the current day, respectively.

	DateTime startOfDay() const;
	DateTime endOfDay() const;

Similar to setToStartOfDay/setToEndOfDay but instead of modifying the caller, returns a fresh DateTime object set to the same date but with appropriate time.

	DateTime previous(Chronos::Named::Month::Month month) const;
	DateTime previous(Chronos::Weekday::Day aDay) const;
	DateTime next(Chronos::Weekday::Day aDay) const;
	DateTime next(Chronos::Named::Month::Month month) const;

Member method for the previous/next described above, returning DateTimes relative to the calling object.

	DateTime next(const Mark::Event & mark) const ;

@param mark: a Chronos::Mark::Event which specifies some time mark (point)
Returns a DateTime object corresponding to next occurrence of mark.


 Chronos::DateTime::now().next(Chronos::Mark::Monthly(13, 18, 30, 0))

would return a DateTime corresponding to the next time it will be the 13th of the month at 18h30.

	DateTime previous(const Mark::Event & ev) const;

Same description as above, for next(mark) but for... previous occurrence.

	void listNext(uint8_t num, const Mark::Event & mark, DateTime returnArray[]) const ;
	void listPrevious(uint8_t num, const Mark::Event & mark, DateTime returnArray[]) const;

Fetch the list of the num next/previous occurrences of mark, and stick them into returnArray.

@param num: uint8_t number of DateTimes to set in returnArray
@param mark: Chronos::Mark::* time mark specification
@param: returnArray[]: an array of (at least) num DateTimes to return values in.

Arithmetic operators

	DateTime operator-(uint32_t numSeconds) const;
	DateTime & operator-=(uint32_t numSeconds);
	DateTime operator-(const Span::Delta & delta) const;
	DateTime & operator-=(const Span::Delta & delta);

	DateTime operator+(uint32_t numSeconds) const;
	DateTime & operator+=(uint32_t numSeconds);

	DateTime operator+(const Span::Delta & delta) const;
	DateTime & operator+=(const Span::Delta & delta);

You can perform arithmetic operations on DateTime objects and they will behave (mostly) as you expect.

 DateTime anExample = DateTime::now();

 DateTime result = anExample + 120; // result will be advanced by 120 seconds
 result = anExample + Chronos::Span::Minutes(2); // same thing

 anExample += Chronos::Span::Hours(3); in place operations

Same thing holds for subtraction but for two caveats: if you subtract the equivalent of "more seconds than have occurred since the epoch from the DT" then you'll wind up at epoch == 0 -- so in the 70s. Probably shouldn't do that.

The second thing is that if you subtract one DateTime from another, you don't get a DateTime as a result... you get a Chronos::Span -- i.e. the difference between the two, the length of time to get from one from the other (see below).

	Span::Absolute operator-(const DateTime & otherDt) const;

A (DateTime - DateTime) operation.

You can subtract one datetime from another but what you get (a Chronos::Span::Absolute) will alway bepositive* (equal to the absolute difference). Use comparison operators if needed to subtract the smaller from the bigger (later) datetime, if you care about "direction" of the result.

Returns Span::Absolute a Span:Delta object that has methods to query the size (in seconds) and break it down into days/hours/mins/secs etc. See Delta.h for details there.

	bool isWithin(const Bounds & bounds) const;

@param bounds: DateTime::Bounds object determining (inclusive) boundaries
Returns true if calling object is within bounds

Comparison operators

	bool operator==(const DateTime & dt) const ;
	bool operator>(const DateTime & dt) const;
	bool operator>=(const DateTime & dt) const;

	bool operator!=(const DateTime & dt) const;
	bool operator<(const DateTime & dt)  const;
	bool operator<=(const DateTime & dt) const;

All work as you'd think for dates and times, where "smaller" means in the past.

	bool sameTimeAs(const DateTime & dt) const;
	bool sameDateAs(const DateTime & dt) const;

Similar to == comparison operator, but only applies to object's time/date (respectively).

	bool isWeekend() const;
	bool isWeekday() const;

Returns true if the caller is on a weekend/weekday, respectively.

	void printTo(Print & p, bool includeTime=true) const;

printTo is mainly a convenience function for debugging, on Arduino or any platform that has some object with a compatible "Print" interface.

@param p: the "printer" (e.g. Serial with Arduino)
@param includeTime: boolean to indicate whether we include the time in the output (defaults to true)

Chronos::Span API

The various Chronos::Span classes all have the same base (Chronos::Span::Absolute) and the same methods, only their constructors vary.
These include:

	Chronos::Span::Seconds(uint32_t n);
	Chronos::Span::Minutes(uint32_t n);
	Chronos::Span::Hours(uint32_t n);
	Chronos::Span::Days(uint32_t n);
	Chronos::Span::Weeks(uint32_t n);

and do what you'd expect.

	uint32_t totalSeconds() const;


Time spans can provide their total seconds count as totalSeconds().
This total may be broken down into:

	uint32_t days() const;
	Chronos::Hours 	  hours() const;
	Chronos::Minutes  minutes() const ;
	Chronos::Seconds  seconds() const ;

For simplicity, there are no setters for these. If you want an Absolute equivalent to 3 days, 6 hours, 5 minutes and 2 seconds you just create one:

 (Span::Days(3) + Span::Hours(6) + Span::Minutes(5) + Span::Seconds(2))


	Absolute & operator+=(uint32_t secs);
	Absolute & operator+=(const Absolute & Absolute);

	Absolute & operator-=(uint32_t secs);
	Absolute & operator-=(const Absolute & Absolute);

	Absolute operator+(uint32_t secs) const ;
	Absolute operator+(const Absolute & Absolute) const;

	Absolute operator-(uint32_t secs) const ;
	Absolute operator-(const Absolute & Absolute) const;

You can add and subtract spans in the normal ways, with results mostly as expected.

NOTE: "mostly" here is because time spans are always positive. This works as expected for any additions, but if you invert things and subtract a larger from a smaller span, you'll still get a positive difference:

 (Chronos::Span::Minutes(1) - Chronos::Span::Hours(1)) == Chronos::Span::Minutes(59)


	bool operator<(const Absolute & other) const;
	bool operator==(const Absolute & other) const;

	bool operator<=(const Absolute & other) const; bool operator>(const Absolute & other)  const;
	bool operator>=(const Absolute & other) const;
	bool operator!=(const Absolute & other) const;

All comparisons between time spans are supported: ==, !=, <, >, <=, >=.

	void printTo(Print & p) const ;

Similar to the member method for DateTime, printTo is mainly a convenience function for debugging, on Arduino or any platform that has some object with a compatible "Print" interface.

@param p: the "printer" (e.g. Serial with Arduino)