将双精度日期和时间值转换为另一个时区



我创建了一些代码来转换双精度日期&将时间值设置为另一个时区。它给出的行为,我只是不明白当DateTimeKind。Local用于代替DateTimeKind.Unspecified。

我的想法是传递给方法ConvertTime的双精度值与它的地理上下文完全脱节。当然,要正确转换时间,有必要指定该值是本地时间并属于某个时区?

无论主机的时区设置如何,我正在努力确保本地源时间正确转换为本地目的地时间并观察日光节约时间。

从这个概念出发,我试图指定

DateTime sourceDT = DateTime.SpecifyKind(inDT, DateTimeKind.Local);

,但这会导致时间不被转换。如果我指定

 DateTime sourceDT = DateTime.SpecifyKind(inDT, DateTimeKind.Unspecified);

则发生转换

谁能解释一下为什么DateTimeKind。在时间转换和如何实现我正在尝试的内容时,不接受Local作为有效规范。

namespace ConvertTime
{
public interface ConvertTimeClass
{
    double ConvertTime(double inTime, [MarshalAs(UnmanagedType.LPStr)] string sourceTZ,     [MarshalAs(UnmanagedType.LPStr)] string destTZ);
}
public class ManagedClass : ConvertTimeClass
{
    public double ConvertTime(double inTime, [MarshalAs(UnmanagedType.LPStr)] string sourceTZ, [MarshalAs(UnmanagedType.LPStr)] string destTZ)
    {
        DateTime inDT = DateTime.FromOADate(inTime);//convert decimal date and time value to a DateTime object.
        DateTime sourceDT = DateTime.SpecifyKind(inDT, DateTimeKind.Unspecified);//specify that the time represents a local time and save into a new object.
        TimeZoneInfo sourceTZI = TimeZoneInfo.FindSystemTimeZoneById(sourceTZ);
        TimeZoneInfo destTZI = TimeZoneInfo.FindSystemTimeZoneById(destTZ);
        DateTime destDT = TimeZoneInfo.ConvertTime(sourceDT, sourceTZI, destTZI);//convert time. FAILS WHEN DateTimeKind.Local is specified
        double outTime = destDT.ToOADate();//extract the decimal date & time value
        return outTime;
    }
}

}

您所说的"双精度日期和时间值"也称为"OLE自动化日期",或简称"OADate"。

OADates不传递任何时区信息。他们只是1899年12月30日之后的一个点在某个未知的日历上。此外,它们的编码方式有一些奇怪的怪癖(请参阅MSDN文档中的注释),这使得它们有点不受欢迎。如果可能的话,我会尽量避免。

尽管如此,您应该始终将它们视为未指定的。事实上,你调用的FromOADate方法已经返回了Unspecified类型,所以根本没有理由调用DateTime.SpecifyKind。简而言之,您的函数应该是:

public double ConvertTime(double inTime, string sourceTZ, string destTZ)
{
    DateTime sourceDT = DateTime.FromOADate(inTime);
    TimeZoneInfo sourceTZI = TimeZoneInfo.FindSystemTimeZoneById(sourceTZ);
    TimeZoneInfo destTZI = TimeZoneInfo.FindSystemTimeZoneById(destTZ);
    DateTime destDT = TimeZoneInfo.ConvertTime(sourceDT, sourceTZI, destTZI);
    return destDT.ToOADate();
}
然而,如果您的用例要求假设输入时间是计算机的本地时区,那么您将创建一个不同的方法:
public double ConvertFromLocalTime(double inTime, string destTZ)
{
    DateTime sourceDT = DateTime.FromOADate(inTime);
    TimeZoneInfo sourceTZI = TimeZoneInfo.Local;
    TimeZoneInfo destTZI = TimeZoneInfo.FindSystemTimeZoneById(destTZ);
    DateTime destDT = TimeZoneInfo.ConvertTime(sourceDT, sourceTZI, destTZI);
    return destDT.ToOADate();
}

您仍然不需要指定本地类型,因为当将未指定的DateTime传递给TimeZoneInfo.ConvertTime时,它会假设该值是以源时区表示的—在本例中是您的本地时区。虽然本地类型可以在这里工作,但不是必需的。

至于为什么在尝试本地类型时出现错误,我认为这是您遇到的错误:

"无法完成转换,因为提供的DateTime没有正确设置Kind属性。例如,当Kind属性为DateTimeKind时。本地,源时区必须为TimeZoneInfo.Local."

正如错误解释的那样,除非源时区特别取自DateTimeKind.Local,否则您无法将具有本地类型的DateTime传递给TimeZoneInfo.ConvertTime

源时区ID与本地时区ID匹配是不够的,因为TimeZoneInfo.Local有一个特殊的情况,考虑到"自动调整夏令时时钟"选项。有关详细信息,请参阅MSDN文档。换句话说:

TimeZoneInfo.Local != TimeZoneInfo.FindSystemTimeZoneById(TimeZoneInfo.Local.Id)
最后,我认为你误解了DateTimeKind。你说:

要正确转换时间,必须指定该值是本地时间并属于某个时区。

DateTimeKind.LocalTimeZoneInfo.Local中的"本地"特指对于运行代码的计算机来说是本地的。这不是本地区域,是本地区域。如果DateTime与其他时区绑定,而不是UTC或计算机自己的本地时区设置,则使用DateTimeKind.Unspecified

最新更新