Pregunta

I've created some code to convert a double precision date & time value to another timezone. It gives behaviour that I just don't understand when DateTimeKind.Local is used in place of DateTimeKind.Unspecified.

My thought is that the double precision value passed in to the method ConvertTime is completely dislocated from it's geographical context. Surely for the time to be converted properly it is necessary to specify that the value is a local time and belongs to a certain timezone?

I'm trying to make certain that the local source time is properly converted to local destination time and observing Daylight Saving Time, irrespective of the time zone settings of the host computer.

Working from this notion I try to specify that the

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

but this results in the time not being converted. If I specify

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

then a conversion happens.

Can someone please explain why DateTimeKind.Local is not accepted as a valid specification in the time conversion and how to achieve what I'm attempting.

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;
    }
}

}

¿Fue útil?

Solución

What you refer to as "double precision date and time value" is also known as an "OLE Automation Date", or an "OADate" for short.

OADates do not convey any time zone information. They are just a point since December 30th 1899 in some unknown calendar. Additionally, there are some strange quirks about how they are encoded (see the remarks in these MSDN docs) that make them slightly undesirable. I would avoid them if at all possible.

Nonetheless, you should always treat them as unspecified. In fact, the FromOADate method you're calling already returns it as Unspecified kind, so there's no reason to call DateTime.SpecifyKind at all. In short, your function should simply be:

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();
}

However, if your use case calls for assuming that the input time is the computer's local time zone, then you would create a different method instead:

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();
}

You still do not need to specify the local kind, because when an unspecified DateTime is passed to TimeZoneInfo.ConvertTime it assumes that value is in terms of the source time zone - which is your local time zone in this case. While a local kind would work here, it's not required.

As to why you got the error when trying local kind, I assume this was the error you had:

"The conversion could not be completed because the supplied DateTime did not have the Kind property set correctly. For example, when the Kind property is DateTimeKind.Local, the source time zone must be TimeZoneInfo.Local."

As the error explains, you can't pass in a DateTime with local kind to TimeZoneInfo.ConvertTime unless the source timezone is specifically taken from DateTimeKind.Local.

It's not enough that the source time zone ID matches the local time zone ID, because TimeZoneInfo.Local has a special case of taking into account the "Automatically adjust clock for Daylight Saving Time" option. See these MSDN docs for details. In other words:

TimeZoneInfo.Local != TimeZoneInfo.FindSystemTimeZoneById(TimeZoneInfo.Local.Id)

Lastly, I think you misunderstand DateTimeKind. You said:

Surely for the time to be converted properly it is necessary to specify that the value is a local time and belongs to a certain timezone?

The "local" in DateTimeKind.Local and in TimeZoneInfo.Local specifically means local to the computer where the code is running. It's not a local zone, it's the local zone. If the DateTime is tied to some other time zone than UTC or the computer's own local time zone setting, then DateTimeKind.Unspecified is used.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top