【问题标题】:Unix C++: get time at a different zoneUnix C++:在不同的区域获取时间
【发布时间】:2012-12-12 12:15:48
【问题描述】:

我正在尝试使用 C++ 获取不同时区 (PST) 的时间。

#define PST (-8);
char* Time::getSecondSystemTime() {
    time_t rawtime;
    struct tm * timeinfo;
    char buffer[80];

    time(&rawtime);
    timeinfo = gmtime(&rawtime);

    timeinfo->tm_hour = timeinfo->tm_hour + PST;

    strftime(buffer, 80, "%I:%M %p", timeinfo);


    std::string temp = std::string(buffer); // to get rid of extra stuff
    std::string extraInfo = " Pacific Time ( US & Canada )";

    temp.append(extraInfo);

    return (char*) (temp.c_str());

}

这里的问题是,当 GMT 时间小于 8 小时时(例如,现在是凌晨 3 点),减去 8 小时是行不通的!

在 Unix 中获取不同时区时间的正确方法是什么?

【问题讨论】:

标签: c++ unix timezone unix-timestamp timezone-offset


【解决方案1】:

由于您说“UNIX”,所以它使用 TZ,但是,TZ=[what goes here] 您需要找出系统上的 [这里发生了什么]。它可能是“America/LosAngeles”或 PST 的其他几个字符串之一。 如果您的系统是 POSIX:保证 TZ=PST8PST 可以工作。但它可能不是最优的。

原始非生产代码假定 TZ 当前未使用。这是 C,而不是 C++,因为您的标签是 C:

setenv("TZ", "PST8PST", 1);   // set TZ
tzset();                // recognize TZ
time_t lt=time(NULL);   //epoch seconds
struct tm *p=localtime(&lt); // get local time struct tm
char tmp[80]={0x0};
strftime(tmp, 80, "%c", p);  // format time use format string, %c 
printf("time and date PST: %s\n", tmp); // display time and date
// you may or may not want to remove the TZ variable at this point.

