Простой способ преобразовать struct tm (выраженную в UTC) в тип time_t - PullRequest
21 голосов
/ 12 ноября 2008

Как мне сделать выше? Есть функция mktime, но она обрабатывает входные данные как выраженные по местному времени, но как мне выполнить преобразование, если моя входная переменная tm находится в UTC.

Ответы [ 9 ]

22 голосов
/ 12 ноября 2008

Используйте timegm () вместо mktime ()

9 голосов
/ 01 апреля 2015

для тех, кто на окнах, доступна следующая функция:

_mkgmtime

ссылка для получения дополнительной информации: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/mkgmtime-mkgmtime32-mkgmtime64

7 голосов
/ 06 ноября 2015

Вот решение, которое я использую (не могу вспомнить, где я его нашел), когда это не платформа 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;
}
6 голосов
/ 29 января 2014

Следующая реализация timegm(1) прекрасно работает на Android и, вероятно, работает и на других вариантах Unix:

time_t timegm( struct tm *tm ) {
  time_t t = mktime( tm );
  return t + localtime( &t )->tm_gmtoff;
}
5 голосов
/ 08 марта 2013

Ответ Локи Астари был хорошим началом, 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;
}
5 голосов
/ 01 февраля 2012

timegm() работает, но присутствует не во всех системах.

Вот версия, которая использует только 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;

}

0 голосов
/ 29 декабря 2017

Меня также беспокоила проблема 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;
}

Идея состоит в том, чтобы получить разницу во времени, вызвав std :: mktime () с известным временем (в данном случае 1980/01/01) и вычесть его временную метку (315532801). Надеюсь, это поможет.

0 голосов
/ 08 марта 2017
Страница

POSIX для tzset описывает глобальную переменную extern long timezone, которая содержит местный часовой пояс в виде смещения секунд от UTC. Это будет присутствовать во всех POSIX-совместимых системах.

Чтобы часовой пояс содержал правильное значение, вам, вероятно, потребуется вызвать tzset() во время инициализации вашей программы.

Затем вы можете просто добавить 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);
}

Примечание. Технически tzset() и mktime() не гарантируют поточно-ориентированной .

Если поток обращается к tzname, [XSI] [Option Start] дневному свету или часовому поясу [Option End] напрямую, когда другой поток находится в вызове tzset (), или к любой функции, которой требуется или разрешено устанавливать информацию о часовом поясе как будто при вызове tzset () поведение не определено.

... но большинство реализаций есть. GNU C использует мьютексы в tzset(), чтобы избежать одновременных модификаций глобальных переменных, которые он устанавливает, а mktime() видит очень широкое применение в многопоточных программах без синхронизации. Я подозреваю, что если кто-то столкнется с побочными эффектами, он будет использовать setenv() для изменения значения TZ, как это было сделано в ответе @ liberforce.

0 голосов
/ 27 июня 2014

Это действительно комментарий с кодом для ответа на ответ Лео Аксенд: Попробуйте следующее:

#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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...