【问题标题】:Standard conformant way of converting std::time_t to System::DateTime?将 std::time_t 转换为 System::DateTime 的标准一致方法?
【发布时间】:2014-02-11 11:32:41
【问题描述】:

我已经找到了几个与将 std::time_t 值转换为 System::DateTime 并返回相关的答案。但是,几乎所有答案似乎都忽略了 std::time_t 的类型实际上在标准中是未定义的。大多数解决方案只是将std::time_t 转换为任何需要的对象,或者将算术运算应用于std::time_t 对象,这是可能的,因为它是一种算术类型,但没有关于此类操作结果的规范。我知道大多数编译器将time_t 定义为一定大小的int,但仅在最近的许多实现中它已从int32 更改为int64 的事实表明,更改确实是可能的.

所以我想出了这个应该适用于任何类型的std::time_t 的解决方案。它从我所看到的工作。但我想知道 - 有没有我可能没有意识到的陷阱?

template <>
inline System::DateTime marshal_as(const std::time_t &from_object)
{
    // Returns DateTime in Local time format from time_t (assumed to be UTC)
    const auto unix_epoch = makeUtcTime(1970, 1, 1, 0, 0, 0);
    const auto unix_epoch_dt = System::DateTime(1970, 1, 1, 0, 0, 0, System::DateTimeKind::Utc);
    const auto secondsSinceEpoch = std::difftime(from_object, unix_epoch);
    return const_cast<System::DateTime&>(unix_epoch_dt).AddSeconds(secondsSinceEpoch).ToLocalTime();
} // end of System::DateTime marshal_as(const std::time_t &from_object)

template <>
inline std::time_t marshal_as(const System::DateTime &from_object)
{
    // Returns time_t in UTC format from DateTime
    auto from_dt = const_cast<System::DateTime&>(from_object).ToUniversalTime();
    return makeUtcTime(from_dt.Year, from_dt.Month, from_dt.Day, from_dt.Hour, from_dt.Minute, from_dt.Second);
} // end of std::time_t marshal_as(const System::DateTime &from_object)

做了3个假设:

  • 生成的 std::time_t 应该是 UTC,因为它不包含任何本地化信息
  • 生成的System::DateTime 应该是本地时间,因为System::DateTime::Now 返回一个本地化的DateTime
  • makeUtcTime 是一个辅助函数,根据提供的值创建 std::tm 并从中创建 UTC std::time_t。这目前是使用_mkgmtime 实现的,因为我们的互操作代码可以安全地依赖于 Microsoft 扩展的存在。但是,mktime 的 UTC 版本在其他编译器中也很容易获得(标准 mktime 需要本地时间)。

需要考虑的2个不太重要的事情:

  • const_cast 是必需的,因为 marshal_as-template 需要 const T&amp; 作为参数,而我无法访问 const .NET 值类型对象的属性。但是,可能有更好的解决方案。
  • unix_epoch... 应该是 static const 吗?

(我不确定这是否应该发布在“程序员交流”上,因为它更多的是讨论,但由于这是一个非常具体的 C++ 问题,我认为 SO 可能是更好的提问场所)

【问题讨论】:

  • 有两种可能的time_t定义,取决于是否定义了_USE_32BIT_TIME_T。如果已定义,它将在 32 位 time_t 工作时工作。 System::DateTime 是 64 位的。
  • 确实如此,感谢您提供的信息。但是由于第二个 marshal_as 中生成的 time_t 是使用日历值 (Year, Month, ...) 构造的,因此可能发生的最糟糕的事情是 time_t 返回为 (time_t)(-1),这基本上意味着转换失败,因为选择的 time_t 实现不能代表 DateTime。但是你去了,原因 #1 为什么简单地将 TotalSeconds 转换为 time_t 可能会失败。

标签: .net datetime c++-cli mixed-mode time-t


【解决方案1】:

