Как это сделать? Существует функция mktime, но она обрабатывает входные данные, выраженные в локальное время, но как я могу выполнить преобразование, если моя переменная ввода tm находится в UTC.
Легкий способ преобразования struct tm (выраженный в UTC) в тип time_t
Ответ 1
Используйте timegm() вместо mktime()
Ответ 2
для тех, кто находится в окнах, доступна следующая функция:
_mkgmtime
ссылка для получения дополнительной информации: https://msdn.microsoft.com/en-us/library/2093ets1.aspx
Ответ 3
Вот решение, которое я использую (не могу вспомнить, где я его нашел), когда это не платформа 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;
}
Ответ 4
timegm()
работает, но отсутствует во всех системах.
Здесь версия, использующая только ANSI C. (EDIT: не строго ANSI C! Я делаю математику по времени_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;
}
Ответ 5
Ответ Локи Астари был хорошим началом, 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;
}
Ответ 6
Следующая реализация timegm(1)
работает плавно на Android и, вероятно, работает и с другими вариантами Unix:
time_t timegm( struct tm *tm ) {
time_t t = mktime( tm );
return t + localtime( &t )->tm_gmtoff;
}
Ответ 7
Это действительно комментарий с кодом для ответа на вопрос 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 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);
}
Ответ 8
Страница 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(), поведение undefined.
... но большинство реализаций. GNU C использует мьютексы в tzset()
, чтобы избежать одновременных модификаций глобальных переменных, которые он устанавливает, и mktime()
видит очень широкое использование в поточных программах без синхронизации. Я подозреваю, что если кто-то столкнется с побочными эффектами, было бы использовать setenv()
для изменения значения TZ
, как это сделано в ответе от @liberforce.
Ответ 9
Меня тоже беспокоило проблема 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). Надеюсь, что это поможет.