测试mktime和localtime_r性能及优化方法

本文通过实验对比了localtime、localtime_r及mktime三个时间转换函数在不同环境变量配置下的性能表现,揭示了环境变量TZ对函数性能的影响,并提供了详细的测试代码与数据。

// 编译方法:g++ -g -o x x.cpp或g++ -O2 -o x x.cpp,两种编译方式性能基本相同。
//
// 结论:
// 1) 环境变量TZ和isdst均不影响localtime_r的性能
// 2) 环境变量TZ严重影响mktime和localtime的性能
// 3) mktime性能不受isdst值的影响,localtime性能与isdst值无关
// *4) 另外需要注意localtime_r为非信号安全函数,
//     不能在信号处理过程中调用,否则可能发生死锁等问题
//
// 64位机器性能数据(与32位CPU不同):
/*
$ ./x 1000000
test: localtime ...
TZ is NULL: 2629ms
TZ is empty: 177ms
TZ is Asia/Shanghai: 177ms

test: localtime_r ...
TZ is NULL and isdst=1: 124ms
TZ is NULL and isdst=0: 125ms
TZ is NULL and isdst=-1: 124ms
TZ is NULL and isdst undefined: 124ms
TZ is empty and isdst=1: 124ms
TZ is empty and isdst=0: 124ms
TZ is empty and isdst=-1: 124ms
TZ is empty and isdst undefined: 124ms
TZ is Asia/Shanghai and isdst=1: 124ms
TZ is Asia/Shanghai and isdst=0: 125ms
TZ is Asia/Shanghai and isdst=-1: 124ms
TZ is Asia/Shanghai and isdst undefined: 124ms

test: mktime ...
TZ is NULL and isdst=1: 2657ms
TZ is NULL and isdst=0: 2654ms
TZ is NULL and isdst=-1: 2661ms
TZ is NULL and isdst undefined: 2672ms
TZ is empty and isdst=1: 232ms
TZ is empty and isdst=0: 232ms
TZ is empty and isdst=-1: 232ms
TZ is empty and isdst undefined: 230ms
TZ is Asia/Shanghai and isdst=1: 233ms
TZ is Asia/Shanghai and isdst=0: 231ms
TZ is Asia/Shanghai and isdst=-1: 232ms
TZ is Asia/Shanghai and isdst undefined: 231ms
*/
// 32位机器性能数据(与64位CPU不同):
/*
> ./x 1000000
test: localtime ...
TZ is NULL: 1510ms
TZ is empty: 252ms
TZ is Asia/Shanghai: 255ms

test: localtime_r ...
TZ is NULL and isdst=1: 157ms
TZ is NULL and isdst=0: 159ms
TZ is NULL and isdst=-1: 159ms
TZ is NULL and isdst undefined: 158ms
TZ is empty and isdst=1: 158ms
TZ is empty and isdst=0: 160ms
TZ is empty and isdst=-1: 157ms
TZ is empty and isdst undefined: 158ms
TZ is Asia/Shanghai and isdst=1: 157ms
TZ is Asia/Shanghai and isdst=0: 156ms
TZ is Asia/Shanghai and isdst=-1: 156ms
TZ is Asia/Shanghai and isdst undefined: 157ms

test: mktime ...
TZ is NULL and isdst=1: 1542ms
TZ is NULL and isdst=0: 1547ms
TZ is NULL and isdst=-1: 1542ms
TZ is NULL and isdst undefined: 1566ms
TZ is empty and isdst=1: 327ms
TZ is empty and isdst=0: 324ms
TZ is empty and isdst=-1: 325ms
TZ is empty and isdst undefined: 323ms
TZ is Asia/Shanghai and isdst=1: 323ms
TZ is Asia/Shanghai and isdst=0: 326ms
TZ is Asia/Shanghai and isdst=-1: 326ms
TZ is Asia/Shanghai and isdst undefined: 324ms
*/
// localtime_r相关源代码:
/*
// The C Standard says that localtime and gmtime return the same pointer.
struct tm _tmbuf; // 全局变量

struct tm * __localtime_r (t, tp)
     const time_t *t;
     struct tm *tp;
{
  return __tz_convert (t, 1, tp);
}

// 非线程安全版本,用到了全局变量_tmbuf
struct tm * localtime(t)
     const time_t *t;
{
  return __tz_convert (t, 1, &_tmbuf);
}

struct tm * __tz_convert (const time_t *timer, int use_localtime, struct tm *tp)
{
  。。。
  // 信号处理函数中调用非信号安全函数,可能造成死锁的地方
  __libc_lock_lock (tzset_lock);
  
  // localtime_r未用到_tmbuf,只是localtime使用它!!!
  // 因此对于localtime_r,传递给tzset_internal的第一个参数总是为0(tp != &_tmpbuf),
  // 而对于localtime,它传递给tzset_internal的第一个参数总是为1
  tzset_internal (tp == &_tmbuf && use_localtime, 1);
  。。。
}

// 决定性能的函数,原因是可能涉及文件操作,
// 因此要想提升性能,则应当想办法避免操作文件!!!
static void internal_function
tzset_internal (always, explicit)
     int always;
     int explicit;
{
  static int is_initialized; // 静态变量
  const char *tz;

  // 对于mktime,参数always值总是为1
  // 对于localtime,参数always值总是为1
  // 对于localtime_r,参数always值总是为0    
  if (is_initialized && !always)
    return; // 对于localtime_r第一次调用后,后续都在这里直接返回!
  is_initialized = 1;

  tz = getenv ("TZ");
  if (tz == NULL && !explicit)
    tz = TZDEFAULT;
  if (tz && *tz == '\0')
    tz = "Universal";
  if (tz && *tz == ':')
    ++tz;

  // 如果不设置环境变量TZ,则下面这个if语句总是不成立!!!
  // 因此只有设置了环境变量TZ,才有可能在这里直接返回而不进入读文件操作__tzfile_read
  if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0)
    return; // 在这里返回则可以避免走到文件操作__tzfile_read
  if (tz == NULL)
    tz = TZDEFAULT;

  tz_rules[0].name = NULL;
  tz_rules[1].name = NULL;

  // Save the value of `tz'.
  free (old_tz);
  old_tz = tz ? __strdup (tz) : NULL;

  // 读文件,性能慢的原因  
  __tzfile_read (tz, 0, NULL); // Try to read a data file.
  if (__use_tzfile)
    return;
  。。。
}
*/
// mktime相关源代码:
/*
time_t mktime (struct tm *tp)
{
#ifdef _LIBC
  // POSIX.1 8.1.1 requires that whenever mktime() is called, the
  //   time zone names contained in the external variable 'tzname' shall
  //   be set as if the tzset() function had been called.
  __tzset ();
#endif

  // __mktime_internal为全内存计算,因此无性能问题
  return __mktime_internal (tp, __localtime_r, &localtime_offset);
}

void __tzset (void)
{
  __libc_lock_lock (tzset_lock);

  // 和localtime_r一样也会调用tzset_internal
  tzset_internal (1, 1);

  if (!__use_tzfile)
    {
      // Set `tzname'.
      __tzname[0] = (char *) tz_rules[0].name;
      __tzname[1] = (char *) tz_rules[1].name;
    }

  __libc_lock_unlock (tzset_lock);
}
*/
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>

