166 lines
6.6 KiB
C++
166 lines
6.6 KiB
C++
#ifndef DATETIME_H
|
|
#define DATETIME_H
|
|
|
|
#include <SDL_stdinc.h>
|
|
#include <string>
|
|
|
|
namespace Time {
|
|
|
|
// We separate lengths of time (TimeDelta) from points in time (DateTime),
|
|
// because they have different operations defined on them
|
|
//
|
|
// The TimeDelta constructor takes a TimeUnit parameter, so you specify
|
|
// a length of time with, e.g., Time::TimeDelta(42, Time::Day)
|
|
//
|
|
// TimeUnit is an enum (rather than a set of const TimeDelta values) because
|
|
// that ensures the compiler will treat the values as compile time constants
|
|
// instead of objects that must be defined in a translation unit and initialised.
|
|
//
|
|
// The base units are (currently) microseconds, which is chosen
|
|
// because it's plenty high enough resolution for most uses, and it's
|
|
// easily understandable, and it gives good range when stored in
|
|
// signed 64-bit integers (approx. +/- 292277 years)
|
|
//
|
|
// The base units can be trivially changed by modifying the TimeUnit enum.
|
|
//
|
|
// DateTime is mostly about dealing with the Gregorian calendar.
|
|
//
|
|
// These types have a fairly unsophisticated understanding of the calendar
|
|
// and the passage of time. For example, leap seconds are not supported at all.
|
|
// But... I'm pretty sure we don't need leap seconds for Pioneer.
|
|
|
|
enum TimeUnit : Sint64 {
|
|
Microsecond = 1ll,
|
|
Millisecond = 1000ll * Microsecond,
|
|
Second = 1000ll * Millisecond,
|
|
Minute = 60ll * Second,
|
|
Hour = 60ll * Minute,
|
|
Day = 24ll * Hour,
|
|
Week = 7ll * Day
|
|
};
|
|
|
|
class TimeDelta;
|
|
class DateTime;
|
|
|
|
class TimeDelta {
|
|
public:
|
|
TimeDelta() :
|
|
m_delta(0) {}
|
|
explicit TimeDelta(Sint64 t, TimeUnit unit = Second) :
|
|
m_delta(t * unit) {}
|
|
|
|
Sint64 GetTotalWeeks() const { return (m_delta / Week); }
|
|
Sint64 GetTotalDays() const { return (m_delta / Day); }
|
|
Sint64 GetTotalHours() const { return (m_delta / Hour); }
|
|
Sint64 GetTotalMinutes() const { return (m_delta / Minute); }
|
|
Sint64 GetTotalSeconds() const { return (m_delta / Second); }
|
|
Sint64 GetTotalMilliseconds() const { return (m_delta / Millisecond); }
|
|
Sint64 GetTotalMicroseconds() const { return (m_delta / Microsecond); }
|
|
|
|
TimeDelta &operator+=(const TimeDelta &x)
|
|
{
|
|
m_delta += x.m_delta;
|
|
return *this;
|
|
}
|
|
TimeDelta &operator-=(const TimeDelta &x)
|
|
{
|
|
m_delta -= x.m_delta;
|
|
return *this;
|
|
}
|
|
|
|
friend TimeDelta operator+(const TimeDelta &a, const TimeDelta &b) { return TimeDelta(a.m_delta + b.m_delta, TimeUnit(1)); }
|
|
friend TimeDelta operator-(const TimeDelta &a, const TimeDelta &b) { return TimeDelta(a.m_delta - b.m_delta, TimeUnit(1)); }
|
|
friend TimeDelta operator*(Sint64 x, const TimeDelta &t) { return TimeDelta(x * t.m_delta, TimeUnit(1)); }
|
|
friend TimeDelta operator/(const TimeDelta &t, Sint64 x) { return TimeDelta(t.m_delta / x, TimeUnit(1)); }
|
|
friend Sint64 operator/(const TimeDelta &a, const TimeDelta &b) { return (a.m_delta / b.m_delta); }
|
|
|
|
friend DateTime operator+(const DateTime &a, const TimeDelta &b);
|
|
friend DateTime operator-(const DateTime &a, const TimeDelta &b);
|
|
|
|
private:
|
|
friend class DateTime;
|
|
Sint64 m_delta;
|
|
};
|
|
|
|
class DateTime {
|
|
public:
|
|
DateTime() :
|
|
m_timestamp(-Sint64(24 * 60 * 60) * Sint64(400 * 365 + 97) * Sint64(Second)) {}
|
|
// month = 1 to 12
|
|
// day = 1 to N where N is the number of days in the specified month and year
|
|
DateTime(int year, int month, int day, int hour = 0, int minute = 0, int second = 0, int microsecond = 0);
|
|
DateTime(double gameTime);
|
|
|
|
void GetDateParts(int *year, int *month, int *day) const;
|
|
void GetTimeParts(int *hour, int *minute, int *second, int *microsecond = nullptr) const;
|
|
|
|
double ToGameTime() const;
|
|
std::string ToDateString() const;
|
|
std::string ToTimeString() const;
|
|
std::string ToDateTimeString() const { return ToStringISO8601(); }
|
|
std::string ToStringISO8601() const;
|
|
|
|
friend TimeDelta operator-(const DateTime &a, const DateTime &b)
|
|
{
|
|
return TimeDelta(a.m_timestamp - b.m_timestamp, TimeUnit(1));
|
|
}
|
|
friend DateTime operator+(const DateTime &a, const TimeDelta &b)
|
|
{
|
|
return DateTime(a.m_timestamp + b.m_delta);
|
|
}
|
|
friend DateTime operator-(const DateTime &a, const TimeDelta &b)
|
|
{
|
|
return DateTime(a.m_timestamp - b.m_delta);
|
|
}
|
|
friend DateTime operator+(const TimeDelta &a, const DateTime &b) { return (b + a); }
|
|
|
|
DateTime &operator+=(const TimeDelta &x)
|
|
{
|
|
m_timestamp += x.m_delta;
|
|
return *this;
|
|
}
|
|
DateTime &operator-=(const TimeDelta &x)
|
|
{
|
|
m_timestamp -= x.m_delta;
|
|
return *this;
|
|
}
|
|
|
|
friend bool operator<(const DateTime &a, const DateTime &b) { return (a.m_timestamp < b.m_timestamp); }
|
|
friend bool operator<=(const DateTime &a, const DateTime &b) { return (a.m_timestamp <= b.m_timestamp); }
|
|
friend bool operator==(const DateTime &a, const DateTime &b) { return (a.m_timestamp == b.m_timestamp); }
|
|
friend bool operator!=(const DateTime &a, const DateTime &b) { return (a.m_timestamp != b.m_timestamp); }
|
|
friend bool operator>(const DateTime &a, const DateTime &b) { return (a.m_timestamp > b.m_timestamp); }
|
|
friend bool operator>=(const DateTime &a, const DateTime &b) { return (a.m_timestamp >= b.m_timestamp); }
|
|
|
|
Sint64 GetTimestamp() const { return m_timestamp; }
|
|
|
|
private:
|
|
explicit DateTime(Sint64 tstamp) :
|
|
m_timestamp(tstamp) {}
|
|
|
|
// The timestamp is the number of microseconds since the epoch (2001-01-01T00:00:00Z)
|
|
//
|
|
// This epoch (the start of the year 2001) is chosen because it makes counting
|
|
// leap years easier (see DateTime constructor from date components)
|
|
//
|
|
// However, these are *not* SI microseconds, they are mean solar microseconds.
|
|
// SI microseconds are defined in terms of the behaviour of the caesium atom.
|
|
// Mean solar microseconds are defined in terms of the rotation of the Earth.
|
|
// This means that there are always precisely 86400 * 10^6 mean solar microseconds in each day.
|
|
// Since every day contains precisely the same number of mean solar microseconds, it is practical
|
|
// to compute the number of mean solar microseconds between any two dates (specified
|
|
// according to the Gregorian calendar) or to compute a date from a timestamp.
|
|
//
|
|
// If we used SI microseconds, we would need to know all past and future leap seconds to be able to correctly
|
|
// convert between the timestamp value and a year-month-day-hour-minute-second-microsecond tuple.
|
|
// Apart from making the calculations a pain in the ass, that is actually impossible for dates in the future,
|
|
// because leap seconds are not predictable (they're introduced as necessary based on astronomical observations)
|
|
//
|
|
// (Incidentally, this is the way all integer timestamps work, at least all the ones I've ever seen)
|
|
Sint64 m_timestamp; // (units: microseconds; 0 means 2001-01-01T00:00:00Z)
|
|
};
|
|
|
|
} // namespace Time
|
|
|
|
#endif
|