C++ 将 struct tm(以 UTC 表示)转换为 time_t 类型的简单方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/283166/
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
Easy way to convert a struct tm (expressed in UTC) to time_t type
提问by user24560
How do I do the above? There is mktime function but that treats the input as expressed in local time but how do i perform the conversion if my input tm variable happens to be in UTC.
我如何做到以上几点?有 mktime 函数,但是它将输入视为以本地时间表示,但是如果我的输入 tm 变量恰好在 UTC 中,我该如何执行转换。
采纳答案by Martin York
Use timegm() instead of mktime()
使用 timegm() 而不是 mktime()
回答by hobo
for those on windows, the below function is available:
对于 Windows 上的用户,可以使用以下功能:
_mkgmtime
link for more info: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/mkgmtime-mkgmtime32-mkgmtime64
更多信息链接:https: //docs.microsoft.com/en-us/cpp/c-runtime-library/reference/mkgmtime-mkgmtime32-mkgmtime64
回答by DTiedy
Here is a solution I use (Can't recall where I found it) when it isn't a windows platform
这是我在不是 Windows 平台时使用的解决方案(不记得在哪里找到的)
time_t _mkgmtime(const struct tm *tm)
{
// Month-to-day offset for non-leap-years.
static const int month_day[12] =
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
// Most of the calculation is easy; leap years are the main difficulty.
int month = tm->tm_mon % 12;
int year = tm->tm_year + tm->tm_mon / 12;
if (month < 0) { // Negative values % 12 are still negative.
month += 12;
--year;
}
// This is the number of Februaries since 1900.
const int year_for_leap = (month > 1) ? year + 1 : year;
time_t rt = tm->tm_sec // Seconds
+ 60 * (tm->tm_min // Minute = 60 seconds
+ 60 * (tm->tm_hour // Hour = 60 minutes
+ 24 * (month_day[month] + tm->tm_mday - 1 // Day = 24 hours
+ 365 * (year - 70) // Year = 365 days
+ (year_for_leap - 69) / 4 // Every 4 years is leap...
- (year_for_leap - 1) / 100 // Except centuries...
+ (year_for_leap + 299) / 400))); // Except 400s.
return rt < 0 ? -1 : rt;
}
回答by Tom
timegm()
works, but is not present on all systems.
timegm()
有效,但并非在所有系统上都存在。
Here's a version that only uses ANSI C. (EDIT: not strictly ANSI C! I'm doing math on time_t, assuming that the units are in seconds since the epoch. AFAIK, the standard does not define the units of time_t.) Note, it makes use of a hack, so-to-speak, to determine the machine's time zone and then adjusts the result from mktime accordingly.
这是一个仅使用 ANSI C 的版本。(编辑:不是严格的 ANSI C!我正在对 time_t 进行数学运算,假设单位是自纪元以来的秒数。AFAIK,标准没有定义 time_t 的单位。) ,它利用黑客技术来确定机器的时区,然后相应地调整 mktime 的结果。
/*
returns the utc timezone offset
(e.g. -8 hours for PST)
*/
int get_utc_offset() {
time_t zero = 24*60*60L;
struct tm * timeptr;
int gmtime_hours;
/* get the local time for Jan 2, 1900 00:00 UTC */
timeptr = localtime( &zero );
gmtime_hours = timeptr->tm_hour;
/* if the local time is the "day before" the UTC, subtract 24 hours
from the hours to get the UTC offset */
if( timeptr->tm_mday < 2 )
gmtime_hours -= 24;
return gmtime_hours;
}
/*
the utc analogue of mktime,
(much like timegm on some systems)
*/
time_t tm_to_time_t_utc( struct tm * timeptr ) {
/* gets the epoch time relative to the local time zone,
and then adds the appropriate number of seconds to make it UTC */
return mktime( timeptr ) + get_utc_offset() * 3600;
}
回答by liberforce
The answer of Loki Astari was a good start, timegm
is one of the possible solutions. However, the man page of timegm
gives a portable version of it, as timegm
is not POSIX-compliant. Here it is:
Loki Astari 的回答是一个好的开始,timegm
也是可能的解决方案之一。但是,手册页timegm
提供了它的便携版本,因为timegm
它不符合 POSIX 标准。这里是:
#include <time.h>
#include <stdlib.h>
time_t
my_timegm(struct tm *tm)
{
time_t ret;
char *tz;
tz = getenv("TZ");
if (tz)
tz = strdup(tz);
setenv("TZ", "", 1);
tzset();
ret = mktime(tm);
if (tz) {
setenv("TZ", tz, 1);
free(tz);
} else
unsetenv("TZ");
tzset();
return ret;
}
回答by Leo Accend
The following implementation of timegm(1)
works swimmingly on Android, and probably works on other Unix variants as well:
以下实现timegm(1)
在 Android 上运行良好,并且可能也适用于其他 Unix 变体:
time_t timegm( struct tm *tm ) {
time_t t = mktime( tm );
return t + localtime( &t )->tm_gmtoff;
}
回答by Arran Cudbard-Bell
POSIX page for tzset, describes global variable extern long timezone
which contains the local timezone as an offset of seconds from UTC. This will be present on all POSIX compliant systems.
tzset 的POSIX 页面,描述了extern long timezone
包含本地时区作为与 UTC 的秒偏移量的全局变量。这将出现在所有符合 POSIX 的系统上。
In order for timezone to contain the correct value, you will likely need to call tzset()
during your program's initialization.
为了使时区包含正确的值,您可能需要tzset()
在程序初始化期间调用。
You can then just add timezone
to the output of mktime
to get the output in UTC.
然后,您可以添加timezone
到 的输出中mktime
以获取 UTC 格式的输出。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
time_t utc_mktime(struct tm *t)
{
return mktime(t) + timezone;
}
int main(int argc, char **argv)
{
struct tm t = { 0 };
tzset();
utc_mktime(&t);
}
Note: Technically tzset()
and mktime()
aren't guaranteed to be threadsafe.
注意:从技术上讲tzset()
,mktime()
并不能保证是线程安全的。
If a thread accesses tzname, [XSI] [Option Start] daylight, or timezone [Option End] directly while another thread is in a call to tzset(), or to any function that is required or allowed to set timezone information as if by calling tzset(), the behavior is undefined.
如果一个线程直接访问 tzname、[XSI] [Option Start] daylight 或 timezone [Option End] 而另一个线程正在调用 tzset() 或任何需要或允许设置时区信息的函数,就像通过调用 tzset(),行为未定义。
...but the majority of implementations are. GNU C uses mutexes in tzset()
to avoid concurrent modifications to the global variables it sets, and mktime()
sees very wide use in threaded programs without synchronization. I suspect if one were to encounter side effects, it would be from using setenv()
to alter the value of TZ
as done in the answer from @liberforce.
...但大多数实现是。GNU C 使用互斥锁tzset()
来避免对其设置的全局变量进行并发修改,并且mktime()
在没有同步的线程程序中得到了非常广泛的使用。我怀疑如果有人会遇到副作用,那将是使用setenv()
来改变TZ
@liberforce 的答案中所做的值。
回答by Bo Tian
I was troubled by the issue of mktime() as well. My solution is the following
我也被 mktime() 的问题困扰。我的解决方案如下
time_t myTimegm(std::tm * utcTime)
{
static std::tm tmv0 = {0, 0, 0, 1, 0, 80, 0, 0, 0}; //1 Jan 1980
static time_t utcDiff = std::mktime(&tmv0) - 315532801;
return std::mktime(utcTime) - utcDiff;
}
The idea is to get the time difference by calling std::mktime() with a known time (in this case 1980/01/01) and subtract its timestamp (315532801). Hope it helps.
这个想法是通过使用已知时间(在本例中为 1980/01/01)调用 std::mktime() 并减去其时间戳 (315532801) 来获得时间差。希望能帮助到你。
回答by Dana
This is really a comment with code to address the answer by Leo Accend: Try the following:
这实际上是一条注释,其中包含解决 Leo Accend 答案的代码:请尝试以下操作:
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
/*
* A bit of a hack that lets you pull DST from your Linux box
*/
time_t timegm( struct tm *tm ) { // From Leo's post, above
time_t t = mktime( tm );
return t + localtime( &t )->tm_gmtoff;
}
main()
{
struct timespec tspec = {0};
struct tm tm_struct = {0};
if (gettimeofday(&tspec, NULL) == 0) // clock_gettime() is better but not always avail
{
tzset(); // Not guaranteed to be called during gmtime_r; acquire timezone info
if (gmtime_r(&(tspec.tv_sec), &tm_struct) == &tm_struct)
{
printf("time represented by original utc time_t: %s\n", asctime(&tm_struct));
// Go backwards from the tm_struct to a time, to pull DST offset.
time_t newtime = timegm (&tm_struct);
if (newtime != tspec.tv_sec) // DST offset detected
{
printf("time represented by new time_t: %s\n", asctime(&tm_struct));
double diff = difftime(newtime, tspec.tv_sec);
printf("DST offset is %g (%f hours)\n", diff, diff / 3600);
time_t intdiff = (time_t) diff;
printf("This amounts to %s\n", asctime(gmtime(&intdiff)));
}
}
}
exit(0);
}