I need to convert some integers
to date
. First off I do know of the Boost::Gregorian library, but I can't use it because it won't compile with Clang, which is where my application gets it best performance.
I am parsing the raw database files and so performance will be important since the conversions will happen hundreds of thousands of times to represent birthdates, timestamps, appointment times and so on.
I have a few different origin dates depending on which database I am parsing. The origin dates I am using are:
System 1: 1706-02-24
System 2: 1840-01-01
I tried it this way, but I get an error that timeinfo2
is null when I try printing it out:
time_t rawtime;
struct tm* timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
timeinfo->tm_year = 1706 - 1900;
timeinfo->tm_mon = 2 - 1;
timeinfo->tm_mday = 24;
timeinfo->tm_mday += 98040; // days since origin
time_t newtime;
struct tm* timeinfo2;
newtime = mktime(timeinfo);
timeinfo2 = localtime(&newtime);
Result should be: 1968-08-12
I have a lot of respect for what Howard has put together, but I needed something that would execute as fast as possible and worried about including the entire date library when all I needed was an int
to date
.
Here is what I came up with:
string GetDateFromDaysSincePointInTime(int days)
{
int a, b, c, d, e, m, dd, mm, yyyy;
a = days + 2374475;
b = (4 * a + 3) / 146097;
c = -b * 146097 / 4 + a;
d = (4 * c + 3) / 1461;
e = -1461 * d / 4 + c;
m = (5 * e + 2) / 153;
dd = -(153 * m + 2) / 5 + e + 1;
mm = -m / 10 * 12 + m + 3;
yyyy = b * 100 + d - 4800 + m / 10;
return to_string(yyyy) + "-" + to_string(mm) + '-' + to_string(dd);
}
To use this, simply call it with an int GetDateFromDaysSincePointInTime(113908)
. That will give you a date. Assuming my starting point is different from your starting point, go to the site https://www.timeanddate.com/date/dateadd.html and add/subtract your desired date from the date that was output. Then alter the int value on variable a
by that amount and run again to get your corrected date.
From there, it should be able to be easily altered to have leading zeros if desired:
std::ostringstream month;
month << std::setw(2) << std::setfill('0') << mm;
std::ostringstream day;
day << std::setw(2) << std::setfill('0') << dd;
return to_string(yyyy) + "-" + month.str() + '-' + day.str()
Here is another way that is more readable, and there doesn't seem to be any real performance hit on 30,000 records:
string GetDateFromInt(int days)
{
int startYear = 1600;
int year = days / 365.2421875;
float peryear = 365.2421875;
int remainder = fmod(days, peryear); // here you could add what day of the year to get a date in the middle of the year
bool leapyear= ((year & 3) == 0 && (year % 100 != 0));
int leapYearIndex = leapyear ? 1 : 0;
int daysInYear = leapYearIndex ? 366 : 365;
const unsigned short int __mon_yday[2][13] =
{
/* Normal years. */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
/* Leap years. */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};
int dayOfYear = remainder;
if (dayOfYear >= 1 && dayOfYear <= daysInYear) {
for (int mon = 0; mon < 12; mon++) {
if (dayOfYear <= __mon_yday[leapYearIndex][mon + 1]) {
int month = mon + 1;
int dayOfMonth = dayOfYear - __mon_yday[leapYearIndex][mon];
std::ostringstream months;
months << std::setw(2) << std::setfill('0') << month;
std::ostringstream day;
day << std::setw(2) << std::setfill('0') << dayOfMonth;
return to_string(startYear + year) + "-" + months.str() + '-' + day.str();
}
}
}
}