当前位置: 首页 > news >正文

手机app应用网站微信群推广软件

手机app应用网站,微信群推广软件,中小企业组网,阿里建站官网在编程世界里#xff0c;从 UTC 日期时间字符串获取 Unix 时间戳#xff0c;看似简单#xff0c;实则暗藏玄机。你以为输入一个像 “Fri, 17 Jan 2025 06:07:07” 这样的 UTC 时间#xff0c;然后轻松得到 1737094027#xff08;从 1970 年 1 月 1 日 00:00:00 UTC 开始经…在编程世界里从 UTC 日期时间字符串获取 Unix 时间戳看似简单实则暗藏玄机。你以为输入一个像 “Fri, 17 Jan 2025 06:07:07” 这样的 UTC 时间然后轻松得到 1737094027从 1970 年 1 月 1 日 00:00:00 UTC 开始经过的秒数就万事大吉了事实可没这么简单这背后涉及到一系列复杂的时间处理问题还会让你发现 POSIX 时间处理函数在不同 C 库及相关语言中的各种 “意外特性”。今天咱们就来深入探讨一下这个让人又爱又恨的话题。 一、时间处理的复杂性 时间本身就是一个复杂的概念要是再把闰秒和相对论这些因素考虑进去那就更让人头疼了。而人类对时间的记录方式从模糊不清到精确无比各不相同。就拿阿姆斯特丹的时间来说由于夏令时的存在“2025 年 3 月 30 日 02:20” 这个时间点在当地是不存在的时间会直接从 01:59:59 跳到 03:00:00。但 “2024 年 10 月 27 日 02:30” 就更让人困惑了因为夏令时结束时02:59:59 的下一秒又回到了 02:00:00这就导致有两个 “02:00” 的时间点。从下面的命令行示例就能看出工具在处理这种情况时的随意性 $ TZEurope/Amsterdam date -d 20241027 01:59:59 %Y-%m-%d %H:%M:%S %s %z 2024-10-27 01:59:59 1729987199 0200 $ TZEurope/Amsterdam date -d 20241027 02:00:00 %Y-%m-%d %H:%M:%S %s %z 2024-10-27 02:00:00 1729990800 0100 你看当要求解释 02:00:00 这个时间时GNU date 工具选择了第二个出现的时间点。而且据我观察这还和执行命令的时间有关如果在四月执行可能就会选择第一个 02:00:00 实例是不是很让人摸不着头脑 二、POSIX 时间概念与 struct tm 在 POSIX/Unix 系统中指定时间的有效方式是用相对于某个 “纪元”epoch的秒数。POSIX/Unix 的纪元是 1970 年 1 月 1 日 00:00:00 UTCGPS 的纪元是 1980 年 1 月 6 日 00:00:00 UTC伽利略欧盟的 GPS 系统的纪元是 1999 年 8 月 21 日 23:59:47 UTC北斗系统的纪元是 2006 年 1 月 1 日 00:00:00 UTC。其中GPS、伽利略和北斗系统都明智地忽略了闰秒把这些麻烦事留给人类去处理。而我们常用的 POSIX/Unix 的 “time_t” 时间戳除了在闰秒期间可能会有歧义不过闰秒以后可能也不会再有了其他时候还是很可靠的。 为了在时间戳和人类可读的时间格式之间进行转换UNIX 提供了 struct tm 结构体它包含了年、月、日、时、分、秒等时间信息 struct tm {int tm_sec; /* 秒 [0, 60] */int tm_min; /* 分 [0, 59] */int tm_hour; /* 时 [0, 23] */int tm_mday; /* 一个月中的第几天 [1, 31] */int tm_mon; /* 月份 [0, 11] 一月是 0 */int tm_year; /* 年份减去 1900 */int tm_wday; /* 一周中的第几天 [0, 6] 周日是 0 */int tm_yday; /* 一年中的第几天 [0, 365] 1 月 1 日是 0 */int tm_isdst; /* 夏令时标志 */long tm_gmtoff; /* 相对于 UTC 的秒数 */const char *tm_zone; /* 时区缩写 */ }; 不过这个结构体的设计其实有点冗余像一周中的第几天和一年中的第几天通过其他字段就能推算出来。而且tm_gmtoff、tm_zone 和 tm_isdst 这几个字段的含义不仅定义得不太清晰理解起来也有难度并且它们的作用还会根据结构体的使用方式而变化。 struct tm 的一个重要作用是作为 mktime() 函数的输入mktime() 会把 “根据本地时区拆分的时间” 转换为 Unix 时间戳。但它的功能可不止这一个它还会对传入的 struct tm 进行标准化处理。比如说如果你想把当前时间往后调一周你可能会直接给 time_t 时间戳加上 604800 秒一周的秒数但如果这个调整跨越了夏令时的边界你原本下午 2 点的约会可能就会变成下周下午 1 点或 3 点这可不是我们想要的结果。而 mktime() 就能帮你处理这种情况即使你传入像 “3 月 35 日” 这样不合理的日期它也能帮你修正。不过使用 mktime() 时也有一些需要注意的地方下面我们就来详细说说。 三、mktime() 的使用与注意事项 先来看个例子 struct tm tm {.tm_hour14, .tm_mday 28, .tm_mon 2, .tm_year 2025 - 1900, .tm_isdst -1}; // - 注意这里的 -1 time_t t mktime(tm); cout original: ctime(t); tm.tm_mday 7; t mktime(tm); cout mktime adjusted: ctime(t); 在欧洲 / 阿姆斯特丹时区这段代码的输出结果是 original: Fri Mar 28 14:00:00 2025 mktime adjusted: Fri Apr 4 15:00:00 2025 为什么我们的约会时间会偏移一个小时呢问题就出在 tm.tm_isdst 这个字段上。mktime() 要求你明确指定时间是否处于夏令时或者让它自己去判断我们一开始把 tm.tm_isdst 设置为 -1 就是让它自己判断。当我们第一次调用 mktime() 时它发现初始时间不在夏令时就把 tm_isdst 设置为 0。第二次调用时这个设置没有改变但新的时间其实是处于夏令时的所以就出现了时间偏移的情况。解决办法就是在第二次调用 mktime() 之前把 tm_isdst 重新设置为 -1。 另外使用 mktime() 处理 UTC 时间时也有个大坑。mktime() 会把传入的时间当作 “本地时间” 来处理所以如果你要处理 UTC 时间就需要在调用 mktime() 之前把时区设置为 UTC。但如果你的程序有其他线程在运行修改整个应用程序的时区可能会产生副作用。不过多线程程序本来就不能随意更改环境变量所以这个方法也行不通。 还好有一个非标准但广泛可用的函数 timegm() 能很好地解决 UTC 时间的处理问题。从 IEEE Std 1003.1 - 2024 标准中可知“未来的标准版本预计会添加一个 timegm() 函数它与 mktime() 类似但 timeptr 指向的 tm 结构体包含的是协调世界时UTC的拆分时间”。在 Windows 系统上timegm() 对应的函数是 mkgmtime()。如果你的系统是 AIX没有 timegm() 函数也可以在特定的地方找到独立的实现。 总结一下使用 mktime() 和 timegm()或 mkgmtime()的要点 使用 mktime() 处理本地时间时把 tm_isdst 设置为 -1这通常符合人们的预期但在夏令时切换时可能会随机得到两个 “02:30”或类似时间点中的一个。 在填充 struct tm 之前最好先把其他字段清零以防万一。 要知道 mktime() 会修改传入的 struct tm可能会产生副作用所以在重复使用 struct tm 之前至少要重置 tm_isdst。 不管你怎么设置 tm_gmtoffset 或 tm_zonemktime() 都会使用当前时区。如果你想让它把 struct tm 当作 UTC 时间处理就需要设置 TZ 环境变量为 UTC但这会影响其他做时间操作的线程。所以能使用 timegm() 或 mkgmtime() 就尽量用它们。 四、解析 UTC 时间字符串 我们都希望能把像 “Fri, 17 Jan 2025 06:07:07 GMT” 这样的时间字符串直接传入 strptime() 函数然后得到一个合理的 struct tm 结构体。但 Linux glibc 的 strptime() 手册中关于 %z 和 %Z 这两个时区格式说明符的描述含糊不清让人摸不着头脑。很多人可能会期望用 strptime() 结合 %Z用于解析 “GMT”再配合 mktime() 就能把 UTC 时间字符串转换为 Unix 时间戳但实际上 mktime() 根本不会看 tm_gmtoffset 和 tm_zone 这两个字段所以即使 strptime() 做对了也无法实现我们的目标而且它还真就做不对。 截至 2024 年虽然 Open Group 对 strptime() 有了更详细的规范但里面也都是些让人无奈的消息。strptime() 对 %z 没有明确的行为定义这个原本应该用来处理 “0200” 这种偏移标识符的符号现在根本靠不住。对于 %Z虽然有一些说明但作用也非常有限。只有在特定的本地化设置下它才可能设置 tm_isdst 的正确值而且像 “EST” 这样的字符串由于没有明确的定义也很难通过 %Z 来解析。 不过既然我们知道了 timegm() 这个好帮手就可以忽略 %z 和 %Z 了。 五、strptime() 与本地化问题 在很多情况下我们需要解析包含英文日期和月份名称的时间字符串并且希望 strptime() 能正确处理。但 IEEE/Open Group 标准规定strptime() 的转换操作是由当前本地化设置的 LC_TIME 类别决定的。这里有个容易忽略的点C 和 C 程序默认使用的是 “C” 本地化这实际上就是美式英语。这对于解析数据中的时间字符串来说通常是好事因为这些字符串大多是英文的。 但如果你的程序调用了 setlocale() 函数设置了非 “C” 的本地化那你的程序可能就只能处理特定语言比如荷兰语的时间字符串了这可就麻烦了。你可能会想在调用 strptime() 之前把本地化设置为 “C”用完再改回来但 setlocale() 在多线程程序中调用并不安全除了在线程启动之前而且即使安全调用也可能会影响其他线程的输出。 所以一般来说如果你需要解析特定的时间字符串并且想用 strptime()一定要确保程序处于你期望的本地化环境中。虽然有 strftime_l() 函数可以指定格式化时间时使用的本地化但并没有官方可用的 strptime_l() 函数。 当然你也可以自己解析像 “17 Jan 2025 06:07:07” 这样的字符串填充 struct tm 结构体然后让 mktime() 来计算 Unix 时间戳这也是一种可行的办法。 六、用 C 解决本地化问题及 C20 的强大时间处理功能 C 的输入输出流iostreams在处理本地化方面比 C/POSIX 做得更好。在 C 中你可以为每个输入输出流设置本地化。下面是一个 C 辅助函数如果你在设置了本地化的 C 程序中需要解析任意 UTC 时间字符串可以调用这个函数 extern C int utcstr2epoch(const char* timestr, const char* fmtstr, struct tm* output) {std::tm t {}; // tm_isdst 0, 不用考虑夏令时这是 UTC 时间std::istringstream ss(timestr);ss.imbue(std::locale()); // LANGC, 但本地化设置是本地的ss std::get_time(t, fmtstr);if (ss.fail())return -1;// 修正星期几、一年中的第几天等字段t.tm_isdst 0; // 不用考虑夏令时t.tm_wday -1;if(mktime(t) -1 t.tm_wday -1) // 真正的错误return -1;*output t;return 0; } 这个函数还展示了如何处理 mktime() 的错误。当 mktime() 处理 1969 年 12 月 31 日 23:59 这样的时间时会返回 -1 作为错误代码。我们可以用 tm_wday 作为标志来判断是否有数据被处理以此确定是否发生了错误。 另外还有一个基于 C 的小示例程序它可以解析英文的 UTC 时间戳并使用调用环境的本地化设置来打印时间 $ LC_TIMEnl_NL.utf-8 ./utcparse 1 Jan 1970 00:00:00 %d %b %Y %H:%M:%S UTC Time: donderdag, 1 januari 1970 00:00:00, day of year 001 time_t: 0 到了 C20 及更高版本更是引入了强大的时区数据库。虽然这个功能还没有在所有编译器上都可用但预标准化版本可以单独使用。比如下面这个超酷的例子 auto meet_nyc make_zoned(America/New_York, date::local_days{Monday[1]/May/2016} 9h); auto meet_lon make_zoned(Europe/London, meet_nyc); auto meet_syd make_zoned(Australia/Sydney, meet_nyc); cout The New York meeting is meet_nyc \n; cout The London meeting is meet_lon \n; cout The Sydney meeting is meet_syd \n; 这段代码选择了 “2016 年 5 月的第一个星期一纽约当地时间上午 9 点”然后轻松地将其转换为另外两个时区的时间 The New York meeting is 2016-05-02 09:00:00 EDT The London meeting is 2016-05-02 14:00:00 BST The Sydney meeting is 2016-05-02 23:00:00 AEST 更厉害的是这个时区库不仅可以使用操作系统的时区数据库可能缺少关键的闰秒细节还能直接从 IANA tzdb 获取数据。这意味着你可以精确计算 1978 年一次航班飞行的实际时长即使这次飞行跨越了夏令时变化和闰秒也不在话下。 在 C 和 C 中从 UTC 日期时间字符串获取 Unix 时间戳确实充满挑战但只要掌握了正确的方法也能轻松应对。希望今天的分享能让你在处理时间相关的编程问题时更加得心应手。 科技脉搏每日跳动。 与敖行客 Allthinker一起创造属于开发者的多彩世界。 - 智慧链接 思想协作 -
http://www.dnsts.com.cn/news/207405.html

