C++ std::mktime 和时区信息
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/530519/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
std::mktime and timezone info
提问by VolkA
I'm trying to convert a time info I reveive as a UTC string to a timestamp using std::mktime
in C++. My problem is that in <ctime>
/ <time.h>
there is no function to convert to UTC; mktime will only return the timestamp as local time.
我正在尝试将我作为 UTC 字符串显示的时间信息转换为std::mktime
C++ 中使用的时间戳。我的问题是在<ctime>
/<time.h>
中没有转换为 UTC 的功能;mktime 只会返回时间戳作为本地时间。
So I need to figure out the timezone offset and take it into account, but I can't find a platform-independent way that doesn't involve porting the whole code to boost::date_time
. Is there some easy solution which I have overlooked?
所以我需要找出时区偏移量并将其考虑在内,但我找不到不涉及将整个代码移植到boost::date_time
. 是否有一些我忽略的简单解决方案?
采纳答案by Anonymous
mktime assumes that the date value is in the local time zone. Thus you can change the timezone environment variable beforehand (setenv) and get the UTC timezone.
mktime 假定日期值在本地时区。因此,您可以预先更改时区环境变量 (setenv) 并获取 UTC 时区。
Can also try looking at various home-made utc-mktimes, mktime-utcs, etc.
也可以试试看各种自制的utc-mktimes、mktime-utcs等。
回答by Serg
timestamp = mktime(&tm) - _timezone;
or platform independent way:
或平台独立的方式:
timestamp = mktime(&tm) - timezone;
If you look in the source of mktime():
如果您查看 mktime() 的来源:
http://www.raspberryginger.com/jbailey/minix/html/mktime_8c-source.html
http://www.raspberryginger.com/jbailey/minix/html/mktime_8c-source.html
in line 00117 the time converted to local time:
在第 00117 行,时间转换为本地时间:
seconds += _timezone;
回答by Daniel N?slund
mktime() uses tzname for detecting timezone. tzset()initializes the tzname variable from the TZ enviroment variable. If the TZ variable appears in the enviroment but its value is empty or its value cannot be correctly interpreted, UTC is used.
mktime() 使用 tzname 来检测时区。tzset()从 TZ 环境变量初始化 tzname 变量。如果环境中出现 TZ 变量但其值为空或无法正确解释其值,则使用 UTC。
A portable (not threadsafe) version according to the timegm manpage
根据timegm 联机帮助页的可移植(非线程安全)版本
#include <time.h>
#include <stdlib.h>
time_t
my_timegm(struct tm *tm)
{
time_t ret;
char *tz;
tz = getenv("TZ");
setenv("TZ", "", 1);
tzset();
ret = mktime(tm);
if (tz)
setenv("TZ", tz, 1);
else
unsetenv("TZ");
tzset();
return ret;
}
Eric S Raymond has a threadsafe version published in his article Time, Clock, and Calendar Programming In C
Eric S Raymond 在他的文章Time, Clock, and Calendar Programming In C 中发表了一个线程安全版本
time_t my_timegm(register struct tm * t)
/* struct tm to seconds since Unix epoch */
{
register long year;
register time_t result;
#define MONTHSPERYEAR 12 /* months per calendar year */
static const int cumdays[MONTHSPERYEAR] =
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
/*@ +matchanyintegral @*/
year = 1900 + t->tm_year + t->tm_mon / MONTHSPERYEAR;
result = (year - 1970) * 365 + cumdays[t->tm_mon % MONTHSPERYEAR];
result += (year - 1968) / 4;
result -= (year - 1900) / 100;
result += (year - 1600) / 400;
if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0) &&
(t->tm_mon % MONTHSPERYEAR) < 2)
result--;
result += t->tm_mday - 1;
result *= 24;
result += t->tm_hour;
result *= 60;
result += t->tm_min;
result *= 60;
result += t->tm_sec;
if (t->tm_isdst == 1)
result -= 3600;
/*@ -matchanyintegral @*/
return (result);
}
回答by fgrieu
Here is a simple, tested, hopefully portable piece of code converting from struct tm
to seconds since the beginning of an adjustable UTC year, without temporary change of time zone.
这是一段简单的、经过测试的、希望可移植的代码,从struct tm
可调整的 UTC 年份开始转换为秒,没有临时更改时区。
// Conversion from UTC date to second, signed 64-bit adjustable epoch version.
// Written by Fran?ois Grieu, 2015-07-21; public domain.
#include <time.h> // needed for struct tm
#include <stdint.h> // needed for int_least64_t
#define MY_EPOCH 1970 // epoch year, changeable
typedef int_least64_t my_time_t; // type for seconds since MY_EPOCH
// my_mktime converts from struct tm UTC to non-leap seconds since
// 00:00:00 on the first UTC day of year MY_EPOCH (adjustable).
// It works since 1582 (start of Gregorian calendar), assuming an
// apocryphal extension of Coordinated Universal Time, until some
// event (like celestial impact) deeply messes with Earth.
// It strive to be strictly C99-conformant.
//
// input: Pointer to a struct tm with field tm_year, tm_mon, tm_mday,
// tm_hour, tm_min, tm_sec set per mktime convention; thus
// - tm_year is year minus 1900;
// - tm_mon is [0..11] for January to December, but [-2..14]
// works for November of previous year to February of next year;
// - tm_mday, tm_hour, tm_min, tm_sec similarly can be offset to
// the full range [-32767 to 32767].
// output: Number of non-leap seconds since beginning of the first UTC
// day of year MY_EPOCH, as a signed at-least-64-bit integer.
// The input is not changed (in particular, fields tm_wday,
// tm_yday, and tm_isdst are unchanged and ignored).
my_time_t my_mktime(const struct tm * ptm) {
int m, y = ptm->tm_year+2000;
if ((m = ptm->tm_mon)<2) { m += 12; --y; }
// compute number of days within constant, assuming appropriate origin
#define MY_MKTIME(Y,M,D) ((my_time_t)Y*365+Y/4-Y/100*3/4+(M+2)*153/5+D)
return ((( MY_MKTIME( y , m, ptm->tm_mday)
-MY_MKTIME((MY_EPOCH+99), 12, 1 )
)*24+ptm->tm_hour)*60+ptm->tm_min)*60+ptm->tm_sec;
#undef MY_MKTIME // this macro is private
}
Key observations allowing great simplification compared to the code in thisand thatanswers:
- numbering months from March, all months except the one before that origin repeat with a cycle of 5 months totaling 153 days alternating 31 and 30 days, so that, for anymonth, and without consideration for leap years, the number of days since the previous February can be computed (within a constant) using addition of an appropriate constant, multiplication by 153 and integer division by 5;
- the correction in days accounting for the rule for leap year on years multiple-of-100 (which by exception to the multiple-of-4 rules are non-leap except if multiple of 400) can be computed (within a constant) by addition of an appropriate constant, integer division by 100, multiplication by 3, and integer division by 4;
- we can compute correction for any epoch using the same formula we use in the main computation, and can do this with a macro so that this correction is computed at compilation time.
- 从 3 月开始编号,除原点之前的月份外,所有月份都以 5 个月的周期重复,总共 153 天,交替 31 天和 30 天,因此,对于任何月份,不考虑闰年,自上一个月份以来的天数二月可以使用适当的常数相加、乘以 153 和整数除以 5 来计算(在一个常数内);
- 计算闰年规则的天数修正为 100 的倍数(除 4 的倍数规则外,除非是 400 的倍数,否则为非闰年)可以通过加法计算(在常数范围内)一个适当的常数,整数除以 100,乘以 3,整数除以 4;
- 我们可以使用我们在主计算中使用的相同公式来计算任何时期的校正,并且可以使用宏来执行此操作,以便在编译时计算此校正。
Here is another version not requiring 64-bit support, locked to 1970 origin.
这是另一个不需要 64 位支持的版本,锁定到 1970 年的起源。
// Conversion from UTC date to second, unsigned 32-bit Unix epoch version.
// Written by Fran?ois Grieu, 2015-07-21; public domain.
#include <time.h> // needed for struct tm
#include <limits.h> // needed for UINT_MAX
#if UINT_MAX>=0xFFFFFFFF // unsigned is at least 32-bit
typedef unsigned my_time_t; // type for seconds since 1970
#else
typedef unsigned long my_time_t; // type for seconds since 1970
#endif
// my_mktime converts from struct tm UTC to non-leap seconds since
// 00:00:00 on the first UTC day of year 1970 (fixed).
// It works from 1970 to 2105 inclusive. It strives to be compatible
// with C compilers supporting // comments and claiming C89 conformance.
//
// input: Pointer to a struct tm with field tm_year, tm_mon, tm_mday,
// tm_hour, tm_min, tm_sec set per mktime convention; thus
// - tm_year is year minus 1900
// - tm_mon is [0..11] for January to December, but [-2..14]
// works for November of previous year to February of next year
// - tm_mday, tm_hour, tm_min, tm_sec similarly can be offset to
// the full range [-32767 to 32768], as long as the combination
// with tm_year gives a result within years [1970..2105], and
// tm_year>0.
// output: Number of non-leap seconds since beginning of the first UTC
// day of year 1970, as an unsigned at-least-32-bit integer.
// The input is not changed (in particular, fields tm_wday,
// tm_yday, and tm_isdst are unchanged and ignored).
my_time_t my_mktime(const struct tm * ptm) {
int m, y = ptm->tm_year;
if ((m = ptm->tm_mon)<2) { m += 12; --y; }
return ((( (my_time_t)(y-69)*365u+y/4-y/100*3/4+(m+2)*153/5-446+
ptm->tm_mday)*24u+ptm->tm_hour)*60u+ptm->tm_min)*60u+ptm->tm_sec;
}
回答by Brian Schlenker
If you are trying to do this in a multithreaded program and don't want to deal with locking and unlocking mutexes (if you use the environment variable method you'd have to), there is a function called timegm that does this. It isn't portable, so here is the source: http://trac.rtmpd.com/browser/trunk/sources/common/src/platform/windows/timegm.cpp
如果您尝试在多线程程序中执行此操作并且不想处理锁定和解锁互斥锁(如果您使用环境变量方法,则必须这样做),有一个名为 timegm 的函数可以执行此操作。它不可移植,所以这里是来源:http: //trac.rtmpd.com/browser/trunk/sources/common/src/platform/windows/timegm.cpp
int is_leap(unsigned y) {
y += 1900;
return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0);
}
time_t timegm (struct tm *tm)
{
static const unsigned ndays[2][12] = {
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
time_t res = 0;
int i;
for (i = 70; i < tm->tm_year; ++i)
res += is_leap(i) ? 366 : 365;
for (i = 0; i < tm->tm_mon; ++i)
res += ndays[is_leap(tm->tm_year)][i];
res += tm->tm_mday - 1;
res *= 24;
res += tm->tm_hour;
res *= 60;
res += tm->tm_min;
res *= 60;
res += tm->tm_sec;
return res;
}
回答by paipeng
I have this same problem last day and by searching the doc "man mktime":
昨天我遇到了同样的问题,通过搜索文档“man mktime”:
The functions mktime() and timegm() convert the broken-out time (in the structure pointed to by *timeptr) into a time value with the same encoding as that of the values returned by the time(3) function (that is, seconds from the Epoch, UTC). The mktime() function interprets the input structure according to the current timezone setting (see tzset(3)). The timegm() function interprets the input structure as representing Universal Coordinated Time (UTC).
函数 mktime() 和 timegm() 将中断时间(在 *timeptr 指向的结构中)转换为与 time(3) 函数返回的值具有相同编码的时间值(即,从纪元开始的秒数,UTC)。mktime() 函数根据当前时区设置解释输入结构(请参阅 tzset(3))。timegm() 函数将输入结构解释为表示世界协调时间 (UTC)。
Short:
短的:
You should use timegm, instead of using mktime.
您应该使用 timegm,而不是使用 mktime。
Regards,
问候,
Pai
排
回答by Martin York
The tm structure used by mktime has a timezone field.
What happens if you put 'UTC' into the timzone field?
mktime 使用的 tm 结构有一个 timezone 字段。
如果将“UTC”放入 timzone 字段会发生什么?
回答by James
I've just been trying to figure out how to do this. I'm not convinced this solution is perfect (it depends on how accurately the runtime library calculates Daylight Savings), but it's working pretty well for my problem.
我只是想弄清楚如何做到这一点。我不相信这个解决方案是完美的(这取决于运行时库计算夏令时的准确程度),但它对我的问题非常有效。
Initially I thought I could just calculate the difference between gmtime
and localtime
, and add that on to my converted timestamp, but that doesn't work because the difference will change according to the time of year that the code is run, and if your source time is in the other half of the year you'll be out by an hour.
起初,我想我可能只是计算差值之间gmtime
和localtime
,并添加到我的转换后的时间戳,但不起作用,因为差异将根据该代码运行一年的时间而改变,如果你的源时间在一年的另一半里,你会离开一个小时。
So, the trick is to get the runtime library to calculate the difference between UTC and local time for the time you're trying to convert.
因此,诀窍是让运行时库计算您尝试转换的时间的 UTC 和本地时间之间的差异。
So what I'm doing is calculating my input time and then modifying that calculated time by plugging it back into localtime
and gmtime
and adding the difference of those two functions:
所以,我做的是我的计算输入时间,然后通过将其放回修改计算什么时间localtime
和gmtime
和增加的这两个函数的区别:
std::tm tm;
// Fill out tm with your input time.
std::time_t basetime = std::mktime( &tm );
std::time_t diff;
tm = *std::localtime( &basetime );
tm.tm_isdst = -1;
diff = std::mktime( &tm );
tm = *std::gmtime( &basetime );
tm.tm_isdst = -1;
diff -= std::mktime( &tm );
std::time_t finaltime = basetime + diff;
It's a bit of a roundabout way to calculate this, but I couldn't find any other way without resorting to helper libraries or writing my own conversion function.
计算这个有点迂回,但是如果不求助于帮助库或编写自己的转换函数,我就找不到任何其他方法。
回答by Sergey D
The easy platform-independent way to convert UTC time from string to a timestamp is to use your own timegm
.
将 UTC 时间从字符串转换为时间戳的与平台无关的简单方法是使用您自己的timegm
.
Using mktime
and manipulating timezone environment variables depends on correctly installed and configured TZ database. In one case some timezone links were incorrectly configured (likely side effect of trying different time server packages) which caused mktime-based algorithm to fail on that machine depending on the selected timezone andthe time.
使用mktime
和操作时区环境变量取决于正确安装和配置的 TZ 数据库。在一种情况下,某些时区链接配置不正确(可能是尝试不同时间服务器包的副作用)导致基于 mktime 的算法在该机器上失败,具体取决于所选的时区和时间。
Trying to solve this problem with mktime
without changing timezone is a dead end because string time (treated as local time) cannot be correctly resolved around the time when your local clock is set back one hour to turn off DST - the same string will match two points in time.
尝试在mktime
不更改时区的情况下解决此问题是一个死胡同,因为当您的本地时钟被设置回一小时以关闭 DST 时,字符串时间(视为本地时间)无法正确解析 - 同一字符串将匹配两个点及时。
// Algorithm: http://howardhinnant.github.io/date_algorithms.html
inline int days_from_civil(int y, int m, int d) noexcept
{
y -= m <= 2;
int era = y / 400;
int yoe = y - era * 400; // [0, 399]
int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; // [0, 365]
int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // [0, 146096]
return era * 146097 + doe - 719468;
}
// Converts a broken-down time structure with UTC time to a simple time representation.
// It does not modify broken-down time structure as BSD timegm() does.
time_t timegm_const(std::tm const* t)
{
int year = t->tm_year + 1900;
int month = t->tm_mon; // 0-11
if (month > 11)
{
year += month / 12;
month %= 12;
}
else if (month < 0)
{
int years_diff = (11 - month) / 12;
year -= years_diff;
month += 12 * years_diff;
}
int days_since_epoch = days_from_civil(year, month + 1, t->tm_mday);
return 60 * (60 * (24L * days_since_1970 + t->tm_hour) + t->tm_min) + t->tm_sec;
}
This solution is free from external dependencies, threadsafe, portable and fast. Let me know if you can find any issues with the code.
该解决方案不受外部依赖、线程安全、可移植且快速。如果您发现代码有任何问题,请告诉我。