加入星计划,您可以享受以下权益:

  • 创作内容快速变现
  • 行业影响力扩散
  • 作品版权保护
  • 300W+ 专业用户
  • 1.5W+ 优质创作者
  • 5000+ 长期合作伙伴
立即加入
  • 正文
    • 1 linux获取时区
    • 2 ISO8601格式时间增加时间
    • 3 总结
  • 相关推荐
  • 电子产业图谱
申请入驻 产业图谱

Linux中时区获取与ISO8601时间完善

11/18 13:10
352
阅读需 14 分钟
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

上篇文章:Linux-C++获取当前时间与计算时间间隔,介绍了ISO8601格式时间的生成,但未包含时区部分。

本篇,来介绍时区部分的获取。

1 linux获取时区

1.1 编程实现

Linux系统中,可以通过localtime来获取时间,并通过tm_gmtoff来获取时区偏移量,进而得到时区。

std::string GetTimeZone() 
{
    // 获取当前时间结构体
    time_t currentTime;
    time(&currentTime);

    // 将时间结构体转换为本地时间结构体
    struct tm *localTime = localtime(&currentTime);

    // 获取时区偏移量(以秒为单位)
    int timezoneOffset = localTime->tm_gmtoff;

    // 根据偏移量计算时区差值(以小时为单位)
    int timezoneDiffHours = timezoneOffset / 3600;
    int timezoneDiffMinutes = (timezoneOffset % 3600) / 60;
    
    // 输出时区信息
    char tmpBuff[32] = {0};
    sprintf(tmpBuff, "%1s%02d:%02d", timezoneOffset>=0 ? "+" : "-", std::abs(timezoneDiffHours), std::abs(timezoneDiffMinutes));
    std::string timeZone = std::string(tmpBuff);

    return timeZone;
}

int main() 
{
    printf("%sn", GetTimeZone().c_str());

    return 0;
}

测试结果如下:

1.2 修改linux系统时区

中国的时区为东八区,Linux系统中,可以通过一些指令来修改时区。我们可以修改时区候,再来验证下刚才编写的函数功能。

1.2.1 查看当前时区

timedatectl指令可以查看当前设置的时区

xxpcb@xxpcb-ubuntu20:~/myTest/cpp/linux/Time$ timedatectl
               Local time: 日 2024-11-17 15:08:43 CST
           Universal time: 日 2024-11-17 07:08:43 UTC
                 RTC time: 日 2024-11-17 07:08:43    
                Time zone: Asia/Shanghai (CST, +0800)
System clock synchronized: yes                       
              NTP service: active                    
          RTC in local TZ: no

1.2.2 查看时区列表

timedatectl list-timezones指令可以查看支持的时区

xxpcb@xxpcb-ubuntu20:~/myTest/cpp/linux/Time$ timedatectl list-timezones
Africa/Abidjan
#省略...
Africa/Cairo
#省略...
America/New_York
#省略...
Antarctica/Vostok
#省略...
Asia/Shanghai
#省略...

1.2.3 修改时区

timedatectl set-timezone指令可以修改时区,并立即生效

timedatectl set-timezone Africa/Cairo
timedatectl set-timezone America/New_York
timedatectl set-timezone Asia/Shanghai

1.3 测试结果

测试结果如下,测试了3个不同的时区,并运行编写的时区获取函数查看运行结果:

2 ISO8601格式时间增加时间

上篇文章,编写了GetISO8601NowTime()来获取ISO8601格式的时间,并通过ISO8601ToTimeT()来将ISO8601格式的时间转换为time_t格式的时间,用于计算两个ISO8601格式时间的时间差。这两个接口都是没有对时区进行处理的。

下面,继续来实现两个有对时区进行处理的接口,从而实现对于时区有区分要求的函数功能。

2.1 编程实现

    GetISO8601NowTimeWithTimeZone()用来获取ISO8601格式的时间,通过追加时区信息来实现带有时区信息的ISO8601格式时间ISO8601WithTimeZoneToTimeT()先对没有时区的部分转换,然后对时区部分进行转换,这里先使用正则表达式,来检查输入的时间格式是否正确,然后对时区中的正负号,小时和分钟的偏移量进行处理,最后将两个部分的时间进行叠加
