【问题标题】:localtime returns GMT for windows programs running on cygwin shellslocaltime 为在 cygwin shell 上运行的 Windows 程序返回 GMT
【发布时间】:2012-07-24 04:42:03
【问题描述】:

考虑以下代码:

time_t t;
t = time( NULL );
elog << "timezone: " << getenv( "TZ" ) 
     << ", current local time: " << asctime( localtime( &t ));

如果我使用 MSVC 构建此代码,并在 windows DOS shell 下运行它,我会得到正确的本地时间:

timezone: , current local time: Wed Jul 25 13:05:08 2012

但如果我在 bash 之类的 cygwin shell 下运行相同的程序,此代码会返回 GMT!

timezone: America/New_York, current local time: Wed Jul 25 18:05:08 2012

如果我在 Linux 或 OsX 中运行这个程序,它也会返回正确的本地时间。

为什么?

@Update:现在一年过去了,我发现我在下面给出的答案并不总是有效。

似乎对于某些程序,取消设置 TZ 并不总是有效。我不知道为什么。但是有一个麻烦的解决方法。基本上,在您取消设置 TZ 之后,您必须检查本地时间是否确实不再返回 GMT,但前​​提是您实际上不在 GMT 时区,并在您调用 localtime() 时计算对 time_t 的手动调整或制作时间()

u64 localTimeOffset = 0;

static void unsetTz()
{
   static bool unsetTZ = false;
   if ( !unsetTZ )
   {
      putenv( "TZ=" );
      unsetTZ = true;

      // unsetting TZ does not always work. So we have to check if it worked
      // and make a manual adjustment if it does not. For long running programs
      // that may span DST changes, this may cause the DST change to not take 
      // effect.
      s32 tzOffset = getTzOffset();

      if ( tzOffset )
      {
         static char timeBuf[48];
         char* s = &(timeBuf[0]);
         struct tm* timeInfoGMT;
         struct tm* timeInfoLocal;

         time_t zero = 86400;
         timeInfoGMT = gmtime( &zero );
         u32 GMTHour = timeInfoGMT->tm_hour;

         timeInfoLocal = localtime( &zero );
         u32 localHour = timeInfoLocal->tm_hour;

         if ( localHour == GMTHour )
         {
            // unsetting tz failed. So we have to make a manual adjustment
            localTimeOffset = tzOffset * 60 * 60;
         }
      }
   }
}

s32 getTzOffset()
{
   TIME_ZONE_INFORMATION tzInfo;
   GetTimeZoneInformation( &tzInfo );
   s32 tz = ( tzInfo.Bias / 60 );
   return tz;
}

调用当地时间:

  time_t t = getAtTimeFromSomewhere();
  t -= localTimeOffset;
  timeInfo = localtime( &t );

打电话给 maketime:

 struct tm timestr;
 makeATMFromAStringForExample( time, timestr );
 time_t timet = mktime( &timestr );
 timet += localTimeOffset;

美好时光。

【问题讨论】:

    标签: c++ c time cygwin


    【解决方案1】:

    这花了我一些时间来弄清楚,我希望它对其他人有用。

    localtime 这样的POSIX 函数将使用环境变量TZ 来确定要使用的时区。如果没有设置TZ,它将使用系统的默认时区。

    如果我在 Linux 或 OS X 下运行,TZ 设置正确并且一切正常。如果我在 Windows 上的 shell 中运行这个程序,TZ 没有设置,所以该函数返回操作系统的默认时区,这又会产生正确的结果。

    如果我在 Cygwin shell 中运行,TZ 已设置 - 但由于我使用 MSVC 构建程序,使用 MSVC 自己的 stdc 库 - 它无法解释 Cygwin 的 TZ 变量。所以它默认为 GMT。

    如果该程序是在 Cygwin 下使用 GCC 构建的,我敢打赌它会在 Cygwin shell 中正常工作。

    所以答案是确保在调用 POSIX 时间函数(如 localtime())的程序中,如果您希望时间函数在 Cygwin shell 下正常工作,则必须取消设置 TZ

    我是这样做的:

    void getLocalTime()
    {
       #ifdef WIN32
       static bool unsetTZ = false;
       if ( !unsetTZ )
       {
          putenv( "TZ=" );
          unsetTZ = true;
       }
       #endif // !WIN32
    
       time_t t;
       t = time( NULL );
       elog << "timezone: " << getenv( "TZ" ) 
            << ", current local time: " << asctime( localtime( &t ));
    }
    

    【讨论】:

    • putenv( "TZ=" ) 实际上并没有取消设置 TZ;它将它设置为空字符串。我不知道这是否重要,但您可以尝试改用unsetenv("TZ")
    • 谢谢。我调查了一下。 Windows 没有 unsetenv。要取消设置变量,请使用 putenv 将其设置为空字符串。
    • cygwin下,运行'unset TZ'就够了
    【解决方案2】:

    在 Cygwin shell 中使用 export TZ="" 可能是最好的方法。

    @更新
    你好,图纳基。感谢您的编辑!

    【讨论】:

      【解决方案3】:

      尝试在 struct tm 中设置 tm_isdst 标志。

      如果夏令时有效,夏令时标志 (tm_isdst) 大于零,如果夏令时无效,则为零,如果信息不可用,则小于零。

      如果我没记错的话,在 Windows 中它必须是 -1 才能正常工作。

      【讨论】:

      • 夏令时无法解释 5 小时的差异。
      • 哎哟..对不起,你说得对。我没有看到5小时的差异。问题是我在 linux/windows 上遇到了类似的问题,我正确地设置了 dst。
      猜你喜欢
      • 2013-03-22
      • 2012-06-13
      • 1970-01-01
      • 2012-04-25
      • 2012-03-30
      • 2019-03-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多