【讨论】:

    【解决方案2】:

    我隐藏了以下 C 代码来处理该问题。效率并不是首先想到的词(两次调用setenv(),两次调用tzset()),但标准C 库并没有让做得更好:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    #include <unistd.h>
    
    static void time_convert(time_t t0, char const *tz_value)
    {
        char old_tz[64];
        strcpy(old_tz, getenv("TZ"));
        setenv("TZ", tz_value, 1);
        tzset();
        char new_tz[64];
        strcpy(new_tz, getenv("TZ"));
        char buffer[64];
        struct tm *lt = localtime(&t0);
        strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", lt);
        setenv("TZ", old_tz, 1);
        tzset();
        printf("%ld = %s (TZ=%s)\n", (long)t0, buffer, new_tz);
    }
    
    int main(void)
    {
        time_t t0 = time(0);
        char *tz = getenv("TZ");
        time_convert(t0, tz);
        time_convert(t0, "UTC0");
        time_convert(t0, "IST-5:30");
        time_convert(t0, "EST5");
        time_convert(t0, "EST5EDT");
        time_convert(t0, "PST8");
        time_convert(t0, "PST8PDT");
    }
    

    在您的原始代码中,您必须担心在更改小时偏移后标准化时间结构。您可以使用 mktime() 函数来做到这一点。这是一个基于问题中的函数的程序,它是纯 C 语言,避免了返回指向局部变量的指针(以及以分号结尾的 #define)的问题:

    #include <assert.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <time.h>
    
    #define PST (-8)
    
    extern int getSecondSystemTime(char *buffer, size_t buflen);
    
    int getSecondSystemTime(char *buffer, size_t buflen)
    {
        time_t rawtime = time(0);;
        struct tm *timeinfo;
        char t_buff[32];
    
        timeinfo = gmtime(&rawtime);
        timeinfo->tm_hour = timeinfo->tm_hour + PST;
    
        time_t pst_time = mktime(timeinfo);
        assert(pst_time != (time_t)-1);
        int len = strftime(t_buff, sizeof(t_buff), "%Y-%m-%d %H:%M:%S", timeinfo);
        assert(len != 0);
    
        int rv = snprintf(buffer, buflen, "%ld = %s (%s)", (long)rawtime, t_buff, 
                          "Pacific Time (US & Canada)");
        assert(rv > 0);
        return rv;
    }
    
    int main(void)
    {
        char buffer[128];
    
        getSecondSystemTime(buffer, sizeof(buffer));
        printf("%s\n", buffer);
        return(0);
    }
    

    显然,更好的接口会将 UTC 时间值和时区偏移量(以小时和分钟为单位)作为参数传递。尽管我的计算机默认运行在美国/太平洋(或美国/洛杉矶)时区,但我将 TZ 设置为各种值(包括美国/东部、IST-05:30)进行了测试并得到了正确的值;根据过去的经验,我有理由相信计算是正确的。

    我有另一个程序试图剖析从mktime() 返回的-1 是因为错误还是因为转换后的时间对应于(time_t)-1

    /* Attempt to determine whether time is really 1969-12-31 23:59:59 +00:00 */
    static int unix_epoch_minus_one(const struct tm *lt)
    {
        printf("tm_sec = %d\n", lt->tm_sec);
        if (lt->tm_sec != 59)
            return(0);
        printf("tm_min = %d\n", lt->tm_min);
        /* Accounts for time zones such as Newfoundland (-04:30), India (+05:30) and Nepal (+05:45) */
        if (lt->tm_min % 15 != 14)
            return(0);
        /* Years minus 1900 */
        printf("tm_year = %d\n", lt->tm_year);
        if (lt->tm_year != 69 && lt->tm_year != 70)
            return(0);
        printf("tm_mday = %d\n", lt->tm_mday);
        if (lt->tm_mday != 31 && lt->tm_mday != 1)
            return(0);
        /* Months 0..11 */
        printf("tm_mon = %d\n", lt->tm_mon);
        if (lt->tm_mon != 11 && lt->tm_mon != 0)
            return(0);
        /* Pretend it is valid after all - though there is a small chance we are incorrect */
        return 1;
    }
    

    【讨论】:

      【解决方案3】:

      这是一种更简洁的方法(此示例获取 GMT 时间,包括 DST 偏差):

      struct STimeZoneFromRegistry
      {
         long  Bias;
         long  StandardBias;
         long  DaylightBias;
         SYSTEMTIME StandardDate;
         SYSTEMTIME DaylightDate;
      };
      
      
      
      static SYSTEMTIME GmtNow()
      {
         FILETIME UTC;
         GetSystemTimeAsFileTime(&UTC); 
      
         SYSTEMTIME GMT;
      
         TIME_ZONE_INFORMATION tz = {0};
         STimeZoneFromRegistry binary_data;
         DWORD size = sizeof(binary_data);
         HKEY hk = NULL;
         TCHAR zone_key[] = _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\GMT Standard Time");
         if ((RegOpenKeyEx(HKEY_LOCAL_MACHINE, zone_key, 0, KEY_QUERY_VALUE, &hk) == ERROR_SUCCESS) &&
            (RegQueryValueEx(hk, "TZI", NULL, NULL, (BYTE *) &binary_data, &size) == ERROR_SUCCESS))
         {
            tz.Bias = binary_data.Bias;
            tz.DaylightBias = binary_data.DaylightBias;
            tz.DaylightDate = binary_data.DaylightDate;
            tz.StandardBias = binary_data.StandardBias;
            tz.StandardDate = binary_data.StandardDate;
         }
      
         SystemTimeToTzSpecificLocalTime(&tz, &UTC, &GMT);
      
         return GMT;
       }
      

      【讨论】:

      • 这是一个很好的答案,但问题是错误的,因为问题是关于 Unix 的,这是 Windows 特有的。 (也许您可以找到一个特定于 Windows 的问题并将其移到那里?)
      猜你喜欢
      • 2013-07-16
      • 2016-08-23
      • 1970-01-01
      • 2021-05-06
      • 1970-01-01
      • 2016-04-30
      • 2011-08-26
      • 1970-01-01
      相关资源
      最近更新 更多