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.

基础的相同算法

我非常尊重霍华德所做的一切,但我需要一些能够尽快执行的东西,并且担心在我需要的只是 intdate

这是我想出的:

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();
            }
        }
    }
}