如何转换一年中的某一天(365 天)等效日期 12 月 31 日

How can I convert the day of the year (365 days) equivalent date December 31

提问人:rob 提问时间:1/24/2023 最后编辑:rob 更新时间:1/25/2023 访问量:319

问:

如何使用 mktime() 等 C++ 库将日期转换为日期,而不是制作自己的算法

C++ 时间 mktime

评论

2赞 Sam Varshavchik 1/24/2023
我相当确定所有年份都至少有 365 天。
1赞 Pepijn Kramer 1/24/2023
在C++使用<chrono>另请参阅:chrono from_stream正确用法。注意:如果您必须包含来自 C++ 的头文件,则您将包含一个头文件(通常)用于向后兼容“C”。.h
0赞 Peter 1/24/2023
@PepijnKramer....或向后兼容预标准 C++ 的标头。例如,它于 1996 年首次出现在 C++ 标准草案中,从 1990 年到 2000 年代初在 C++ 中经常使用(因为在 1998 年首次批准该标准后,实现需要一段时间才能赶上并完全支持该标准)。<iostream><iostream.h>
0赞 Pepijn Kramer 1/24/2023
@Peter很好的补充和真实。我应该知道,自 1995 年以来一直使用 C++。;)(但我也很久没有使用标准库中的任何 .h 文件了)
1赞 Pepijn Kramer 1/24/2023
另一个不错的选择是使用 Howard Hinnant 的日期/时间库:github.com/HowardHinnant/date。(如果你还不能使用 C++20,那就特别好)

答:

1赞 Marcus Müller 1/24/2023 #1

Mktime 不是 C++,而是一个 C 库函数。老实说,这非常可怕。你甚至提到它的事实可能表明你正在阅读过时的材料。此外,它的目的与人们认为它的作用不同,但这在这里无关紧要:

所以,一个现代的 C++ 解决方案(所有功劳都归功于 chris,他把这段代码放在 godbolt 编译器资源管理器上,我重新设计了一下供你试验:

#include <array>
#include <chrono>
#include <iostream>

using namespace std::literals;
namespace chr = std::chrono;

int main() {
    constexpr auto day_of_year = 365;

    // y suffix, to turn 2022 into a year specification in std::literals
    // the overloaded / operator makes specifying Gregorian calendar dates easy
    const auto date = chr::sys_days{2022y / 1 / 0} + chr::days{day_of_year};

    // Year-month-day representation of the resulting date
    const chr::year_month_day ymd(date);

    // Weekday
    const chr::weekday day_of_week(date);
    constexpr std::array<const char*, 7> weekdays{"Weekend, the 2nd",
                                                  "Extended Weekend",
                                                  "Tuesday",
                                                  "Funday",
                                                  "Ultimate Funday",
                                                  "Pre-Weekend",
                                                  "Weekend (first day)"};

    // explicitly cast to int (for years, which exist BC) or unsigned
    // (month of year and day of month are always non-negative)
    std::cout << static_cast<int>(ymd.year()) << "-"
              << static_cast<unsigned>(ymd.month()) << "-"
              << static_cast<unsigned>(ymd.day()) << ",\n"
              << "Which is a " << weekdays[day_of_week.c_encoding()] << "\n";

    // note that the above date calculation is all based on constants, so the
    // compiler does it for you at compile time – it doesn't get more efficient
}

C++ 有 .这允许你做一些事情,比如创建一个描述一年开始的日期表示对象,然后添加一个时间增量,即一个(例如 365 天或 10⁶ 秒),然后将其转换为你需要的任何可读表示。std::chronostd::duration

你真的应该偶然发现它!文档充足且随时可用

评论

0赞 chris 1/24/2023
你打败了我一个答案,但为了省去你的麻烦:gcc.godbolt.org/z/1dd66Trr9。看起来 godbolt 有问题,但它适用于我的机器™。
2赞 Howard Hinnant 1/24/2023
输出真的是 2022-12-31 吗?我想你有一个差 1 错误,你的输出是 2023-01-01。但你真的非常接近我会写的:.一个数据点表示“无效日期”的胜利!;-)sys_days{year/1/0}
1赞 chris 1/24/2023
@HowardHinnant,我很愚蠢,忘记了我在本地 IDE 中将其更改为 /0,所以我没有将其复制回 godbolt。谢谢你指出来!
0赞 Henrique Bucher 1/24/2023
std::mktimeC++ 标准的一部分。你的答案不正确。
0赞 Marcus Müller 1/24/2023
@chris将您的代码简化为“也适用于 Godbolt”,并将C++ std 减少到 20 C++。谢谢!
0赞 Henrique Bucher 1/24/2023 #2

下面写了一个如何使用来处理所提出的问题的示例。std::mktime

该算法的关键是用于将年份输入转换为基准年纪元,即从 1970-01-01 到 year-01-01 的秒数。std::mktime

