C++:将儒略日期转换为公历日期

发布于 2024-08-28 20:11:21 字数 1060 浏览 5 评论 0原文

我需要编写一个函数,将儒略日期(年、年、日、分)转换为标准形式(年、月、日、日、分)并将其表示为字符串。我想一定有人已经编写了一个库或组件,可以完成从一年中的某一天到月份和每月的某一天的转换。我查看了几个著名的日期时间库:

  • ctime -具体来说,使用 tm 结构和 mktime(tm *timeptr) ,因为这通常会将 tm 结构的值设置到适当的位置,除了“timeptr 的成员 tm_wday 和 tm_yday 的原始值被忽略。 ..”这没有帮助。
  • Boost::DateTime - 构造公历date(greg_year, greg_month, greg_day) 这没有帮助。但是,它们确实有一个 date_from_tm(tm datetm) ,但“字段: tm_wday 、 tm_yday 、 tm_hour 、 tm_min 、 tm_sec 和 tm_isdst 被忽略。”再次,没有帮助。
  • COleDateTime - 此项目包含 COM那么为什么不呢? COleDateTime 构造函数 COleDateTime( int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec ) 没有帮助。而且我没有看到任何其他转换函数可以与之配合。

正如您所看到的,这些都需要月份和月份的日期,这正是我首先要避免的。我一定是错过了一些东西,或者没有找到正确的地方(尽我所能,并不完美。)

任何人都可以帮忙吗?我宁愿避免自己编写,因为几乎总会有一些我错过的问题。

I need to write a function that converts a Julian dates (Year, Day of Year, Hour of Day and Minutes) into a standard form (Year, Month, Day of Month, Hour of Day and Minutes) and express it as a string. I figure there's got to be someone who's already written a library or component which can do the conversion from Day Of Year to Month and Day of Month. I've looked at several well-known datetime libraries:

  • ctime - Specifically using a tm struct and mktime(tm *timeptr) as this generally sets the values of the tm struct to the appropriate places except that "The original values of the members tm_wday and tm_yday of timeptr are ignored..." which doesn't help.
  • Boost::DateTime - Gregorian is constructed date(greg_year, greg_month, greg_day) which doesn't help. However, they do have a date_from_tm(tm datetm) but "The fields: tm_wday , tm_yday , tm_hour, tm_min, tm_sec, and tm_isdst are ignored." Again, no help.
  • COleDateTime - This project contains COM so why not? The COleDateTime constructor COleDateTime( int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec ) doesn't help. And I don't see any other conversion functions to go with it.

As you can see, these all need Month and Day of Month which is exactly what I'm trying to avoid in the first place. I must be either missing something or haven't looked in the right places (not perfect, as much as I try.)

Anyone can help? I'd prefer to avoid writing my own as there's almost always bound to be some gotcha I miss.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

记忆里有你的影子 2024-09-04 20:11:22

我偶然发现了这个老问题,并认为我可以添加一些新信息。我由 Thomas Pornin 撰写的现有答案是一个很好的答案,我已经投票了。然而,我将其视为改善它的挑战。如果我们能以两倍的速度得出相同的答案怎么办?也许更快?

为了测试这项工作,我将 Thomas 的答案包装在一个函数中:

#include <tuple>

