EADateTime

Introduction

EADateTime provides a unified implementation of date, time, and calendar functionality. The DateTime class represents date and time in a single class. Unlike other date/time systems, this class doesn't have a strictly limited resolution, due to its use of 64 bits (instead of 32 bits) of storage for the date/time value.

EADateTime supports the modern Gregorian calendar correctly, including such concepts as leap years. You can, for example, use EADateTime to implement proper calendar functionality for your application.

The C standard library provides some time and date functionality, but it is slow, has some limitations, is not very easy to use, and isn't supported by all C/C++ compilers. Writing a calendar application with the C time and date functions would be possible, but would be tedious and error-prone.

EADateTime does not support the formatting of date and time strings for display to a users. Such functionality is outside the scope of this class, primarily due to the issue of multiple string encodings (e.g. ASCII and Unicode) and due to the existence of multiple locales (e.g. English and French) which use different formats for time and date strings. A separate module -- EALocale -- exists which implements string formatting of dates and times. You can use that module and EADateTime together to implement locale-savvy times, dates, and calendars.

Example usage

All examples presume a #include "Foundation/EADateTime.h" and assume the using of namespace EA.

Basic usage of class DateTime:

DateTime dt;   // Sets dt to the current date and time.
 
uint32_t year = dt.GetParameter(kParameterYear);      // Get the current year.
 
dt.SetParameter(kParameterMonth, kMonthAugust);       // Set month to August.
dt.AddTime(kParameterDayOfYear, 77);                  // Add 77 days.

Comparison of DateTime instances:

DateTime dtD(2008, kMonthDecember, 25);   // Sets dt to Christmas 2008.
DateTime dtH(2008, kMonthOctober, 31);    // Sets dt to Halloween 2008.
 
if(dtH < dtD)
    printf("Halloween occurs before Christmas.");
else
    printf("Christmas occurs before Halloween.");

How to save and restore a DateTime to and from disk:

DateTime dt;

uint64_t n = dt.GetSeconds();         // Get the absolute time in seconds. This alone fully represents a DateTime.

WriteToDisk64(ToBigEndian(n));        // Hypthetical disk writing function and endian conversion function.

n = FromBigEndian(ReadFromDisk64());  // Hypothetical disk reading function and endian conversion function.

dt = DateTime(n);                     // Restore the saved value to a DateTime instance.

Usage of standalone EADateTime utility functions, which are independent of class DateTime:

bool b = IsLeapYear(2015);
 
uint32_t n = GetDaysInYear(1974);
 
uint32_t d = GetDaysInMonth(kMonthMarch, 2007);
 
int64_t nSeconds = GetDaylightSavingsBias();

Conversion of C time to DateTime:
#include <time.h>

tm time;
asctime(&ascTime);         // Get C time.

DateTime dt;
TmToDateTime(time, dt);    // Convert to DateTime.

Interface

The following is defined in EADateTime.h. The latest versions of these definitions can be found in the current version of EADateTime.h

Enumerations

enum Month
{
    kMonthUnknown        =  0,
    kMonthJanuary        =  1,
    kMonthFebruary       =  2,
    kMonthMarch          =  3,
    kMonthApril          =  4,
    kMonthMay            =  5,
    kMonthJune           =  6,
    kMonthJuly           =  7,
    kMonthAugust         =  8,
    kMonthSeptember      =  9,
    kMonthOctober        = 10,
    kMonthNovember       = 11,
    kMonthDecember       = 12
};
 
enum DayOfMonth
{
    kDayOfMonthUnknown   =  0,
    kDayOfMonthMin       =  1,
    kDayOfMonthMax       = 31   /// The actual max month is dependent on which month is being referred to.
};

enum DayOfWeek
{
    kDayOfWeekUnknown    =  0,
    kDayOfWeekSunday     =  1,  
    kDayOfWeekMonday     =  2,
    kDayOfWeekTuesday    =  3,
    kDayOfWeekWednesday  =  4,
    kDayOfWeekThursday   =  5,
    kDayOfWeekFriday     =  6,
    kDayOfWeekSaturday   =  7
};

/// TimeFrame
enum TimeFrame
{
    kTimeFrameUnknown = 0,  /// Unspecified time frame.
    kTimeFrameUTC     = 1,  /// Universal Coordinated Time. This is the time based on the time zone at Greenwich, England.
    kTimeFrameLocal   = 2   /// Same time as current machine.
};

