5

我已经找到了几个与将std::time_t值相互转换相关的答案System::DateTime。但是,几乎所有答案似乎都忽略std::time_t了标准中实际上未定义的类型。大多数解决方案只是强制std::time_t转换为任何需要的对象或将算术运算应用于std::time_t可能的对象,因为它是算术类型,但没有关于此类运算结果的规范。我知道大多数编译器都定义time_tint某种大小的 an,但仅在最近的许多实现中它已从 更改为 的事实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 扩展的存在。但是,UTC 版本mktime在其他编译器中也很容易获得(标准mktime需要本地时间)。

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

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

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

4

2 回答 2

16

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

std::time_t 正稳步走向 Y2K38 灾难。微软先发制人地对此做了一些事情,并且确实不得不这样做,因为他们选择了 LLP64,但其他人都指望他们的LP64 数据模型让他们摆脱麻烦。换句话说,到 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);
    }
};
于 2014-02-01T10:56:41.230 回答
0

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

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
}
于 2014-09-26T20:25:26.453 回答