将std :: time_t转换为System :: DateTime的标准符合方式?

时间:2014-01-21 17:48:08

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

我已经找到了几个与将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需要本地时间)。

要考虑的两件不太重要的事情:

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

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

2 个答案:

答案 0 :(得分:12)

坚持采用“符合标准”的方式进行转换并不是很有效率。 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);
    }
};

答案 1 :(得分:0)

以下是我的团队到达的解决方案:

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

因此,日期时间2.50将相当于1900年1月1日12:00:00(即该部分代表当天的1/2 - 下午12点)。

我们计算出1970年1月1日--Unix Epoch--是DateTime Epoch之后的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
}