static void test_localtime(int M); // 测试localtime性能
static void test_localtime_r(int M); // 测试localtime_r性能
static void test_mktime(int M); // 测试mktime性能

int main(int argc, char* argv[])
{
    const int M = (argc<2)? 1000000: atoi(argv[1]);

    test_localtime(M);
    printf("\n");
    test_localtime_r(M);
    printf("\n");
    test_mktime(M);

    return 0;
}

// test_localtime
void test_localtime(int M)
{
    int i;
    time_t now = time(NULL);
    struct timeval tv1, tv2;

    printf("test: localtime ...\n");
    unsetenv("TZ");

    // test1
    {        
        struct tm* result1;        
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            result1 = localtime(&now);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }
    
    setenv("TZ", "", 0);

    // test2
    {        
        struct tm* result2;        
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            result2 = localtime(&now);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    setenv("TZ", "Asia/Shanghai", 0);

    // test3
    {        
        struct tm* result3;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            result3 = localtime(&now);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }
}

// test_localtime_r
void test_localtime_r(int M)
{
    int i;
    time_t now = time(NULL);
    struct timeval tv1, tv2;
  
    printf("test: localtime_r ...\n");
    unsetenv("TZ");

    // test1
    {        
        struct tm result1;        
        result1.tm_isdst = 1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result1);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL and isdst=1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test2
    {
        struct tm result2;
        result2.tm_isdst = 0;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result2);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL and isdst=0: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test3
    {
        struct tm result3;
        result3.tm_isdst = -1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result3);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL and isdst=-1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test4
    {
        struct tm result4;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result4);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL and isdst undefined: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }
    
    setenv("TZ", "", 0);

    // test5
    {        
        struct tm result5;        
        result5.tm_isdst = 1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result5);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty and isdst=1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test6
    {
        struct tm result6;
        result6.tm_isdst = 0;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result6);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty and isdst=0: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test7
    {
        struct tm result7;
        result7.tm_isdst = -1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result7);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty and isdst=-1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test8
    {
        struct tm result8;
        result8.tm_isdst = -1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result8);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty and isdst undefined: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    setenv("TZ", "Asia/Shanghai", 0);

    // test9
    {        
        struct tm result9;        
        result9.tm_isdst = 1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result9);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai and isdst=1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test10
    {
        struct tm result10;
        result10.tm_isdst = 0;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result10);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai and isdst=0: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test11
    {
        struct tm result11;
        result11.tm_isdst = -1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result11);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai and isdst=-1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test12
    {
        struct tm result12;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            localtime_r(&now, &result12);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai and isdst undefined: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }
}

