C++ Int 到任何没有外部库的日期
C++ Int to Any Date Without External Library
我需要将一些 integers
转换为 date
。首先,我知道 Boost::Gregorian 库,但我不能使用它,因为它不能用 Clang 编译,而这正是我的应用程序获得最佳性能的地方。
我正在解析原始数据库文件,因此性能很重要,因为转换将发生数十万次以表示生日、时间戳、约会时间等。
根据我正在解析的数据库,我有几个不同的起始日期。我使用的起始日期是:
System 1: 1706-02-24
System 2: 1840-01-01
我尝试过这种方式,但是当我尝试打印出来时出现 timeinfo2
为空的错误:
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);
结果应该是:1968-08-12
Here is a list of public domain algorithms that model Unix Time and the proleptic Gregorian calendar for millions of years backward and forward in time. 它们非常高效(无迭代、最小分支、最小缓存抖动)。
您可以使用这些算法编写自己的日期库,以正确处理 1970 年前的日期。这些也是构成 Howard Hinnant's C++20 preview of the <chrono>
library.
基础的相同算法
我非常尊重霍华德所做的一切,但我需要一些能够尽快执行的东西,并且担心在我需要的只是 int
到 date
。
这是我想出的:
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);
}
要使用它,只需使用 int GetDateFromDaysSincePointInTime(113908)
调用它。那会给你一个约会。假设我的起点与您的起点不同,请从输出日期转到站点 https://www.timeanddate.com/date/dateadd.html 和 add/subtract 您想要的日期。然后按该数量更改变量 a
上的 int 值,然后再次 运行 以获得更正日期。
从那里开始,如果需要,应该可以轻松地将其更改为具有前导零:
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()
替代方式
这是另一种可读性更强的方式,而且似乎对 30,000 条记录没有任何实际性能影响:
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();
}
}
}
}
我需要将一些 integers
转换为 date
。首先,我知道 Boost::Gregorian 库,但我不能使用它,因为它不能用 Clang 编译,而这正是我的应用程序获得最佳性能的地方。
我正在解析原始数据库文件,因此性能很重要,因为转换将发生数十万次以表示生日、时间戳、约会时间等。
根据我正在解析的数据库,我有几个不同的起始日期。我使用的起始日期是:
System 1: 1706-02-24
System 2: 1840-01-01
我尝试过这种方式,但是当我尝试打印出来时出现 timeinfo2
为空的错误:
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);
结果应该是:1968-08-12
Here is a list of public domain algorithms that model Unix Time and the proleptic Gregorian calendar for millions of years backward and forward in time. 它们非常高效(无迭代、最小分支、最小缓存抖动)。
您可以使用这些算法编写自己的日期库,以正确处理 1970 年前的日期。这些也是构成 Howard Hinnant's C++20 preview of the <chrono>
library.
我非常尊重霍华德所做的一切,但我需要一些能够尽快执行的东西,并且担心在我需要的只是 int
到 date
。
这是我想出的:
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);
}
要使用它,只需使用 int GetDateFromDaysSincePointInTime(113908)
调用它。那会给你一个约会。假设我的起点与您的起点不同,请从输出日期转到站点 https://www.timeanddate.com/date/dateadd.html 和 add/subtract 您想要的日期。然后按该数量更改变量 a
上的 int 值,然后再次 运行 以获得更正日期。
从那里开始,如果需要,应该可以轻松地将其更改为具有前导零:
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()
替代方式
这是另一种可读性更强的方式,而且似乎对 30,000 条记录没有任何实际性能影响:
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();
}
}
}
}