/// TimeZone
/// Standard time zone definitions, with their values corresponding to the nmuber of hours they are
/// off relative to UTC (Universal Coordinated Time, which is the time at Greenwich England). 
/// Note, for example, that kTimeZonePacific is -8 hours relative to Greenwich, England.
enum TimeZone
{
    kTimeZoneEniwetok    = -12,
    kTimeZoneSamoa       = -11,
    kTimeZoneHawaii      = -10,
    kTimeZoneAlaska      =  -9,
    kTimeZonePacific     =  -8,
    kTimeZoneMountain    =  -7,
    kTimeZoneCentral     =  -6,
    kTimeZoneEastern     =  -5,
    kTimeZoneAltantic    =  -4,
    kTimeZoneBrazilia    =  -3,
    kTimeZoneMidAtlantic =  -2,
    kTimeZoneAzores      =  -1,
    kTimeZoneGreenwich   =   0,
    kTimeZoneRome        =  +1,
    kTimeZoneIsrael      =  +2,
    kTimeZoneMoscow      =  +3,
    kTimeZoneBaku        =  +4,
    kTimeZoneNewDelhi    =  +5,
    kTimeZoneDhakar      =  +6,
    kTimeZoneBangkok     =  +7,
    kTimeZoneHongKong    =  +8,
    kTimeZoneTokyo       =  +9,
    kTimeZoneSydney      = +10,
    kTimeZoneMagadan     = +11,
    kTimeZoneWellington  = +12
};

/// Epoch
/// The use of an epoch is to provide a timeframe with which to work.
/// Most of the time you don't need to know about this.
enum Epoch
{
    kEpochUnknown         =  0,
    kEpochJulian          =  1, /// Began at noon, January 1, 4712 BC.
    kEpochModifiedJulian  =  2, /// Began at midnight, November 17, 1858. Exactly 2,400,000.5 days after Julian epoch began.
    kEpochGregorian       =  3, /// Began at midnight, September 14, 1752.
    kEpoch1900            =  4, /// Began at midnight, January 1, 1900.
    kEpoch1950            =  5, /// Began at midnight, January 1, 1950.
    kEpoch1970            =  6, /// Began at midnight, January 1, 1970. Same epoch used by C runtime library and Unix OS.
    kEpoch2000            =  7  /// Began at midnight, January 1, 2000.
};

enum Era
{
    kEraUnknown = 0,
    kEraBC      = 1,
    kEraAD      = 2
};

/// Parameter
/// Defines a date or time parameter.
enum Parameter
{
    kParameterUnknown     =  0,
    kParameterYear        =  1, /// Refers to full year value, such as 1994, 2006, etc. but not a two digit value such as 94 or 04. The valid range is 0-INT_MAX.
    kParameterMonth       =  2, /// Refers to month of year, starting with 1 for January. The valid range is 1-12.
    kParameterWeekOfYear  =  3, /// Refers to the week of the year, starting with 1 for the week of January 1. The valid range is 1-52.
    kParameterWeekOfMonth =  4, /// Refers to the week of the month, starting with 1 for the first week. The valid range is 1-5.
    kParameterDayOfYear   =  5, /// Refers to a day of the year, starting with 1 for January 1st. The valid range is 1-366.
    kParameterDayOfMonth  =  6, /// Refers to the day of the month, starting with 1 for the first of the month. The valid range is 1-31.
    kParameterDayOfWeek   =  7, /// Refers to the day of the week, starting with 1 for Sunday. The valid range is 1-7.
    kParameterHour        =  8, /// Refers to the hour of a day in 24 hour format, starting with 0 for midnight. The valid range is 0-23.
    kParameterMinute      =  9, /// Refers to the minute of the hour, starting with 0 for the first minute. The valid range is 0-59.
    kParameterSecond      = 10, /// Refers to the second of the minute, starting with 0 for the first second. The valid range is 0-59.
};

/// Conversions
/// Defines useful conversion multipliers between seconds, minutes, hours, and days.
/// Conversions are not defined for some entities (e.g. days per year) because the 
/// value changes based on the particular year.
enum Conversions
{
    kSecondsPerMinute =    60,
    kSecondsPerHour   =  3600,
    kSecondsPerDay    = 86400,
    kMinutesPerHour   =    60,
    kMinutesPerDay    =  1440,
    kHoursPerDay      =    24,
    kDaysPerWeek      =     7,
    kWeeksPerYear     =    52,
    kMonthsPerYear    =    12
};
  

DateTime

class DateTime
{
public:
    static const uint32_t kValueDefault = 0xffffffff;
    static const uint32_t kValueIgnored = 0xffffffff;

public:
    DateTime(TimeFrame timeFrame = kTimeFrameLocal);

    DateTime(int64_t nSeconds) : mnSeconds(nSeconds);

    DateTime(const DateTime& dateTime) : mnSeconds(dateTime.mnSeconds);