// test_mktime
void test_mktime(int M)
{
    int i;  
    time_t now = time(NULL);
    struct timeval tv1, tv2;

    printf("test: mktime ...\n");
    unsetenv("TZ");

    // test1
    {        
        struct tm result1;
        localtime_r(&now, &result1);        
        result1.tm_isdst = 1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result1);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL and isdst=1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test2
    {
        struct tm result2;
        localtime_r(&now, &result2);
        result2.tm_isdst = 0;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result2);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL and isdst=0: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test3
    {
        struct tm result3;
        localtime_r(&now, &result3);
        result3.tm_isdst = -1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result3);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL and isdst=-1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test4
    {
        struct tm result4;
        localtime_r(&now, &result4);
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result4);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is NULL and isdst undefined: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    setenv("TZ", "", 0);

    // test5
    {
        struct tm result5;
        localtime_r(&now, &result5);        
        result5.tm_isdst = 1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result5);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty and isdst=1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test6
    {
        struct tm result6;
        localtime_r(&now, &result6);
        result6.tm_isdst = 0;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result6);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty and isdst=0: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test7
    {
        struct tm result7;
        localtime_r(&now, &result7);
        result7.tm_isdst = -1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result7);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty and isdst=-1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test8
    {
        struct tm result8;
        localtime_r(&now, &result8);
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result8);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is empty and isdst undefined: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    setenv("TZ", "Asia/Shanghai", 0);

    // test9
    {
        struct tm result9;
        localtime_r(&now, &result9);        
        result9.tm_isdst = 1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result9);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai and isdst=1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test10
    {
        struct tm result10;
        localtime_r(&now, &result10);
        result10.tm_isdst = 0;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result10);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai and isdst=0: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test11
    {
        struct tm result11;
        localtime_r(&now, &result11);
        result11.tm_isdst = -1;
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result11);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai and isdst=-1: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }

    // test12
    {
        struct tm result12;
        localtime_r(&now, &result12);
        gettimeofday(&tv1, NULL);
        for (i=0; i<M; ++i)
        {
            mktime(&result12);
        }
        gettimeofday(&tv2, NULL);
        printf("TZ is Asia/Shanghai and isdst undefined: %ums\n", (tv2.tv_sec-tv1.tv_sec)*1000 + (tv2.tv_usec-tv1.tv_usec)/1000);
    }
}