std::string GetISO8601NowTimeWithTimeZone()
{
    return GetISO8601NowTime() + GetTimeZone();
}

time_t ISO8601WithTimeZoneToTimeT(std::string &dateTime)
{
    time_t t = ISO8601ToTimeT(dateTime); //先对没有时区的部分转换
    
    std::string pattern{".*([-+])([0-1][0-9]):([0-5][0-9])"}; //+08:00
    try
    {
        std::regex re(pattern); //正则表达式检查时间格式是否正确
        std::smatch result;
        if (!std::regex_match(dateTime, result, re))
        {
            printf("[%s] regex not matchn", __func__);
            return 0;
        }
        
        //再对时区进行转换
        std::string zone = result[1].str(); //提取的时区
        int hour = std::stoi(result[2].str()); //提取的时区中的小时
        int minute = std::stoi(result[3].str()); //提取的时区中的分析
        int s = hour * 3600 + minute * 60;
        if ("-" == zone) s = 0 - s;
        
        return t + s;
    }
    catch(std::regex_error &e)
    {
        printf("[%s] regex err:%dn", __func__, e.code());
        return 0;
    }
}

2.2 正则表达式分析

首先来看定义的一个正则表达式模式的含义:

std::string pattern{".*([-+])([0-1][0-9]):([0-5][0-9])"}; //+08:00

这里定义了一个正则表达式模式 pattern,用于匹配包含时区信息的字符串:

.*:表示匹配任意字符零次或多次,这是为了允许在时区信息之前可以有其他任意内容(比如完整的日期时间字符串中的日期部分等)

([-+]):这是一个捕获组,用于捕获时区的正负标识,即+-

([0-1][0-9]):也是一个捕获组,用于捕获时区中的小时数,它限制小时数的范围是从0019(因为第一位数字只能是01,第二位数字可以是09),不过按照常见的时区偏移量,实际上一般不会出现大于14的情况(UTC + 14 是常见的最大偏移量)

([0-5][0-9]):同样是捕获组,用于捕获时区中的分钟数,它限制分钟数在0059之间,并且按照常规,分钟数通常是15的倍数(如00153045),但这里的正则表达式允许任意0059的分钟数

然后看下提取时区信息并转换为秒偏移量:

std::string zone = result[1].str(); //提取的时区
int hour = std::stoi(result[2].str()); //提取的时区中的小时
int minute = std::stoi(result[3].str()); //提取的时区中的分析
int s = hour * 3600 + minute * 60;
if ("-" == zone) s = 0 - s;

这里:

    • 通过result[1].str()、
    • result[2].str()
    • 和result[3].str()
    • 分别提取出正则表达式匹配结果中的时区正负标识、小时数和分钟数然后计算出以秒为单位的时间偏移量s,
    • 先将小时数乘以3600
      • (因为一小时有3600秒),再将分钟数乘以60
      • (因为一分钟有60秒),两者相加得到总的时间偏移量如果时区标识为-,则将计算出的偏移量取相反数,以正确反映与 UTC 时间的负偏移关系

2.3 测试结果

测试函数,有一个固定的旧时间,然后获取当前时间,计算两个时间的时间差

int main()
{
    std::string t1 = "2024-11-17T15:31:09.000-06:00";
    std::string t2 = GetISO8601NowTimeWithTimeZone();
    printf("t1(old):%snt2(now):%sn", t1.c_str(), t2.c_str());
    
    uint64_t deltaTotalSec = TimeDurationSec(t1, t2);
    uint64_t deltaDay = deltaTotalSec / (3600*24);
    uint32_t deltaHour = deltaTotalSec % (3600*24) / 3600;
    uint32_t deltaMin = deltaTotalSec % 3600 / 60;
    uint32_t deltaSec = deltaTotalSec % 60;
    printf("delta sec:%lu(%lu day, %u hour, %u min, %u sec)n", deltaTotalSec, deltaDay, deltaHour, deltaMin, deltaSec);
    
    return 0;
}

测试结果如下:

3 总结

本篇介绍了通过编程实现linux系统中时区的获取,并完善上篇的ISO8601格式时间的生成,增加了时区的处理。

相关推荐

电子产业图谱

控制科学与工程硕士,日常分享单片机、嵌入式、C/C++、Linux等学习经验干货~