    /// DateTime
    /// Constructs a DateTime class object from some standard parameters.
    /// To construct a DateTime class with a different set of parameter types,
    /// you'll need to use the Set function or in odd cases do manual calculations. 
    DateTime(uint32_t nYear, uint32_t nMonth, uint32_t nDayOfMonth, 
             uint32_t nHour = 0, uint32_t nMinute = 0, uint32_t nSecond = 0);

    /// operator=
    DateTime& operator=(const DateTime& dateTime);

    /// Compare
    /// This function compares this object with another DateTime object and 
    /// returns an integer result.The return value is the same as with the 
    /// strcmp string comparison function. If this object is less than the 
    /// argument dateTime, the return value is < 0. Comparison operators are 
    /// defined outside this class, though they use the Compare function to do their work.
    int Compare(const DateTime& dateTime, bool bCompareDate = true, bool bCompareTime = true) const;

    /// GetParameter
    /// Gets the given parameter. If you want to get the year, you would call Get(kParameterYear).
    uint32_t GetParameter(Parameter parameter) const;

    /// SetParameter
    /// Sets the given parameter. If you want to set the year to 1999, you would 
    /// call Set(kParameterYear, 1999).
    void SetParameter(Parameter parameter, uint32_t nValue);

    /// Set
    /// Sets the time based on the current time. If the timeFrame is kTimeFrameUTC, 
    /// the time is set to what the current UTC time is, which will be a fixed number 
    /// of hours off of what the current local time is.
    void Set(TimeFrame timeFrame = kTimeFrameLocal);

    /// Set
    /// Sets the time based on various inputs. If any input is kValueIgnored (uint32_t)-1, 
    /// then the input is ignored and the current value is used. If any of the cyclic 
    /// inputs is beyond its valid range, the modulo of the value is used and the division 
    /// of the value is added to the next higher bracket. For example, if the input nMinute 
    /// is 65, then the minute used is 5 and 1 is added to the current hour value.
    void Set(uint32_t nYear, uint32_t nMonth, uint32_t nDayOfMonth, uint32_t nHour, 
             uint32_t nMinute, uint32_t nSecond);

    /// AddTime
    /// Allows you to increment (or decrement) the given parameter by the given amount.
    void AddTime(Parameter parameter, int32_t nValue);

    /// IsDST
    /// Returns true if the time is daylight savings time. This function assumes that DST is valid
    /// for the given current locale; some locales within the United States don't observe DST.
    bool IsDST() const;

    /// GetSeconds
    /// Returns the DateTime internal representation.
    int64_t GetSeconds() const;
};

Utility

/// IsLeapYear
/// Returns true if the given year is a leap year.
bool IsLeapYear(uint32_t nYear);

/// GetDaysInYear
/// Returns the number of days in the given year. The value will vary based on whether 
/// the year is a leap year or not.
uint16_t GetDaysInYear(uint32_t nYear);

/// GetDaysInMonth
/// Returns the number of days in the given month. The value will vary based on the 
/// month and based on whether the year is a leap year. The input nMonth takes one
/// of enum Month or an integer equivalent.
uint8_t GetDaysInMonth(uint32_t nMonth, uint32_t nYear);

/// GetDayOfYear
/// The input nMonth takes one of enum Month or an integer equivalent.
/// The input nDayOfMonth takes an integer consistent with enum DayOfMonth.
uint32_t GetDayOfYear(uint32_t nMonth, uint32_t nDayOfMonth, uint32_t nYear);

/// Convert4DigitTo2DigitYear
/// Note that two-digit years bring a number of problems; they are best used for text
/// display only and not for any internal processing.
inline int Convert4DigitTo2DigitYear(int nYear4);

/// Convert2DigitTo4DigitYear
/// This code returns a year in the 1900s if the input year is > 30 but returns
/// a year in the 2000s if the year is <= 30. This is merely a guess and in fact there
/// really is no good way to reliably convert a two digit year to a four digit year.
inline int Convert2DigitTo4DigitYear(int nYear2); 

/// GetCurrent
/// Returns the current year, month, hour, etc.
uint32_t GetCurrent(Parameter parameter);

/// GetDaylightSavingsBias
/// Returns the number of seconds that the current time is daylight savings adjusted from 
/// the conventional time. Adding this value to the conventional time yields the time when
/// adjusted for daylight savings.
int64_t GetDaylightSavingsBias();

/// GetTimeZoneBias
/// Returns the number of seconds that the local time zone is off of UTC.
/// Adding this value to the current UTC yields the current local time.
int64_t GetTimeZoneBias();

/// DateTimeToTm
/// Converts a DateTime to a C tm struct.
void DateTimeToTm(const DateTime& dateTime, tm& time);

/// TmToDateTime
/// Converts a C tm struct to a DateTime.
void TmToDateTime(const tm& time, DateTime& dateTime);