strtm(UTC로 표현)을 time_t 타입으로 변환하는 쉬운 방법
위의 작업은 어떻게 합니까?mktime 함수가 있지만 그것은 입력을 현지 시간으로 표현한 것으로 처리하지만 만약 내 입력 tm 변수가 UTC에 있을 경우 변환을 어떻게 수행해야 합니까?
사용하다timegm()mktime()
- 와 같이 것은 @chux - Reinstate Monica 입니다.time_t timegm(struct tm *timeptr)는 C23 표준에 추가되는 것으로 간주됩니다(따라서 C++ 표준에 포함됨).
윈도우에 있는 경우 다음 기능을 사용할 수 있습니다.
_mkgmtime
자세한 정보를 보려면 링크: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/mkgmtime-mkgmtime32-mkgmtime64
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;
}
이었습니다.timegm가능한 해결책 중 하나입니다.의 맨 .timegm다로 합니다.timegmPOSIX와 호환되지 않습니다.여기 있습니다.
#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;
}
timegm()작동하지만 모든 시스템에 존재하지는 않습니다.
여기 ANSIC만 사용하는 버전이 있습니다. (EDIT: 엄밀하게 ANSIC은 아닙니다! 저는 단위가 시대 이후 초 단위라고 가정하고 time_t에서 수학을 하고 있습니다.AFAIK, 표준은 time_t의 단위를 정의하지 않습니다.)참고로 말하자면, 기계의 시간대를 결정하기 위해 hack을 사용하고 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;
}
과 같은 :timegm(1)Android에서도 잘 작동하며, 다른 Unix 변종에서도 작동할 수 있습니다.
time_t timegm( struct tm *tm ) {
time_t t = mktime( tm );
return t + localtime( &t )->tm_gmtoff;
}
C++20 크로노는 이 작업을 매우 사소하고 매우 효율적으로 만들기 때문에 오래된 질문에 대한 새로운 답변입니다.
- 나사산 안전장치.
- 로컬 UTC 오프셋을 포함하지 않습니다.
- 크로노 구현 내에서도 반복되지 않습니다.
#include <chrono>
#include <ctime>
std::time_t
my_timegm(std::tm const& t)
{
using namespace std::chrono;
return system_clock::to_time_t(
sys_days{year{t.tm_year+1900}/(t.tm_mon+1)/t.tm_mday} +
hours{t.tm_hour} + minutes{t.tm_min} + seconds{t.tm_sec});
}
<chrono>다시는 C timing API를 다룰 필요가 없도록 설계되었습니다.이 이 해야 할 <chrono>그것도 쉽게 만들 수 있습니다.
업데이트:
아래 첫 번째 의견에 대한 답변:
year{t.tm_year+1900}/(t.tm_mon+1)/t.tm_mday다를 .{year, month, day}라고 하는 year_month_day . , 을 되지 않습니다.year_month_day의 필드를 합니다.
에 에.year_month_day됩니다이라는 됩니다.sys_days 는입니다.time_point에 기반을 둔system_clockdays. 이것은 1970-01-01의 Unix Time 시대 이후의 일수를 나타냅니다.이 변환은 링크에서 자세히 설명된 알고리즘을 사용합니다.알고리즘에는 루프가 없고, 좋은 옵티마이저를 사용하면 분기도 제거할 수 있습니다( -O3에서 clang을 사용하기도 가능합니다).
마지막으로 크로노는 필요한 모든 변환 요소(하루 수에 86400, 시간 수에 3600 등을 곱함)를 제공하여 날짜에 시간을 추가합니다.
는 는 입니다.time_point에 기반을 둔system_clockseconds가 알고 . . .system_clock::to_time_t다의 합니다.seconds그래서 그것은 a에 저장될 수 있습니다.time_t.
tzset에 대한 POSIX 페이지, 전역 변수 설명extern long timezoneUTC로부터 초 단위의 오프셋으로 로컬 시간대를 포함합니다.이는 모든 POSIX 호환 시스템에 적용됩니다.
에 해야 할 것 .tzset()당신의 프로그램 초기화 동안에.
만 .timezonemktimeUTC에서 출력을 얻을 수 있습니다.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
time_t utc_mktime(struct tm *t)
{
return (mktime(t) - timezone) - ((t->tm_isdst > 0) * 3600);
}
int main(int argc, char **argv)
{
struct tm t = { 0 };
tzset();
utc_mktime(&t);
}
: tzset()그리고.mktime() 안전하다고 보장되는 건 아닙니다
스레드가 tzname, [XSI] [Option Start] daylight 또는 timezone [Option End]에 직접 액세스하거나 다른 스레드가 tzset()을 호출하는 것처럼 시간대 정보를 설정해야 하거나 허용하는 모든 함수에 액세스하는 경우 동작이 정의되지 않습니다.
은...하지만 대부분의 구현은 그렇습니다.는 GNUC합니다.tzset()합니다.mktime()동기화 없이 스레드 프로그램에서 매우 광범위하게 사용할 수 있습니다.면,합니다를 하는 것이 setenv()TZ@liberforce의 답변에서와 같이.
저도 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;
}
이 아이디어는 알려진 시간(이 경우 1980/01/01)으로 std::mktime()을 호출하여 시간 차이를 구하고 타임스탬프(315532801)를 빼주는 것입니다.도움이 되길 바랍니다.
만을 으로 한 제은 이렇습니다. 이것은 전적으로 다음과 같습니다.time_t/tm 그것이수,정에 한 추정.time_t선형이라는 것입니다.
- 나은 .
tm가 아님,해야 함 변환합니다님 DST면;만 3함). 다음으로 변환합니다.time_t. - 를 합니다.
tm구조물입니다. 하지만 이번에는 UTC를 대표합니다. - 더 나은 지식에 반하는 척 하는 것은
tm더 을 컬우 DST하게는 함)조로 입니다.time_t한번 - 에.
time_t간 DST)다 UTC비에서 시간( 경우할 수 .time_t부대 단위 - 에 그
time_t결과는 UTC에서의 적절한 시간을 알려줍니다.
은 한 번 할 수 이 은 안전성 으로 인해 하는 문제를 일 수 있습니다. .gmtime.
(편집: 또한 오프셋을 계산하는 데 사용된 날짜와 변환할 날짜 사이에 시간대가 변경되면 문제가 발생할 수 있습니다.
tm tt;
// populate tt here
tt.tm_isdst = 0;
time_t tLoc = mktime(&tt);
tt = *gmtime(&tLoc);
tt.tm_isdst = 0;
time_t tRev = mktime(&tt);
time_t tDiff = tLoc - tRev;
time_t tUTC = tLoc + tDiff;
주의 이 TAI 을 : 이 TAI time_t(또는 윤초를 존중하는 다른 모든 것), 윤초 삽입에 가까운 시점에 적용할 경우 결과 시간이 1초씩 꺼질 수 있습니다.
이것은 정말로 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);
}
모든 시간대와 불가능하지는 않더라도 항상 극도로 어려울 것입니다.다양한 임의 시간대 및 일광 절약 시간(DST) 법령에 대한 정확한 기록이 필요합니다.때때로, 지방 당국이 누구인지 명확하지 않을 때도 있고, 무엇이 언제 규정되었는지는 신경 쓰지 않습니다.예를 들어, 대부분의 시스템은 업타임(타임 시스템이 업) 또는 부팅 시간(타임스탬프 시스템 부팅)에 대해 1초씩 중단됩니다.좋은 테스트는 한때 DST에 있었지만 지금은 그렇지 않은 날짜(또는 그 반대)일 것입니다.(미국에서 바뀐 것은 얼마 전입니다.)
에서 한 소스 :timegm():
https://sources.debian.org/src/tdb/1.2.1-2/libreplace/timegm.c/
static int is_leap(unsigned y)
{
y += 1900;
return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0);
}
time_t rep_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;
unsigned i;
if (tm->tm_mon > 12 ||
tm->tm_mon < 0 ||
tm->tm_mday > 31 ||
tm->tm_min > 60 ||
tm->tm_sec > 60 ||
tm->tm_hour > 24) {
/* invalid tm structure */
return 0;
}
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;
}
시간대 전환을 통한 테스트
int main()
{
struct tm utc = {};
utc.tm_year = 1972 - 1900;
utc.tm_mon = 1 - 1;
utc.tm_mday = 1;
time_t calendar = rep_timegm(&utc);
printf("is_leap: %d\n", is_leap(utc.tm_year));
printf("timegm: %ld\n", calendar);
assert(calendar == 63072000);
return 0;
}
언급URL : https://stackoverflow.com/questions/283166/easy-way-to-convert-a-struct-tm-expressed-in-utc-to-time-t-type
'codememo' 카테고리의 다른 글
| Long int 값을 C 단위로 인쇄 (0) | 2023.10.05 |
|---|---|
| 각도 5 FormGroup 재설정이 검증자를 재설정하지 않습니다. (0) | 2023.10.05 |
| Oracle에서 외부 키 가져오기 (0) | 2023.10.05 |
| MySQL은 왜 이러한 가능한 키를 사용하지 않습니까? (0) | 2023.10.05 |
| git 선택적으로 파일에서 로컬 변경사항 되돌리기 (0) | 2023.10.05 |