【问题标题】:How is "variant time" (DATE, double, 8-byte) handled?如何处理“变体时间”(DATE、双精度、8 字节)?
【发布时间】:2014-04-23 22:43:07
【问题描述】:

我似乎找不到任何关于如何处理“变体时间”(DATE,双精度,8 字节变量)的信息...... 我有一个变体时间“A”,其值为“41716.892329”。 如果我使用“VariantTimeToSystemTime”(或“COleDateTime”)转换“A” - 我得到“2014-03-18 21:24:57”。

  • 如何计算这个变体时间?
  • 它能够存储毫秒吗?
  • 有什么方法可以确定变体时间是上午时间还是下午时间?

我对上午/下午的事情有点困惑,因为当时我正在使用的设备设置为“09:24:57”(上午)而不是“ 21:24:57"(下午)。

这可能是设备 SDK 的问题,它为我提供了不正确的变体时间吗?

编辑:这是设备中设置的时区不正确的问题(设置为“GMT-12:00”而不是“GMT-00:00” )

谢谢。

【问题讨论】:

  • 41716.892329 真的是 '2014-03-18 21:24:57'(所以,晚上 9 点),这一点没有歧义。您在自己的问题中指向文档:“小时值表示为数字小数部分的绝对值”。
  • 立陶宛距离太平洋上一个阳光明媚的岛屿太远,无法解释 UTC+12 偏移。听起来设备刚关机 12 小时,很容易出错。
  • 好的,谢谢大家!我刚刚注意到有人将设备的时区设置为“GMT-12:00”而不是“GMT-00:00”。 (汉斯,即使我在立陶宛,我也在使用位于英国的设备;))

标签: c++ datetime time com variant


【解决方案1】:

“变体时间”(DATE) 与 Excel 日期几乎但不完全相同。 Excel(因为它最初与 Lotus-123 竞争)复制了 Lotus 存储日期的方式(为了与现有用户兼容)。不幸的是,Lotus 有一个错误(可能是故意的,请参阅Joel Spolsky's blog),这意味着 1900 年 2 月有 29 天。因此,Excel 还认为 1900 年 2 月有 29 天(仍然)。尝试将值 60 格式化为日期。但是,“变体时间”用于 OLE 自动化(不只是在 Excel 中)并且已修复,以便它可以正确表示任何时间点,最初在 1753 年 1 月 1 日和 2078 年 12 月 31 日之间(ca. 2006)但现在在 100 年 1 月 1 日到 9999 年 12 月 31 日之间(12/05/2018),因此可以通过简单(可能是两部分,见下文)获得时间差减法。

Eric Lippert有综合分析但总结是:

“变体时间”是一个双精度数,其中整数部分是在 30dec1899 之后的天数。因此,0 是 30dec1899 而 01jan1900 实际上是 2(与复制 Lotus 并将其定义为第 1 天的 Excel 不同)并且没有 29feb1900。 十进制字符串表示的小数部分被视为从当天 00:00:00 开始的无符号部分日期偏移量。这里措辞的意义在于,虽然数值上 -1.75 与 -1 + -0.75 相同,但变体时间将 -1.75 视为 -1 + +0.75(即之后一天的 3/4 1899 年 12 月 29 日午夜)。因此,在将负值转换为 YYYY/MM/DD HH:MM:SS 或计算涉及负值的日期差异时,必须分别处理日期和时间部分。

【讨论】:

    【解决方案2】:

    好的,我想我已经找到了所有答案! 正如 Simon Mourier 评论的那样,“41716.892329”实际上是“2014-03-18 21:24:57”,这就是为什么 - 看起来变体时间是这样分割的:“(日期) 41716 . (时间) 892329"。 如果您将“0.892329”乘以 24.0,我们将得到 21,415896,其中“21”是我的小时值。

    我在这里找到了一些有关如何自行计算此变体时间的信息: http://doxygen.reactos.org/df/d85/variant_8c_source.html -(在“VarUdateFromDate”函数中)

    看起来变体时间能够处理毫秒,它只是像“VariantTimeToSystemTime”这样的函数忽略了它们(可能是因为缺乏精度的原因?)。函数只是四舍五入毫秒,如果需要的话向前调整日期时间。 (所以如果我们输入时间“21:24:57.567”,输出时间将为“21:24:58”。

    在这里找到了一些有趣的材料:http://www.codeproject.com/Articles/17576/SystemTime-to-VariantTime-with-Milliseconds - "SystemTime to VariantTime with Milliseconds"。

    【讨论】:

    • A double 在此范围内(几乎没有)具有足够的微秒精度,因此毫秒应该不是问题。
    • 查看我关于变体时间起源的回答。它可以追溯到(通过一些曲折的路径)到系统时钟仅精确到大约 1/18 秒的 MSDOS 时代。因此,四舍五入到干净的秒数可能被认为是“整洁的”。
    【解决方案3】:

    variant 中的日期/时间格式与 Excel 中的日期/时间格式相同。

    以下是其计算方式的简要说明: 小数点前为日期,小数点后为时间。

    格式的基础是 1900-01-01 00:00:00,值为 1。 所以 1900-01-02 00:00:00 的值为 2,snd 以此类推,有一点例外:值 60 表示日期 1900-02-29,它不存在(1900 年没有闰年)。

    值 >= 0 和

    时间被简单地划分为天。时 / 24、分 / 24 / 60、秒 / 24 / 60 / 60。

    时区信息未保存。

    通常不使用毫秒,但可以存储。

    【讨论】:

    • 不完全相同。在 1900 年 3 月 1 日之前有所不同。请参阅我的回答。
    【解决方案4】:

    在 C++20 中,可以将其转换为 time_point<system_clock, milliseconds>:

    using namespace std::chrono;
    using ddays = duration<double, days::period>;
    
    constexpr auto tp = round<milliseconds>(
       sys_time{ddays{41716.892329}} - (sys_seconds{} - sys_days{December/30/1899}));
    
    static_assert(tp == sys_days{2014y/3/18} + 21h + 24min + 57s + 226ms);
    

    在这个范围内(从 1899-12-30)double 的精度约为半微秒。

    【讨论】:

      【解决方案5】:

      这是我用来计算时间部分的公式,包括毫秒。请注意,如果这与您计划计算的一样远,则对毫秒进行四舍五入很重要。

          double date = var.date;
          double hours = (date - floor(date)) * 24;
          double minutes = (hours - floor(hours)) * 60;
          double seconds = (minutes - floor(minutes)) * 60;
          double milliseconds = round((seconds - floor(seconds)) * 1000);
      

      每一个都是双精度数,所以要让最终的数字都转换为 int。我知道这是一个老问题,但可能对某人有用

      【讨论】:

      • 请注意将 0.5 舍入为偶数的整数转换。不是 C、C++ 或 C#,而是 Powershell 和 VB.NET。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-23
      • 2015-07-19
      • 1970-01-01
      • 1970-01-01
      • 2014-09-21
      • 1970-01-01
      • 2016-03-07
      相关资源
      最近更新 更多