int GetTimezoneOffset(double lon, double lat, const char* shapefile_path, std::string* out_tzid) { // 1. 坐标标准化转换 (核心修复) lon = std::fmod(lon, 360.0); if (lon > 180) lon -= 360; else if (lon < -180) lon += 360; // 转换为shapefile期望的坐标顺序 (纬度, 经度) const double point_coords[2] = {lat, lon}; // [Y, X] 格式 // 2. GDAL初始化 GDALAllRegister(); CPLSetConfigOption("SHAPE_RESTORE_SHX", "YES"); // 方案B: 不使用exportToWkt的替代方法 GDALDataset* poDS = static_cast<GDALDataset*>( GDALOpenEx(shapefile_path, GDAL_OF_VECTOR | GDAL_OF_READONLY, nullptr, nullptr, nullptr)); if (!poDS) { FLOGW << "Open failed: " << CPLGetLastErrorMsg(); return 1; } std::unique_ptr<GDALDataset, decltype(&GDALClose)> ds_guard(poDS, GDALClose); OGRLayer* poLayer = poDS->GetLayer(0); if (!poLayer) { FLOGW << "Layer 0 not found"; return 1; } // 获取图层的实际SRS const OGRSpatialReference* layerSRS = poLayer->GetSpatialRef(); // 创建查询点 (使用图层SRS) OGRPoint point; point.setX(lon); // 经度 point.setY(lat); // 纬度 if (layerSRS) { point.assignSpatialReference(layerSRS); // 调试输出SRS信息 char* srsWkt = nullptr; layerSRS->exportToPrettyWkt(&srsWkt); FLOGD << "Layer SRS: " << (srsWkt ? srsWkt : "NULL"); CPLFree(srsWkt); } // 7. 精确点查询 (完全模拟ogrinfo行为) poLayer->SetSpatialFilter(&point); poLayer->ResetReading(); // 8. 收集所有匹配的时区ID std::vector<std::string> matched_tzids; OGRFeature* poFeature; while ((poFeature = poLayer->GetNextFeature()) != nullptr) { std::unique_ptr<OGRFeature, decltype(&OGRFeature::DestroyFeature)> feature_guard( poFeature, OGRFeature::DestroyFeature); OGRGeometry* geom = poFeature->GetGeometryRef(); if (!geom) continue; // 统一空间参考系统 if (geom->getSpatialReference() == nullptr) { geom->assignSpatialReference(poLayer->GetSpatialRef()); } // 精确包含测试 if (geom->Contains(&point) || geom->Intersects(&point)) { const char* tzid = poFeature->GetFieldAsString("tzid"); if (tzid) { matched_tzids.push_back(tzid); FLOGD << "Found matching TZ: " << tzid; } } } // 9. 验证结果 (关键调试步骤) if (matched_tzids.empty()) { FLOGW << "NO MATCHING TIMEZONE FOUND for (" << lon << "," << lat << ")"; // 备用策略:使用ogrinfo方法 FLOGW << "Falling back to bbox query method"; OGREnvelope env; point.getEnvelope(&env); poLayer->SetSpatialFilterRect(env.MinX, env.MinY, env.MaxX, env.MaxY); poLayer->ResetReading(); while ((poFeature = poLayer->GetNextFeature()) != nullptr) { std::unique_ptr<OGRFeature, decltype(&OGRFeature::DestroyFeature)> feature_guard( poFeature, OGRFeature::DestroyFeature); OGRGeometry* geom = poFeature->GetGeometryRef(); if (!geom) continue; if (geom->Contains(&point)) { const char* tzid = poFeature->GetFieldAsString("tzid"); if (tzid) { matched_tzids.push_back(tzid); FLOGD << "Fallback match: " << tzid; } } } } // 10. 结果处理时区选择 if (matched_tzids.empty()) { FLOGW << "No timezone found for point (" << lon << "," << lat << ")"; return 1; } for(auto& id:matched_tzids) { FLOGI << "tony ids:" << id; } // 选择策略:优先大陆时区 std::string selected_tzid; auto it = std::find_if(matched_tzids.begin(), matched_tzids.end(), [](const std::string& tz) { return tz.find("Asia") != std::string::npos || tz.find("Europe") != std::string::npos; }); selected_tzid = (it != matched_tzids.end()) ? *it : matched_tzids[0]; FLOGI << "Matched timezone: " << selected_tzid << " at (" << lon << "," << lat << ")"; // 11. 时区偏移计算 (跨平台兼容) const char* orig_tz = getenv("TZ"); std::unique_ptr<char, void(*)(void*)> old_tz( orig_tz ? strdup(orig_tz) : nullptr, [](void* p) { if(p) free(p); }); setenv("TZ", selected_tzid.c_str(), 1); tzset(); time_t now = time(nullptr); struct tm local_tm; #ifdef _WIN32 localtime_s(&local_tm, &now); #else localtime_r(&now, &local_tm); #endif int offset = 0; #if defined(__linux__) || defined(__APPLE__) offset = static_cast<int>(local_tm.tm_gmtoff / 60); #elif defined(_WIN32) TIME_ZONE_INFORMATION tz_info; GetTimeZoneInformation(&tz_info); offset = -static_cast<int>(tz_info.Bias); #else struct tm utc_tm; gmtime_r(&now, &utc_tm); time_t utc_time = mktime(&utc_tm); time_t local_time = mktime(&local_tm); offset = static_cast<int>(difftime(local_time, utc_time) / 60); #endif // 恢复原始TZ设置 if (old_tz) { setenv("TZ", old_tz.get(), 1); } else { unsetenv("TZ"); } tzset(); if (out_tzid) *out_tzid = selected_tzid; return offset; } 这段代码中,如果给到经纬度122.564,52.9745,他会查询到以下几种America/Adak ,Asia/Shanghai,Asia/Yakutsk 时区,其中America/Adak很明显不在经纬度附近,但还是被匹配到了,请帮我修复,我的gdal库是3.7.0版本,我的shp文件时2023版本的
11-07
`gmtime_r` `localtime_r` 是 C 标准库中用于将 **UTC 时间戳**(`time_t`)转换为 **结构化时间**(`struct tm`)的线程安全函数。它们是 `gmtime()` `localtime()` 的可重入版本,适用于多线程环境。以下是详细说明: --- ### **1. 函数原型** ```c #include <time.h> // 将 UTC 时间戳转换为 UTC 时间的结构化表示(线程安全) struct tm *gmtime_r(const time_t *timep, struct tm *result); // 将 UTC 时间戳转换为本地时间的结构化表示(线程安全) struct tm *localtime_r(const time_t *timep, struct tm *result); ``` --- ### **2. 参数与返回值** - **参数**: - `timep`:指向 `time_t` 类型 UTC 时间戳的指针。 - `result`:用户提供的 `struct tm` 结构体指针,用于存储转换结果。 - **返回值**: - 成功时返回 `result` 指针,失败时返回 `NULL`(极少发生)。 --- ### **3. 核心区别** | 函数 | 输入时间戳 | 输出时间 | 时区依赖 | 线程安全 | |---------------|------------|----------------|----------------|----------| | `gmtime_r` | UTC | UTC 时间 | 无(固定 UTC) | 是 | | `localtime_r` | UTC | 本地时间 | 依赖系统时区 | 是 | --- ### **4. 示例代码** #### **示例 1:UTC 时间转换(`gmtime_r`)** ```c #include <stdio.h> #include <time.h> int main() { time_t rawtime = 1715083200; // 示例 UTC 时间戳(2024-05-07 12:00:00 UTC) struct tm utc_time; gmtime_r(&rawtime, &utc_time); // 转换为 UTC 时间 printf("UTC 时间: %04d-%02d-%02d %02d:%02d:%02d\n", utc_time.tm_year + 1900, utc_time.tm_mon + 1, utc_time.tm_mday, utc_time.tm_hour, utc_time.tm_min, utc_time.tm_sec); return 0; } ``` **输出**: ``` UTC 时间: 2024-05-07 12:00:00 ``` #### **示例 2:本地时间转换(`localtime_r`)** ```c #include <stdio.h> #include <time.h> int main() { time_t rawtime = 1715083200; // 示例 UTC 时间戳 struct tm local_time; localtime_r(&rawtime, &local_time); // 转换为本地时间(依赖系统时区) printf("本地时间: %04d-%02d-%02d %02d:%02d:%02d\n", local_time.tm_year + 1900, local_time.tm_mon + 1, local_time.tm_mday, local_time.tm_hour, local_time.tm_min, local_time.tm_sec); return 0; } ``` **输出**(假设系统时区为 CST,UTC+8): ``` 本地时间: 2024-05-07 20:00:00 ``` --- ### **5. 关键注意事项** 1. **线程安全**: - `gmtime_r` `localtime_r` 通过用户提供的 `struct tm` 避免静态内存竞争,而 `gmtime()` `localtime()` 使用静态缓冲区,多线程下不安全。 2. **时区依赖**: - `localtime_r` 的结果受系统时区设置影响(如 `/etc/localtime` 或 `TZ` 环境变量)。 - 若需强制指定时区,可临时修改 `TZ`: ```c #include <stdlib.h> setenv("TZ", "Asia/Shanghai", 1); // 设置为 CST 时区 tzset(); // 应用时区变更 localtime_r(&rawtime, &local_time); ``` 3. **错误处理**: - 极少失败,但可检查返回值: ```c if (localtime_r(&rawtime, &local_time) == NULL) { perror("localtime_r failed"); } ``` 4. **性能**: - 相比非线程安全版本,`_r` 函数可能因参数传递有轻微开销,但在多线程环境中是必要的。 --- ### **6. 与非线程安全版本的对比** | 函数 | 线程安全 | 存储位置 | 示例风险 | |---------------|----------|------------------------|------------------------------| | `gmtime()` | 否 | 静态内存 | 多线程下结果可能被覆盖 | | `localtime()` | 否 | 静态内存 | 同上,且依赖全局时区设置 | | `gmtime_r()` | 是 | 用户提供的 `struct tm` | 无竞争,需手动管理存储空间 | | `localtime_r()`| 是 | 同上 | 同上 | --- ### **7. 常见问题** #### **Q1:如何将 `struct tm` 转换回 `time_t`?** 使用 `mktime()` 或 `timegm()`(注意 `timegm` 非标准): ```c #include <time.h> time_t mktime(struct tm *tm); // 转换为本地时间的 time_t // 或(非标准,但常见) time_t timegm(struct tm *tm); // 转换为 UTC 时间的 time_t ``` #### **Q2:为什么 `localtime_r` 在 UTC 时区下仍返回 UTC 时间?** 若系统时区设置为 UTC(`TZ=UTC`),`localtime_r` 的行为与 `gmtime_r` 一致,因为本地时间即 UTC。 #### **Q3:如何手动计算 CST 时间(UTC+8)?** 直接对 `time_t` 加 8 小时的秒数(但需注意溢出): ```c time_t cst_time = rawtime + 8 * 3600; // UTC+8 gmtime_r(&cst_time, &cst_time_struct); // 显示为 CST 时间(逻辑错误,仅作示例) ``` **正确做法**:始终用 `localtime_r` 并设置 `TZ=Asia/Shanghai`。 --- ### **总结** - **`gmtime_r`**:UTC 时间戳 → UTC 结构化时间(线程安全)。 - **`localtime_r`**:UTC 时间戳 → 本地时间(依赖时区,线程安全)。 - **关键点**:多线程必须用 `_r` 版本,时区影响 `localtime_r` 的结果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值