whe*_*ies 5 c++ datetime date julian-date
我需要编写一个函数,将Julian日期(年,日,日和小时)转换为标准格式(年,月,日,小时和分钟)并将其表示为字符串.我认为必须有一个人已经编写了一个库或组件,可以进行从日期到月份和月份的转换.我查看了几个众所周知的日期时间库:
mktime(tm *timeptr)因为这通常将tm结构的值设置为适当的位置,除了"忽略了成员的原始值tm_wday和tm_yday的timeptr ..."这没有帮助.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( 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};
}
Run Code Online (Sandbox Code Playgroud)
我改善它的尝试是基于:
上述文章没有直接解决这种情况.然而,它确实详细描述了与日期操作有关的算法,甚至包括"一年中的一天"概念,尽管该概念与此问题中指定的概念不同:
在这个问题中,"一年中的一天"是一个基于1的计数,1月01日是年初(1月1日= 1日). 时间兼容的低级别日期算法在算法中具有类似的"一年中的一天"概念,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};
}
Run Code Online (Sandbox Code Playgroud)
仍有分支,但较少.为了测试这个替代方案的正确性和性能,我写了以下内容:
#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";
}
Run Code Online (Sandbox Code Playgroud)
此测试中有三个循环:
事实证明,这两种算法都是快速的...我正在测试的iMac Core i5上有几纳秒的时间.因此引入皮秒以获得分数纳秒的一阶估计.
typedef duration<long long, pico> picoseconds;
Run Code Online (Sandbox Code Playgroud)
我想指出两件事:
std::chrono可以很容易地与皮秒互操作?对我来说,这个测试打印出来(大约):
8660ps
2631ps
Run Code Online (Sandbox Code Playgroud)
表明ymd_from_ydoy2大约是其快3.3倍ymd_from_ydoy1.
希望这可以帮助.从这个答案中得到的重要事项:
<chrono>可以非常灵活地测量非常快速的功能.比快速快三倍仍然是一个很好的胜利.从一年中的一天计算出月份和月份中的日期似乎很容易。这应该可以做到:
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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,这会计算一月份从零开始的月份,但假设日计数(一年中的某一天或一个月中的某一天)从 1 开始。如果一年中的日期计数无效(超出年底),则结果month值为 12(“12 月之后的月份”)。
“儒略历”是一个混乱的根源,因为它也代表与公历相差几十天的“儒略历”以及闰年的计算。在这里,我只是假设您只想在给定的公历年份的上下文中将“一年中的某一天”计数转换为“月份和月份中的某一天”。