#include <stdint.h>
#include <stdio.h>
#include "../include/DateTime.h" // conflicts with datetime.h from python includes
#include "Utils.h"
#include "Test.h"
BEGIN_NAMESPACE
const int epochYear = -20000; // 20,000 BC
const int janOff = 0; const int febOff = janOff+31; const int marOff = febOff+28;
const int aprOff = marOff+31; const int mayOff = aprOff+30; const int junOff = mayOff+31;
const int julOff = junOff+30; const int augOff = julOff+31; const int sepOff = augOff+31;
const int octOff = sepOff+30; const int novOff = octOff+31; const int decOff = novOff+30;
const int yearOffsetByMonth[2][13] = {
/* Non-leap years (first index is dummy for Jan being represented by 01, no 00) */
{ 0, janOff, febOff, marOff, aprOff, mayOff, junOff,
julOff, augOff, sepOff, octOff, novOff, decOff },
/* Offsets for leap years */
{ 0, janOff, febOff, marOff+1, aprOff+1, mayOff+1, junOff+1,
julOff+1, augOff+1, sepOff+1, octOff+1, novOff+1, decOff+1 }
};
static bool isLeapYear(int year)
{
bool leapYear = false;
if ( (year % 4) == 0 ) {
if ( (year % 100) == 0 ) {
if ( (year % 400) == 0 ) {
leapYear = true;
}
} else {
leapYear = true;
}
}
return leapYear;
}
void DateTime::FromString(const char* a_str)
{
int year, month, day;
int hour, minute;
double seconds;
uint64_t microSeconds = 0ULL;
if (sscanf(a_str, "%d-%d-%d %d:%d:%lf", &year, &month, &day, &hour, &minute, &seconds) == 6)
{
bool leapYear = isLeapYear(year);
/* We will assume that always a leap year for easier de-coding of the microSeconds value */
/* As an int it will still be useful for comparisons, however the calculation for elapsed time */
/* is no longer so straight forward, particularly if calculating over a period of years
so it wouldn't be recommended to use this for that, or a special elapsed function is needed */
day = (year - epochYear) * (366) + yearOffsetByMonth[leapYear?1:0][month] + day;
microSeconds = (uint64_t)(seconds * 1000000.0);
microSeconds += minute * 60000000ULL;
microSeconds += hour * 3600000000ULL;
microSeconds += day * 86400000000ULL;
}
m_microSecondsFromEpoch = microSeconds;
}
String DateTime::ToString() const
{
int year, month, day;
int hour, minute;
int seconds;
uint64_t microSeconds = m_microSecondsFromEpoch;
microSeconds -= (day = int(microSeconds / 86400000000ULL)) * 86400000000ULL;
microSeconds -= (hour = int(microSeconds / 3600000000ULL)) * 3600000000ULL;
microSeconds -= (minute = int(microSeconds / 60000000ULL)) * 60000000ULL;
microSeconds -= (seconds = int(microSeconds / 1000000ULL)) * 1000000ULL;
day -= (year = day / 366) * 366;
year += epochYear;
bool leapYear = isLeapYear(year);
month = 0;
for (int i = 0; i < 13; i++)
{
if (yearOffsetByMonth[leapYear?1:0][i] > day)
{
month = i - 1;
break;
}
}
day -= yearOffsetByMonth[leapYear?1:0][month];
//std::string str = string_format("%d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, seconds, (int)microSeconds);
String str("%d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, seconds, (int)microSeconds);
return str;
}
uint64_t DateTime::Elapsed(const DateTime& a_startTime) const
{
int day = int(m_microSecondsFromEpoch / 86400000000ULL);
int startDay = int(a_startTime.m_microSecondsFromEpoch / 86400000000ULL);
if (day != startDay)
{
// we are crossing a day boundary so we need to do more checks
int year = day / 366;
int startYear = startDay / 366;
if (year != startYear)
{
int year1 = (year < startYear) ? year : startYear;
int year2 = (year > startYear) ? year : startYear;
// we are crossing a year boundary so need to do more work
int nonLeapYearCount = 0;
// we count the non-leap years between the years
for (int y = year1; y < year2; y++)
nonLeapYearCount += isLeapYear(y) ? 1 : 0; // TODO: fix this brute force approach
// Fixup for the fact we store years as if they have space for leap years for all years even though they don't
return (m_microSecondsFromEpoch - a_startTime.m_microSecondsFromEpoch) - 86400000000ULL*nonLeapYearCount;
}
}
// Simple case if we are inside the same year
return m_microSecondsFromEpoch - a_startTime.m_microSecondsFromEpoch;
}
uint64_t DateTime::ToInt() const
{
return m_microSecondsFromEpoch;
}
UNIT_TEST(TestLeapYear, 0)
{
CHECK(isLeapYear(1997) == false);
CHECK(isLeapYear(1900) == false);
CHECK(isLeapYear(2000) == true);
CHECK(isLeapYear(2012) == true);
}
UNIT_TEST(TestDateTime, 0)
{
DateTime t;
t.FromString("2012-01-01 01:01:01.000");
CHECK(t.ToString() == "2012-01-01 01:01:01.000");
t.FromString("2012-11-05 01:01:01.000");
CHECK(t.ToString() == "2012-11-05 01:01:01.000");
t.FromString("2001-01-01 01:01:01.000");
CHECK(t.ToString() == "2001-01-01 01:01:01.000");
t.FromString("2001-11-05 01:01:01.000");
CHECK(t.ToString() == "2001-11-05 01:01:01.000");
}
END_NAMESPACE