然后,手动添加距离所需日期的秒数。请记住,闰秒不计算在内,因此只需将一天中的秒数 86400 相加即可。

最后一步是从纪元时间转换回完整日期。

#include <ctime>
#include <cstring>
#include <cstdio>

struct YearDay {
    int year;
    int month;
    int day;
};

YearDay convert( int year, int yday ) {
    // Convert year to epoch
    struct tm tms;
    std::memset(&tms,0,sizeof(tms));
    tms.tm_year = year - 1900;
    std::time_t date = std::mktime(&tms);

    // add days manually
    date += yday * 86400;

    // Synthesize into a full date
    struct tm* newtm = std::gmtime( &date );
    return YearDay{newtm->tm_year+1900, 
                   newtm->tm_mon+1, 
                   newtm->tm_mday};    
}

一个用法示例是:

int main() {
    int year = 2022;
    for ( int yday = 1; yday<=365; ++yday ) {
    YearDay yd = convert( year, yday );
    printf( "%4d %-3d  ==> %4d-%02d-%02d\n",
            year, yday, yd.year, yd.month, yd.day  );
    }
}

运行时产生:

Program stdout
2022 1    ==> 2022-01-01
2022 2    ==> 2022-01-02
2022 3    ==> 2022-01-03
...
2022 363  ==> 2022-12-29
2022 364  ==> 2022-12-30
2022 365  ==> 2022-12-31

Godbolt:https://godbolt.org/z/YvGsoenbK

评论

0赞 Marcus Müller 1/25/2023
对于这个答案感兴趣的读者:诺尔和我在这里对他的这个答案进行了相当有趣的(我认为!)讨论,以及我的答案在我的答案下面。我的观点是,这都是C代码(也许放在前面),这恰好是有效的C++,但不是你想在C++代码库中看到的东西。可以理解的是,诺尔有不同的意见。我建议阅读这两个答案,并形成自己的观点!std::
0赞 selbie 1/24/2023 #3

首先,2022 年 12 月 31 日是星期六,而不是标题所暗示的星期日。

虽然你可能会找到一个 std:: 库方法来获得你想要的东西,但我在面试循环中问的是“计算一年中的某一天”类型问题的较小版本。因此,我很容易为您删除此代码。下面是一个函数,它将花费一年和一个“一年中的某一天”(闰年的值介于 1-365 或 1-366 之间)并为月、日和星期几生成一个字符串。享受。

此代码的缺点是字符串被硬编码为英语。但它有效。如果您需要使其适用于公元 301 年之前的日期,则需要稍作调整。

#include <iostream>
#include <string>
using namespace std;

string MakeDate(const int year, const int dayOfYear) {
    auto isLeapYear = [](int y) {
        return (y % 400 == 0) || ((y % 4 == 0) && (y % 100 != 0));
    };
    static const int days_in_month[13] =    { 0, 31,28,31,30,31,30,31,31,30,31,30,31 };
    static const int days_in_month_ly[13] = { 0, 31,29,31,30,31,30,31,31,30,31,30,31 };
    static const string month_names[13] = { "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
    static const string day_names[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursay", "Friday", "Saturday" };

    // January 1, 301 was a Tuesday if the Gregorian calendar went back that far
    // Every 400 years since then, it's been a Tuesday on January 1
    // 0=Sunday, 1=Monday, 2=Tuesday.... 6=Saturday
    int firstDayOfYear = 2;
    int computeYear = 301;
    int diff = (year > computeYear) ? (year - computeYear) : 0;
    computeYear += 400 * (diff / 400);

    while (year > computeYear) {
        int addOn = isLeapYear(computeYear) ? 2 : 1;
        firstDayOfYear = (firstDayOfYear + addOn) % 7;
        computeYear++;
    }

    // assume "jan 1" is "dayOfYear==1". Normalize it to be a zero based offset
    int days = dayOfYear - 1;
    int d = 1;
    int m = 1;
    int y = year;
    int dayOfWeek = (firstDayOfYear + days) % 7;

    const int* dim = isLeapYear(y) ? days_in_month_ly : days_in_month;

    while (days >= dim[m]) {
        days -= dim[m];
        m++;
        if (m > 12) {
            m = 1;
            y++;
            dim = isLeapYear(y) ? days_in_month_ly : days_in_month;
        }
    }
    d = 1 + days;

    string result = month_names[m] + " " + to_string(d) + " " + to_string(y) + ", " + day_names[dayOfWeek];
    return result;
}
int main()
{
    // print out all 365 days of 2022
    for (int i = 1; i <= 365; i++)
    {
        std::cout << MakeDate(2022, i) << "\n";
    }
    return 0;
}

评论

0赞 Henrique Bucher 1/24/2023
问题是他是否为此目的使用 c++ 标准库