【问题标题】:C++ Int to Any Date Without External Library没有外部库的 C++ Int 到任何日期
【发布时间】:2020-02-24 00:23:49
【问题描述】:

我需要将一些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

【问题讨论】:

  • 不是C次,为什么不是C++
  • chrono 是否处理 1970 年之前的日期?抱歉,如果这是一个愚蠢的问题,我是 C++ 新手,并且引用的是 1970 年的日期。
  • chrono 是疯狂的多才多艺。 What epoch do you want to use?
  • @user4581301 测试来自该链接的代码使我的函数 sys_datescast_timeutc_time 由于某种原因未定义。我在 MSVS2019 中使用 C++17。我在这里看到了参考:en.cppreference.com/w/cpp/chrono/year_month_day/operator_days,但这似乎不在我与 Microsoft 的库中。不确定这是否仅适用于 unix,仍在解决中
  • 抱歉。我只是说这个链接是一种强化,你可以在你的时代疯狂。底部的代码是一种先发制人的打击。它演示了如何使用即将发布的 C++ 标准修订版中的功能。现在要使用该代码,您需要使用 Mr. Hinnant's Date library,这是 Chrono 的 C++2a 扩展的原型。

标签: c++ date gregorian-calendar


【解决方案1】:

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 基础的相同算法。

【讨论】:

    【解决方案2】:

    我非常尊重霍华德所做的一切,但我需要能够尽可能快地执行的东西,并且当我只需要 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 并从输出日期中添加/减去您想要的日期。然后将变量 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();
                }
            }
        }
    }
    

    【讨论】:

    • 第一个实现本质上与 boost 的相同(以字符串部分为模)。如果您对性能非常感兴趣,您可能会对this 项目感兴趣,我在该项目中展示了一个优于 boost、libc++、glibc、.Net、OpenJDK 等的实现(同样,没有字符串转换部分)。此外,它还包含逆转换(即从日期到整数)和其他日历算法:is_leap_yearlast_day_of_month
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多