标准一致的方式转换std :: time_t到System :: DateTime? [英] Standard conformant way of converting std::time_t to System::DateTime?

查看:221
本文介绍了标准一致的方式转换std :: time_t到System :: DateTime?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经找到了几个与将 std :: time_t 值转换为 System :: DateTime 背部。然而,几乎所有的答案似乎忽略了 std :: time_t 的类型在标准中实际上是未定义的。大多数解决方案只是将 std :: time_t 转换为任何所需的或应用算术运算到 std :: time_t 可能是因为它是算术类型,但没有关于这种操作的结果的规范。我知道最常见的编译器将 time_t 定义为一些大小的 int 在许多实现中它已从 int32 变为 int64 ,最近显示更改确实可能。

使用任何类型的 std :: time_t code>。它从我所看到的工作。但我想知道 - 有没有可能的陷阱我可能不知道?

  template< > 
inline System :: DateTime marshal_as(const std :: time_t& from_object)
{
//从time_t(假定为UTC)返回本地时间格式的DateTime
const auto unix_epoch = makeUtcTime(1970,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)
{
//以UTC格式从DateTime返回time_t
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

  • 导致 System :: DateTime 应为自<$ c $之后的本地时间c> System :: DateTime ::现在返回一个本地化的 DateTime

  • makeUtcTime 是一个辅助函数,从提供的值创建一个 std :: tm ,并创建一个UTC std :: time_t 出来了。这是目前使用 _mkgmtime 实现的,因为我们的interop代码可以安全地依赖于Microsoft扩展的存在。但是, mktime 的UTC版本也可以在其他编译器中使用(标准 mktime 期望本地时间)。



2不太重要的事项:




  • const_cast 是必要的,因为marshal_as-template期望一个 const T& 作为参数, const .NET值类型对象的属性。但是可能有更好的解决方案。

  • 如果 unix_epoch ... 的东西是 static const li>


(我不知道这是否应该发布在Programmers Exchange上,因为它更像是一个讨论,问题我认为这可能是更好的地方问)

解决方案

坚持一个标准符合的方式进行这种转换。只有一个std :: time_t和System :: DateTime会遇到的地方是 Ecma-372标准。其中有现在,肯定会永远只有一个实施。 Mono项目可以被假定为另一个项目的最可能的来源,但现在他们似乎完全不感兴趣提供混合模式实现,这是你曾经考虑过使用C ++ / CLI的唯一原因。



std :: time_t正在稳步迈向Y2K38灾难。微软预先做了一些事情,真的要这样做,因为他们去LLP64,但每个人都指望他们 LP64数据模型,使他们免受麻烦。换句话说,2038年还没有剩余的32位处理器运行。这可能是一个自我实现的预言。



无论如何,转换将必须工作自1970年1月1日以来经过的毫秒数。这可以是32位或64位积分值,具体取决于实现。对于System :: DateTime,处理毫秒需要使用 double 。任何编译器都能够在double和一个整数值之间进行转换,它是大括号语言中的标准转换。一种具有毫秒级精度的转换,直到DateTime :: MaxValue。另外还有保证往返。您至少在2038年之前不会遇到移植此代码的问题:

  #include< ctime> 

使用命名空间System;

public ref class Conversions {
public:
static DateTime time_t2DateTime(std :: time_t date){
double msec = static_cast< double>(date);
return DateTime(1970,1,0,0,0,DateTimeKind :: Utc).AddMilliseconds(msec);

}
static std :: time_t DateTime2time_t(DateTime date){
TimeSpan diff = date.ToUniversalTime() - DateTime(1970,1,1);
return static_cast< std :: time_t>(diff.TotalMilliseconds);

}
};


I have already found several answers related to converting a std::time_t value to System::DateTime and back. However, almost all answers seem to neglect that the type of std::time_t is actually undefined in the standard. Most solutions just cast std::time_t to whatever needed or apply arithmetic operations to a std::time_t object which is possible since it's an arithmetic type, but there is no specification about the result of such an operation. I know that most compilers define time_t as an int of some size but the fact alone that it has changed from int32 to int64 in many implementations recently shows that changes are indeed possible.

So I've come up with this solution which should work with any type of std::time_t. It works from what I have seen. But I was wondering - Are there any possible pitfalls I might be unaware of?

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 assumptions were made:

  • Resulting std::time_t should be in UTC since it doesn't contain any info on localization
  • Resulting System::DateTime should be local time since System::DateTime::Now returns a localized DateTime
  • makeUtcTime is a helper function creating a std::tm from the values supplied and creates a UTC std::time_t out of it. This is currently implemented using _mkgmtime because our interop code can safely rely on the existence of Microsoft extensions. However, a UTC version of mktime is readily available in other compilers as well (standard mktime expects local time).

2 less important things to consider:

  • The const_cast is necessary because the marshal_as-template expects a const T& as parameter and I can't access the properties of a const .NET value-type object. However there might be a better solution.
  • Should the unix_epoch... stuff be static const?

(I wasn't sure if this should be posted on "Programmers Exchange" since it's more of a discussion but since it's a very specific C++ question I thought SO might be the better place to ask)

解决方案

It just isn't very productive to insist on a "standard conformant" way to make this conversion. The only place where an std::time_t and a System::DateTime are ever going to meet is covered by the Ecma-372 standard. Of which there is right now, and surely will ever be, only one implementation. The Mono project could be assumed to be the most likely source of another one, but right now they appear entirely uninterested in providing a mixed-mode implementation, the only reason you'd ever consider using C++/CLI.

std::time_t is steadily marching towards the Y2K38 disaster. With Microsoft pre-emptively having done something about it, and really having to do so because they went for LLP64, but everybody else counting on their LP64 data model keeping them out of trouble. In other words, no remaining 32-bit processors still running in 2038. This could well be a self-fulfilling prophesy.

Regardless, the conversion is going to have to work with elapsed milliseconds since 1/1/1970. And that can be a 32-bit or a 64-bit integral value, depending on implementation. For System::DateTime, working with milliseconds requires using double. And any compiler is going to be able to convert between double and an integral value, it is a standard conversion in the curly brace languages. A conversion that has millisecond accuracy all the way up to DateTime::MaxValue. One that furthermore is guaranteed to round-trip. You are not going to have a problem ever porting this code, at least until 2038:

#include <ctime>

using namespace System;

public ref class Conversions {
public:
    static DateTime time_t2DateTime(std::time_t date) {
        double msec = static_cast<double>(date);
        return DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind::Utc).AddMilliseconds(msec);

    }
    static std::time_t DateTime2time_t(DateTime date) {
        TimeSpan diff = date.ToUniversalTime() - DateTime(1970, 1, 1);
        return static_cast<std::time_t>(diff.TotalMilliseconds);

    }
};

这篇关于标准一致的方式转换std :: time_t到System :: DateTime?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