相关文章:

  • 宁波专业品牌网站制作外包营销型网站建设系统
  • 营销型的物流网站模板建设工程材料网站
  • 有没有网站教做美食的搜索引擎优化的核心及内容
  • 优秀企业网站建设价格大连三丰建设集团公司网站
  • 客户做网站需要提供什么全球搜索引擎入口
  • 网站开发 职位网站建设后应该干什么
  • 中国网站免费服务器wordpress自定义添加meta模块
  • php网站开发是学什么的青岛公司网页设计
  • 怎么建设投票网站ui设计论文
  • 网站开发市场调查国家建设执业注册中心网站
  • 0基础建设网站上海企业登记全程电子化服务平台
  • wordpress网站如何与关联苏州营销型网站建设哪家好
  • 更新网站要怎么做呢工业设计软件有哪些软件
  • 景泰做网站做影视网站侵权吗
  • 成都网站设计苏州好的做网站的公司有哪些
  • 网站制作可以询价么北京注册公司地址可以是住宅吗
  • 烟台建设集团 招聘信息网站做企业网站多少钱
  • 昆明集团网站建设有什么做海报的网站吗
  • 做欧美贸易的主要有哪些网站网络规划的主要步骤
  • 如何建设一个普通网页网站重庆网上房地产查询
  • 济南建站公司模板微信订阅号做微网站吗
  • google网站收录跨境电商的特点
  • 修改公司网站怎么自己制作网站链接
  • 电子商务搭建网站红色 网站配色
  • wordpress能做什么网站云服务器和虚拟主机有什么区别
  • 流量网站建设教程一个空间如何做多个网站
  • 吉林科技网站建设wordpress彩色提示框
  • 房地产项目网站建设红包网站开发
  • 云南建设厅网站监理员培训厦门网站建设2015
  • 大学 建网站广州网站建设集团