easy datetime

pre

重构模块,一个过期时间的功能点,想要实现更灵活的配置,支持:

  • 时间长度过期:after 3600s (3600秒之后过期)
  • 时间点过期: appoint 2023-10-09 05:00:00 (localtime 过期)
  • 以天为单位的时刻过期:point 04:00:00 (每天4点过期)
    • 当天4点前获得道具,当天4点过期
    • 当天4点后获得道具,第二天4点过期

旧的样子

之前老的时间模块有这些函数:

  • get_current_timestamp - 获取当前时间戳
  • get_timestamp_from_string - 获取指定时间戳的format string
  • get_string_from_timestamp - 获取指定format string 对应的时间戳
  • get_day_start_timestamp - 获得当天开始的时间戳(localtime 0点)

期望的样子

看起来好像是够用的,组合一下,基本可以实现上面的需求;不过可能是这个模块本身写的比较乱(命名不是像上面整齐、函数实现有用c的,有用std::chrono的、ms/s用了两个函数,而且命名看不出取到的是什么、数据类型有int32,int64,uint64,time_t)就打算重新整理一下,期望:

  • timestamp 相关的函数用模版区分 seconds/millisecs
  • format string 提供只提供一种方式(基本都是内部使用,不涉及utc/http不同格式的format)
  • from string 也就只从一种格式解析,但是期望同时能解析只有date,只有time的情况(覆盖上面的第三种配置方式)
  • 性能上 format string 同一秒不做重复解析,需要缓存上次解析后的字符串,对日志这种经常获取当前时间字符串的场景可以提升不少性能
  • 因为主要用在游戏开发上,精度到millisecs基本够用
  • 基于std::chrono,之后升级到c++20可以更灵活的替换部分函数

实现

实现上其实没太复杂,只有 from string 的处理上需要覆盖期望没有用 std::get_time/strptime,自己写了解析,代码长了点,实测性能比 std::get_time 高1倍

code: https://github.com/kinly/anything/blob/main/easy_datetime.h

之前有印象google的时间解析库比 std::get_time 快10倍还是20倍的样子,想着有什么办法能给自己写的提高点性能,尝试无果后找隔壁桌的大佬帮忙。

  • 首先他觉得这样的东西应该在逻辑层处理,作为datetime模块基本只需要 旧的样子 就好(我觉得日常场景挺频繁的,基础模块做好这些没关系,毕竟已经写好了….)
  • format string 用 ms 级别缓存,后来看他写的也是用 s 的,但是可能因为他的精度是macro second 记混了,明确下:一般场景用 ms 反而会拖累性能,用 s 更合适
  • datetime 模块需要缓存 loacltime,不然做时间偏移的时候会有问题,这也是当时有最大争论的点~那会儿我也给绕晕了,怀疑是否没考虑完整…概念
    • 结论:一切基于timestamp的只有在需要转换成string,或者从string转换的时候才需要考虑localtime问题
    • timestamp 本身是 long long 类型,表示 time since epoch (1970)到当前时间经历的秒数(seconds)0时区的
    • 也就是 timestamp 永远是个相对数值量
    • 无论是判断两个 timestamp 是否是同一天、同一月、年,只要都是一种规则的偏移,判断一定是准确的,小学数学:[(x + a), (x + b)] 的关系和 [(x + 8hours + a), (x + 8hours + b)] 在数值比较运算上没有区别….
    • 后来他给了我他的测试用例(大佬的测试用例确实完善,很值得学习),套在我的代码上是这样的
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      auto _00 = easy::datetime::from_timestamp<std::chrono::seconds>(0);                 // 1970-01-01 00:00:00
      auto _01 = easy::datetime::from_timestamp<std::chrono::seconds>(57600); // +16 hours
      auto _02 = easy::datetime::from_timestamp<std::chrono::seconds>(86400); // +24 hours
      auto _03 = easy::datetime::from_timestamp<std::chrono::seconds>(90000); // +25 hours
      auto _04 = easy::datetime::from_timestamp<std::chrono::seconds>(144000); // +1 days 16 hours
      auto _05 = easy::datetime::from_timestamp<std::chrono::seconds>(172800); // +2 days
      auto _06 = easy::datetime::from_timestamp<std::chrono::seconds>(28800 - 28800); // 0
      auto _07 = easy::datetime::from_timestamp<std::chrono::seconds>(57600 - 28800); // +16 hours - 8 hours
      auto _08 = easy::datetime::from_timestamp<std::chrono::seconds>(86400 - 28800); // +24 hours - 8 hours
      auto _09 = easy::datetime::from_timestamp<std::chrono::seconds>(90000 - 28800); // +25 hours - 8 hours
      auto _10 = easy::datetime::from_timestamp<std::chrono::seconds>(144000 - 28800); // +40 hours - 8 hours
      auto _11 = easy::datetime::from_timestamp<std::chrono::seconds>(172800 - 28800); // +48 hours - 8 hours

      std::cout << "local: " << easy::datetime::localtime_string(_00) << " utc: " << easy::datetime::utc_string(_00) << " // 0" << std::endl;
      std::cout << "local: " << easy::datetime::localtime_string(_01) << " utc: " << easy::datetime::utc_string(_01) << " // +16 hours" << std::endl;
      std::cout << "local: " << easy::datetime::localtime_string(_02) << " utc: " << easy::datetime::utc_string(_02) << " // +24 hours" << std::endl;
      std::cout << "local: " << easy::datetime::localtime_string(_03) << " utc: " << easy::datetime::utc_string(_03) << " // +25 hours" << std::endl;
      std::cout << "local: " << easy::datetime::localtime_string(_04) << " utc: " << easy::datetime::utc_string(_04) << " // +1 days 16 hours" << std::endl;
      std::cout << "local: " << easy::datetime::localtime_string(_05) << " utc: " << easy::datetime::utc_string(_05) << " // +2 days" << std::endl;
      std::cout << "local: " << easy::datetime::localtime_string(_06) << " utc: " << easy::datetime::utc_string(_06) << " // 0" << std::endl;
      std::cout << "local: " << easy::datetime::localtime_string(_07) << " utc: " << easy::datetime::utc_string(_07) << " // +16 hours - 8 hours" << std::endl;
      std::cout << "local: " << easy::datetime::localtime_string(_08) << " utc: " << easy::datetime::utc_string(_08) << " // +24 hours - 8 hours" << std::endl;
      std::cout << "local: " << easy::datetime::localtime_string(_09) << " utc: " << easy::datetime::utc_string(_09) << " // +25 hours - 8 hours" << std::endl;
      std::cout << "local: " << easy::datetime::localtime_string(_10) << " utc: " << easy::datetime::utc_string(_10) << " // +40 hours - 8 hours" << std::endl;
      std::cout << "local: " << easy::datetime::localtime_string(_11) << " utc: " << easy::datetime::utc_string(_11) << " // +48 hours - 8 hours" << std::endl;

    • 后面在他的建议上加了一些相对时间的函数,看了c++20新增的一些 help types,加了 std::chrono::days 的写法
      1
      2
      3
      using days = std::chrono::duration<long long, std::ratio_multiply<std::ratio<24>, hours::period>>;
      // weak 这样子~这个也比较常用
      using days = std::chrono::duration<long long, std::ratio_multiply<std::ratio<7>, days::period>>;
  • 新标准下的 c++ 未来可期!
  • todo: 后面有办法了还是要优化下从字符串解析的性能
------ 本文结束 ------
------ 版权声明:转载请注明出处 ------