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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 14:20:19  来源:igfitidea点击:

Easy way to convert a struct tm (expressed in UTC) to time_t type

c++c

提问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, timegmis one of the possible solutions. However, the man page of timegmgives a portable version of it, as timegmis 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 timezonewhich 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 timezoneto the output of mktimeto 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 TZas 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);
}