For instantaneous DateTime tracking, I am using a DateTimeOffset
datatype. The following function adds the user corresponding TimeZone ID offset to the UTC DateTime property of DateTimeOffset
According to the documentation, UtcDateTime
will perform both a time zone conversion and a type conversion on a DateTimeOffset
. The following code does not though. Why is the conversion not taking place?
Function to add TimeSpan offset,
public static DateTimeOffset GetUtcDateTime (DateTime sourceDateTime, string timeZoneId) {
TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById (timeZoneId);
TimeSpan offset = timeZone.GetUtcOffset (sourceDateTime);
DateTimeOffset utcTime = new DateTimeOffset (sourceDateTime, offset);
return utcTime;
}
and here where I am trying to convert,
DateTimeOffset utcDate = (DateTime.UtcNow);
DateTime fromUtc = utcDate.DateTime;
DateTimeOffset UtcDate = StaticHandlers.GetUtcDateTime (fromUtc, "America/Los_Angeles");
Console.WriteLine ("UTC now is {0} and UTC Date LA is {1} and UtcDateTime LA is {2}", utcDate, UtcDate, utcDate.UtcDateTime);
the output is,
UTC now is 5/8/18 6:43:37 AM +00:00 and and UTC Date LA is 5/8/18 6:43:37 AM -07:00 UtcDateTime LA is 5/8/18 6:43:37 AM
update,
I want to preserve both UTC and the user offset for tracking purposes. DST matters in this context. The example below shows what I am talking about.
DateTime currentDateTime = DateTime.Now;
DateTime beforeDST_LA = new DateTime (2018, 3, 11, 0, 0, 0);
DateTime afterDST_LA = new DateTime (2018, 3, 12, 0, 0, 0);
TimeSpan offsetCurrent = tzi.GetUtcOffset (currentDateTime);
TimeSpan offsetBeforeDST = tzi.GetUtcOffset (beforeDST_LA);
TimeSpan offsetAfterDST = tzi.GetUtcOffset (afterDST_LA);
Console.WriteLine ("Current offset is {0} before DST is {1} and After DST is {2}", offsetCurrent, offsetBeforeDST, offsetAfterDST);
Current offset is -07:00:00 before DST is -08:00:00 and After DST is -07:00:00
First, I would not call your function
GetUtcDateTime
, because that's not what it does. It is trying to get aDateTimeOffset
for a specific time zone for a specific time, so call it something likeGetDateTimeOffset
.The main concept you're missing in your code is that
DateTime
has.Kind
property, which sets aDateTimeKind
value. The kind is taken into consideration by several places in your code:GetUtcOffset
will convertUtc
orLocal
kinds to the zone provided before determining the offset.new DateTimeOffset
(the constructor) will error if the kind and the offset conflict, if you provide an offset.When you assign a
DateTime
to aDateTimeOffset
, the implicit conversion is evaluating the kind.When you call
.DateTime
from theDateTimeOffset
, the kind will always beUnspecified
- regardless of the offset.If you take all of this into account, you'll realize you need to check the kind yourself before calling
GetUtcOffset
. If it's notUnspecified
then you'll need to convert it to the specified time zone before getting the offset.Now that this is handled, turn to the next set of problems, which is where you call it.
In line 1, the implicit cast from
DateTime
toDateTimeOffset
sets the offset to00:00
- becauseDateTime.UtcNow
has.Kind == DateTimeKind.Utc
.In line 2, the call to the
.DateTime
property setsfromUtc.Kind == DateTimeKind.Unspecified
. Essentially, you've stripped away the kind.So instead of this, just pass
DateTime.UtcNow
directly into the function. The kind will persist, and it will all work - now that theKind
is recognized and the conversion is happening inside the function.All that said, if your original values are all
DateTimeOffset
(example,DateTimeOffset.UtcNow
) then you don't need that function at all. Just callTimeZoneInfo.ConvertTime
with theDateTimeOffset
directly.