坚持采用“符合标准”的方式进行这种转换并不是很有效率。 Ecma-372 standard 涵盖了 std::time_t 和 System::DateTime 唯一相遇的地方。其中现在只有一个实现,而且肯定会永远存在。 Mono 项目可能被认为是另一个项目的最有可能的来源,但现在他们似乎对提供混合模式实现完全不感兴趣,这是您考虑使用 C++/CLI 的唯一原因。

std::time_t 正朝着 Y2K38 灾难稳步前进。微软先发制人地对此做了一些事情,并且确实不得不这样做,因为他们选择了 LLP64,但其他人都指望他们的LP64 data model 让他们摆脱麻烦。换句话说,到 2038 年,没有剩余的 32 位处理器仍在运行。这很可能是一个自我实现的预言。

无论如何,转换必须使用自 1970 年 1 月 1 日以来经过的秒数。这可以是 32 位或 64 位整数值,具体取决于实现。我能给出的唯一保证是这段代码至少在 2038 年之前都可以使用:

#include <ctime>

using namespace System;

public ref class Conversions {
public:
    static DateTime time_t2DateTime(std::time_t date) {
        double sec = static_cast<double>(date);
        return DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind::Utc).AddSeconds(sec);
    }
    static std::time_t DateTime2time_t(DateTime date) {
        TimeSpan diff = date.ToUniversalTime() - DateTime(1970, 1, 1);
        return static_cast<std::time_t>(diff.TotalSeconds);
    }
};

【讨论】:

  • 谢谢,我希望对我的代码进行更直接的审查,但你说得对,在这种情况下,我不妨假设 std::time_t 是微软想要的并保留它代码到最低限度。感谢您的详尽回答。
  • time_t 以秒为单位,但您将值添加为毫秒。我认为你应该使用AddSecondsTotalSeconds
  • 很好的答案,但应该是 .AddSeconds 而不是 .AddMilliSeconds。本地时间也使用 DateTime::Local 而不是 DateTime::Utc.
  • 修复了几秒钟,但我认为我需要坚持使用 UTC。
【解决方案2】:

这是我的团队得出的解决方案:

DateTime 表示自 1899 年 12 月 30 日午夜以来的混合小数天数,以双精度表示。我相信这个纪元日期被用来解释 1900 年不是闰年这一事实,它允许额外的两天(为什么是两天而不是一天?-我不清楚为什么没有选择 1899 年 12 月 31 日作为他们的时代。)

因此,2.50 的 DateTime 相当于 January 1, 1900 12:00:00 (即分数代表一天的 1/2 - 12PM)。

我们计算出 1970 年 1 月 1 日(Unix 纪元)比 DateTime 纪元晚 25569 天。

所以等价的公式是:

#include <time.h>
System::DateTime toDateTime(time_t &t)
{
    return 25569.0 + t / 86400.0; // ensure you use floating point math or you will truncate the fractional portion
}

【讨论】:

  • Microsoft 文档对单参数 DateTime 构造函数进行了说明:日期和时间以自 0001 年 1 月 1 日 00:00:00.000 以来经过的 100 纳秒间隔数表示阳历日历。你指的是什么信息?
  • Why two and not one? - It is not clear to me why Dec 31, 1899 was not chosen as their epoch.) 我认为 12 月 30 日可能是一个错误/错字,因为我找不到任何关于它实际上是 12 月 30 日而不是 12 月 31 日的信息。事实上,我至少可以找到 @ 987654321@ 以 1899 年 12 月 31 日为纪元发给 Microsoft,但 12 月 30 日没有。
  • 会不会和这个有关:cpearson.com/excel/datetime.htm
  • 是的!这可以解释 1 天的差异。很棒的发现!所以你可以纠正我的说法。它确实始于 1900 年 1 月 0 日(即 1899 年 12 月 31 日),但 1900 年 2 月 29 日错误地存在!谈谈民间传说...
猜你喜欢
  • 2016-07-21
  • 1970-01-01
  • 2021-06-17
  • 1970-01-01
  • 2016-11-01
  • 1970-01-01
  • 2022-06-22
  • 2011-10-14
相关资源
最近更新 更多