我想打印或提取年/月/日值。
由于2038年的问题,我不想使用time_t
,但我在互联网上找到的所有示例都使用它将time_point
转换为tm
。
是否有一种简单的方法可以将time_point转换为tm(最好是没有boost 的)?
来自 libc 的 timesub 这样的实现将是我的最后手段: http://www.opensource.apple.com/source/Libc/Libc-262/stdtime/localtime.c
编辑:在阅读建议的链接并进行更多研究后,我得出以下结论:
值得注意的是,Boost.Date_Time可以是仅限标头的库。资源: http://www.boost.org/doc/libs/1_53_0/more/getting_started/unix-variants.html#header-only-libraries
答案 0 :(得分:23)
使用更好的算法更新答案,链接到算法的详细说明,并完成转换为std::tm
。
我想打印或提取年/月/日值。 是否有一种简单的方法可以从time_point转换为tm(最好是 没有提升)?
首先要注意的是,std::chrono::time_point
不仅会在duration
上模仿,还会在时钟上模板化。时钟意味着一个时代。不同的时钟可以有不同的时代。
例如,在我的系统上,std::chrono::high_resolution_clock
和std::chrono::steady_clock
有一个时代:每当计算机启动时。如果您不知道计算机启动的时间,则无法将time_point
转换为任何日历系统。
话虽如此,你可能正在谈论std::chrono::system_clock::time_point
,因为time_point
,只有这个time_point
,需要与民间(格里高利)日历有确定的关系
事实证明,我所知道的std::chrono::system_clock
的每个实现都在使用unix time。这有一个1970年的新纪元,忽视了闰秒。
标准不保证这一点。但是,如果您希望使用以下公式,则可以利用此事实:
首先,警告,我正在使用最新的C ++ 1y草案,其中包括很棒的新constexpr
工具。如果您需要为编译器取消一些constexpr
属性,请执行此操作。
根据上述链接中找到的算法,您可以将std::chrono::time_point<std::chrono::system_clock, Duration>
转换为std::tm
,而无需使用time_t
以下函数:
template <class Duration>
std::tm
make_utc_tm(std::chrono::time_point<std::chrono::system_clock, Duration> tp)
{
using namespace std;
using namespace std::chrono;
typedef duration<int, ratio_multiply<hours::period, ratio<24>>> days;
// t is time duration since 1970-01-01
Duration t = tp.time_since_epoch();
// d is days since 1970-01-01
days d = round_down<days>(t);
// t is now time duration since midnight of day d
t -= d;
// break d down into year/month/day
int year;
unsigned month;
unsigned day;
std::tie(year, month, day) = civil_from_days(d.count());
// start filling in the tm with calendar info
std::tm tm = {0};
tm.tm_year = year - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = day;
tm.tm_wday = weekday_from_days(d.count());
tm.tm_yday = d.count() - days_from_civil(year, 1, 1);
// Fill in the time
tm.tm_hour = duration_cast<hours>(t).count();
t -= hours(tm.tm_hour);
tm.tm_min = duration_cast<minutes>(t).count();
t -= minutes(tm.tm_min);
tm.tm_sec = duration_cast<seconds>(t).count();
return tm;
}
另请注意,所有现有实施中的std::chrono::system_clock::time_point
都是UTC(忽略闰秒)时区的持续时间。如果您想使用其他时区转换time_point
,则需要在将时区的持续时间偏移量转换为精确度为std::chrono::system_clock::time_point
之前将其加/减到days
。如果您还想考虑闰秒,请使用this table在截断前调整适当的秒数days
,并知道unix时间与UTC 对齐
可以通过以下方式验证此功能:
#include <iostream>
#include <iomanip>
void
print_tm(const std::tm& tm)
{
using namespace std;
cout << tm.tm_year+1900;
char fill = cout.fill();
cout << setfill('0');
cout << '-' << setw(2) << tm.tm_mon+1;
cout << '-' << setw(2) << tm.tm_mday;
cout << ' ';
switch (tm.tm_wday)
{
case 0:
cout << "Sun";
break;
case 1:
cout << "Mon";
break;
case 2:
cout << "Tue";
break;
case 3:
cout << "Wed";
break;
case 4:
cout << "Thu";
break;
case 5:
cout << "Fri";
break;
case 6:
cout << "Sat";
break;
}
cout << ' ';
cout << ' ' << setw(2) << tm.tm_hour;
cout << ':' << setw(2) << tm.tm_min;
cout << ':' << setw(2) << tm.tm_sec << " UTC.";
cout << setfill(fill);
cout << " This is " << tm.tm_yday << " days since Jan 1\n";
}
int
main()
{
print_tm(make_utc_tm(std::chrono::system_clock::now()));
}
目前我打印出来的是:
2013-09-15太阳18:16:50 UTC这是自1月1日以来的257天
如果chrono-Compatible Low-Level Date Algorithms离线或被移动,以下是make_utc_tm
中使用的算法。在上面的链接中有对这些算法的深入解释。它们经过了充分的测试,具有非常大的有效性。
// Returns number of days since civil 1970-01-01. Negative values indicate
// days prior to 1970-01-01.
// Preconditions: y-m-d represents a date in the civil (Gregorian) calendar
// m is in [1, 12]
// d is in [1, last_day_of_month(y, m)]
// y is "approximately" in
// [numeric_limits<Int>::min()/366, numeric_limits<Int>::max()/366]
// Exact range of validity is:
// [civil_from_days(numeric_limits<Int>::min()),
// civil_from_days(numeric_limits<Int>::max()-719468)]
template <class Int>
constexpr
Int
days_from_civil(Int y, unsigned m, unsigned d) noexcept
{
static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<Int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer");
y -= m <= 2;
const Int era = (y >= 0 ? y : y-399) / 400;
const unsigned yoe = static_cast<unsigned>(y - era * 400); // [0, 399]
const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1; // [0, 365]
const unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096]
return era * 146097 + static_cast<Int>(doe) - 719468;
}
// Returns year/month/day triple in civil calendar
// Preconditions: z is number of days since 1970-01-01 and is in the range:
// [numeric_limits<Int>::min(), numeric_limits<Int>::max()-719468].
template <class Int>
constexpr
std::tuple<Int, unsigned, unsigned>
civil_from_days(Int z) noexcept
{
static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<Int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer");
z += 719468;
const Int era = (z >= 0 ? z : z - 146096) / 146097;
const unsigned doe = static_cast<unsigned>(z - era * 146097); // [0, 146096]
const unsigned yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399]
const Int y = static_cast<Int>(yoe) + era * 400;
const unsigned doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365]
const unsigned mp = (5*doy + 2)/153; // [0, 11]
const unsigned d = doy - (153*mp+2)/5 + 1; // [1, 31]
const unsigned m = mp + (mp < 10 ? 3 : -9); // [1, 12]
return std::tuple<Int, unsigned, unsigned>(y + (m <= 2), m, d);
}
template <class Int>
constexpr
unsigned
weekday_from_days(Int z) noexcept
{
return static_cast<unsigned>(z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6);
}
template <class To, class Rep, class Period>
To
round_down(const std::chrono::duration<Rep, Period>& d)
{
To t = std::chrono::duration_cast<To>(d);
if (t > d)
--t;
return t;
}
<强>更新强>
最近,我将上述算法包装到一个免费提供的日期/时间库documented and available here中。这个库可以很容易地从std::system_clock::time_point
中提取年/月/日,甚至几小时:分钟:秒:小数秒。所有这些都没有通过time_t
。
这是一个简单的程序,使用上面的仅标题库来打印UTC时区中的当前日期和时间,以及system_clock::time_point
提供的精度(在这种情况下为微秒):
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std;
using namespace std::chrono;
auto const now = system_clock::now();
auto const dp = time_point_cast<days>(now);
auto const date = year_month_day(dp);
auto const time = make_time(now-dp);
cout << date << ' ' << time << " UTC\n";
}
哪个只为我输出:
2015-05-19 15:03:47.754002 UTC
此库有效地将std::chrono::system_clock::time_point
转换为易于使用的日期时间类型。
答案 1 :(得分:3)
除了基于time_t
的C库函数之外,标准库中没有任何内容可以支持日历日期。
选项按照我的偏好顺序排列:
gmtime()
答案 2 :(得分:1)
我使用Howard Hinnant's date library编写了一个从time_point
转换为struct tm
的函数:
template <typename Clock, typename Duration>
std::tm to_calendar_time(std::chrono::time_point<Clock, Duration> tp)
{
using namespace date;
auto date = floor<days>(tp);
auto ymd = year_month_day(date);
auto weekday = year_month_weekday(date).weekday_indexed().weekday();
auto tod = make_time(tp - date);
days daysSinceJan1 = date - sys_days(ymd.year()/1/1);
std::tm result;
std::memset(&result, 0, sizeof(result));
result.tm_sec = tod.seconds().count();
result.tm_min = tod.minutes().count();
result.tm_hour = tod.hours().count();
result.tm_mday = unsigned(ymd.day());
result.tm_mon = unsigned(ymd.month()) - 1u; // Zero-based!
result.tm_year = int(ymd.year()) - 1900;
result.tm_wday = unsigned(weekday);
result.tm_yday = daysSinceJan1.count();
result.tm_isdst = -1; // Information not available
return result;
}
这有效地绕过time_t
在32位系统上潜伏的Y2038问题。这个函数已经贡献给了这个GitHub wiki,我希望其他人可以提供其他有用的例子和食谱。