gpt4 book ai didi

C++:将 Julian 日期转换为 Gregorian

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:22:32 25 4
gpt4 key购买 nike

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

  • ctime - 特别是使用 tm 结构和 mktime(tm *timeptr) 因为这通常会将 tm 结构的值设置到适当的位置,除了“timeptr 的成员 tm_wday 和 tm_yday 的原始值被忽略...”这无济于事。
  • Boost::DateTime - Gregorian 是构造 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 ) 没有帮助。而且我看不到任何其他转换函数。

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

有人可以帮忙吗?我宁愿避免自己编写,因为几乎总会有一些我想念的陷阱。

最佳答案

我偶然发现了这个老问题,并认为我可以向其中添加一些新信息。我写这篇文章时的唯一现有答案 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-compatible Low-Level Date Algorithms

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

在这个问题中,“day of year”是从 1 开始的计数,1 月 1 日是一年的开始(Jan 1 == day 1)。 chrono-compatible Low-Level Date Algorithms在算法 civil_from_days 中有类似的“day of year”概念但现在是 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-compatible Low-Level Date Algorithms具有用于日期操作的有用且高效的算法。即使您必须将算法分开并重新组合,它们也很有用,如本例所示。算法的解释是为了使您能够将它们分开并在诸如此类的示例中重新应用它们。
  2. <chrono>可以非常灵活地测量非常快的函数。比非常快快三倍仍然是一个很好的胜利。

关于C++:将 Julian 日期转换为 Gregorian,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2638441/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com