std::tuple<int, int, int>
ymd_from_ydoy1(int year, int day_of_year)
{
    static const int month_len[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

    int leap = (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0);
    int day_of_month = day_of_year;
    int month;
    for (month = 0; month < 12; month ++) {
        int mlen = month_len[month];
        if (leap && month == 1)
            mlen ++;
        if (day_of_month <= mlen)
            break;
        day_of_month -= mlen;
    }
    return {year, month, day_of_month};
}

我改进它的尝试基于:

与 chrono 兼容的低级日期算法

上面的文章没有直接解决这种情况。然而,它确实详细描述了涉及日期操作的算法,甚至包括“一年中的某一天”概念,尽管该概念与此问题中指定的概念不同:

在这个问题中,“一年中的某一天”是 1基于 1 月 1 日的计数为一年的开始(1 月 1 == 第 1 天)。 chrono 兼容的低级日期算法 在算法中具有类似的“一年中的某一天”概念civil_from_days 但现在已经是 3 月 1 日过去的日子了(3 月 1 日 ==第 0 天)。

我的想法是,我可以从 civil_from_days 中挑选一些零碎的东西创建一个新的 ymd_from_ydoy,它不需要迭代 12 个月即可找到所需的结果。这就是我的想法:

std::tuple<int, int, int>
ymd_from_ydoy2(int year, int day_of_year)
{
    int leap = (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0);
    if (day_of_year < 60 + leap)
        return {year, day_of_year/32, day_of_year - (day_of_year/32)*31};
    day_of_year -= 60 + leap;
    int mp = (5*day_of_year + 2)/153;
    int day_of_month = day_of_year - (153*mp+2)/5 + 1;
    return {year, mp + 2, day_of_month};
}

仍然有分支机构,但数量较少。为了测试此替代方案的正确性和性能,我编写了以下内容:

#include <iostream>
#include <chrono>
#include <cassert>

template <class Int>
constexpr
bool
is_leap(Int y) noexcept
{
    return  y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
}

constexpr
unsigned
last_day_of_month_common_year(unsigned m) noexcept
{
    constexpr unsigned char a[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    return a[m-1];
}

template <class Int>
constexpr
unsigned
last_day_of_month(Int y, unsigned m) noexcept
{
    return m != 2 || !is_leap(y) ? last_day_of_month_common_year(m) : 29u;
}
int
main()
{
    using namespace std;
    using namespace std::chrono;
    typedef duration<long long, pico> picoseconds;
    picoseconds ps1{0};
    picoseconds ps2{0};
    int count = 0;
    const int ymax = 1000000;
    for (int y = -ymax; y <= ymax; ++y)
    {
        bool leap = is_leap(y);
        for (int doy = 1; doy <= 365 + leap; ++doy, ++count)
        {
            auto d1 = ymd_from_ydoy1(y, doy);
            auto d2 = ymd_from_ydoy2(y, doy);
            assert(d1 == d2);
        }
    }
    auto t0 = high_resolution_clock::now();
    for (int y = -ymax; y <= ymax; ++y)
    {
        bool leap = is_leap(y);
        for (int doy = 1; doy <= 365 + leap; ++doy)
        {
            auto d1 = ymd_from_ydoy1(y, doy);
            auto d2 = ymd_from_ydoy1(y, doy);
            assert(d1 == d2);
        }
    }
    auto t1 = high_resolution_clock::now();
    for (int y = -ymax; y <= ymax; ++y)
    {
        bool leap = is_leap(y);
        for (int doy = 1; doy <= 365 + leap; ++doy)
        {
            auto d1 = ymd_from_ydoy2(y, doy);
            auto d2 = ymd_from_ydoy2(y, doy);
            assert(d1 == d2);
        }
    }
    auto t2 = high_resolution_clock::now();
    ps1 = picoseconds(t1-t0)/(count*2);
    ps2 = picoseconds(t2-t1)/(count*2);
    cout << ps1.count() << "ps\n";
    cout << ps2.count() << "ps\n";
}

此测试中有三个循环:

  1. 测试两种算法在+/-一百万年的范围内产生相同的结果。
  2. 对第一个算法进行计时。
  3. 对第二个算法进行计时。

事实证明,这两种算法都非常快……在我正在测试的 iMac Core i5 上只需几纳秒。因此引入皮秒来获得纳秒分数的一阶估计。

    typedef duration<long long, pico> picoseconds;

我想指出两件事:

  1. 我们开始使用皮秒作为测量单位有多酷?
  2. std::chrono 让与皮秒的互操作变得如此简单,这有多酷?

对我来说,这个测试打印出(大约):

8660ps
2631ps

表明 ymd_from_ydoy2ymd_from_ydoy1 快大约 3.3 倍。

希望这有帮助。从这个答案中可以得到的重要信息:

  1. chrono 兼容的低级日期算法 有用且高效日期操作的算法。即使您必须将算法分开并重新组装(如本例所示),它们也很有用。对算法的解释使您能够将它们分开并在诸如此类的示例中重新应用它们。
  2. 在测量非常快的函数时可以非常灵活。比非常快快三倍仍然是一个不错的胜利。

I stumbled across this old question, and thought I might be able to add some new information to it. The single existing answer as I write this by Thomas Pornin is a good answer, and I've upvoted it. However I've taken it as a challenge to better it. What if we could produce the same answer twice as fast? Maybe even faster?

To test this effort, I've wrapped Thomas' answer up in a function:

#include <tuple>

std::tuple<int, int, int>
ymd_from_ydoy1(int year, int day_of_year)
{
    static const int month_len[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

    int leap = (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0);
    int day_of_month = day_of_year;
    int month;
    for (month = 0; month < 12; month ++) {
        int mlen = month_len[month];
        if (leap && month == 1)
            mlen ++;
        if (day_of_month <= mlen)
            break;
        day_of_month -= mlen;
    }
    return {year, month, day_of_month};
}

And my attempt to better it is based off of:

chrono-compatible Low-Level Date Algorithms

The above article does not address this situation directly. However it does go into detailed descriptions of the algorithms involved with date manipulations, and even includes a "day of year" concept, though that concept is different than what is specified in this question:

In this question "day of year" is a 1-based count with Jan 01 being the start of the year (Jan 1 == day 1). chrono-compatible Low-Level Date Algorithms has a similar "day of year" concept in the algorithm civil_from_days but it is days past Mar 01 (Mar 1 == day 0).

My thought is that I could pick bits and pieces from civil_from_days and create a new ymd_from_ydoy which did not need to iterate over the 12 months to find the desired result. Here is what I came up with:

std::tuple<int, int, int>
ymd_from_ydoy2(int year, int day_of_year)
{
    int leap = (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0);
    if (day_of_year < 60 + leap)
        return {year, day_of_year/32, day_of_year - (day_of_year/32)*31};
    day_of_year -= 60 + leap;
    int mp = (5*day_of_year + 2)/153;
    int day_of_month = day_of_year - (153*mp+2)/5 + 1;
    return {year, mp + 2, day_of_month};
}

There are still branches, but fewer of them. To test both the correctness and performance of this alternative I wrote the following:

#include <iostream>
#include <chrono>
#include <cassert>

template <class Int>
constexpr
bool
is_leap(Int y) noexcept
{
    return  y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
}

constexpr
unsigned
last_day_of_month_common_year(unsigned m) noexcept
{
    constexpr unsigned char a[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    return a[m-1];
}

template <class Int>
constexpr
unsigned
last_day_of_month(Int y, unsigned m) noexcept
{
    return m != 2 || !is_leap(y) ? last_day_of_month_common_year(m) : 29u;
}
int
main()
{
    using namespace std;
    using namespace std::chrono;
    typedef duration<long long, pico> picoseconds;
    picoseconds ps1{0};
    picoseconds ps2{0};
    int count = 0;
    const int ymax = 1000000;
    for (int y = -ymax; y <= ymax; ++y)
    {
        bool leap = is_leap(y);
        for (int doy = 1; doy <= 365 + leap; ++doy, ++count)
        {
            auto d1 = ymd_from_ydoy1(y, doy);
            auto d2 = ymd_from_ydoy2(y, doy);
            assert(d1 == d2);
        }
    }
    auto t0 = high_resolution_clock::now();
    for (int y = -ymax; y <= ymax; ++y)
    {
        bool leap = is_leap(y);
        for (int doy = 1; doy <= 365 + leap; ++doy)
        {
            auto d1 = ymd_from_ydoy1(y, doy);
            auto d2 = ymd_from_ydoy1(y, doy);
            assert(d1 == d2);
        }
    }
    auto t1 = high_resolution_clock::now();
    for (int y = -ymax; y <= ymax; ++y)
    {
        bool leap = is_leap(y);
        for (int doy = 1; doy <= 365 + leap; ++doy)
        {
            auto d1 = ymd_from_ydoy2(y, doy);
            auto d2 = ymd_from_ydoy2(y, doy);
            assert(d1 == d2);
        }
    }
    auto t2 = high_resolution_clock::now();
    ps1 = picoseconds(t1-t0)/(count*2);
    ps2 = picoseconds(t2-t1)/(count*2);
    cout << ps1.count() << "ps\n";
    cout << ps2.count() << "ps\n";
}

There are three loops in this test:

  1. Test that the two algorithms produce the same results over a range of +/- a million years.
  2. Time the first algorithm.
  3. Time the second algorithm.

It turns out that both algorithms are wicked fast ... a handful of nanoseconds on the iMac Core i5 I'm testing on. And thus the introduction of picoseconds to get a first-order estimate of fractional nanoseconds.

    typedef duration<long long, pico> picoseconds;

I'd like to point out two things:

  1. How cool is it that we are starting to use picoseconds as a unit of measurement?
  2. How cool is it that std::chrono makes it so easy to interoperate with picoseconds?

For me this test prints out (approximately):

8660ps
2631ps

Indicating that ymd_from_ydoy2 is approximately 3.3 times faster than ymd_from_ydoy1.

Hope this helps. Important things to get from this answer:

  1. chrono-compatible Low-Level Date Algorithms has useful and efficient algorithms for date manipulation. They can be useful even if you have to pick the algorithms apart and reassemble them, as in this example. The explanations of the algorithms are there to enable you to pick them apart and reapply them in examples such as this.
  2. <chrono> can be very flexible in measuring very fast functions. Three times faster than very fast is still a good win.
鹊巢 2024-09-04 20:11:22

从一年中的一天计算出月份和月份中的日期似乎很容易。这应该可以做到:

static const int month_len[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

int leap = (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0);
int day_of_month = day_of_year;
int month;
for (month = 0; month < 12; month ++) {
    int mlen = month_len[month];
    if (leap && month == 1)
        mlen ++;
    if (day_of_month <= mlen)
        break;
    day_of_month -= mlen;
}

请注意,这计算一月份从零开始的月份,但假设天数(一年中的某一天或一个月中的某一天)从一开始。如果一年中的日期计数无效(超出年底),则生成的值为 12(“12 月之后的月份”)。

“儒略历”是一个混乱的根源,因为它也代表与公历相差几十天的“儒略历”以及闰年的计算。在这里,我只是假设您只想在给定的公历年份的上下文中将“一年中的某一天”计数转换为“月份和月份中的某一天”。

It seems easy to compute the month and day of month from the day of year. This should do it:

static const int month_len[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

int leap = (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0);
int day_of_month = day_of_year;
int month;
for (month = 0; month < 12; month ++) {
    int mlen = month_len[month];
    if (leap && month == 1)
        mlen ++;
    if (day_of_month <= mlen)
        break;
    day_of_month -= mlen;
}

Note that this computes the month starting with zero for January, but assumes that day counts (either day of year or day of month) start at one. If the day of year count is invalid (beyond the end of the year) then the resulting month value is 12 (the "month after December").

"Julian" is a source of confusion, since it also stands for the "Julian calendar" which differs from the Gregorian calendar by a few dozen days, and the computation of leap years. Here I just assumed that you just wanted to convert a "day of year" count into a "month and day of month" within the context of a given, Gregorian year.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文