Reputation: 1542
Environment: Visual Studio 2015
TimeZone:: UTC + 7:00, Bangkok
Issue: On DateTimeOffset nullable varialbe (DateTimeOffset?), the use of Null Conditional operator results in exception i.e. it still calls the method even if the value is NULL i.e. (value as DateTimeOffset?)?.ToLocalTime(), it calls the ToLocalTime and results in exception.
Query: I can resolve it by not using the Null conditional operator or using the GetValueOrDefault instead of the operator but I want to understand why it resutls in exception with all UTC + TimeZones, it works well with UTC - TimeZones
Code:
var dateTimeMinimum = DateTime.MinValue;
var value = (object)dateTimeMinimum; // Mimic the WPF converter behavior
var a1 = value as DateTimeOffset?; // This works
if (a1 != null)// This works as it won't execute the code in the 'if'loop
{
var b1 = (a1 as DateTimeOffset?)?.ToLocalTime();
}
var dto = (value as DateTimeOffset?)?.ToLocalTime() ?? (DateTime)value;// This breaks with following exception
EDIT:
I understand there are many ways to fix the code i.e.
DateTime dateTimeMinimum = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
Here's my query though, when I do not use the null conditional operator
var a1 = value as DateTimeOffset?;
It does not result in exception. Is it because the null conditional operator unwraps the variable per following blog
http://www.ninjacrab.com/2016/09/11/c-how-the-null-conditional-operator-works-with-nullable-types/
I am more interested in understanding why it breaks when I use null conditional operator and works when I simple cast if using the 'as' operator without using the DateTimeKind.Utc
EDIT2:
This is the constructor of DateTimeOffset (.NET framework code) and it breaks at ValidateOffset method. Source - http://referencesource.microsoft.com/#mscorlib/system/datetimeoffset.cs,68b4bb83ce8d1c31
// Constructs a DateTimeOffset from a DateTime. For Local and Unspecified kinds,
// extracts the local offset. For UTC, creates a UTC instance with a zero offset.
public DateTimeOffset(DateTime dateTime) {
TimeSpan offset;
if (dateTime.Kind != DateTimeKind.Utc) {
// Local and Unspecified are both treated as Local
offset = TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime);
}
else {
offset = new TimeSpan(0);
}
m_offsetMinutes = ValidateOffset(offset);
m_dateTime = ValidateDate(dateTime, offset);
}
Upvotes: 0
Views: 8147
Reputation: 7880
The issue is that the minimum date is at UTC 0, so if you want that but with a positive UTC, that means that at UTC 0 it will be earlier than the minimum possible DateTime
.
To put it simply, you can't create this (minimum date UTC +1):
new DateTimeOffset(DateTime.MinValue, new TimeSpan(1, 0, 0))
Because this would create a DateTimeOffset
for Dec 31st -0001 11:00PM UTC.
The exception occurs exactly here:
var dto = <something null> ?? (DateTime)value;
As dto
is inferred as DateTimeOffset
, there you are doing (DateTimeOffset)(DateTime)value
, and then is when the exception is thrown. That cast tries to create the negative date, which can't be represented.
Try this code to confirm that the issue is not related to a null variable:
var dateTimeMinimum = DateTime.MinValue;
var value = (object)dateTimeMinimum; // Mimic the WPF converter behavior
DateTimeOffset dto = (DateTime)value;
UPDATE
As you still don't believe me, try this:
var dto = (value as DateTimeOffset?)?.ToLocalTime() ?? new DateTimeOffset();
This won't fail. Why? Because ToLocalTime
is not being executed and never was, and what failed all this time was what I told you, the cast from minimum DateTime
to DateTimeOffset
with positive timezone.
BTW, you can't just convert a DateTime
to a DateTimeOffset?
with the as
operator; that will always return null. That operator is for compatible clases.
In the end, even fixing this, I think your code is too difficult to understand and maintain. What are you exactly trying to do here?
Upvotes: 1
Reputation: 4526
This has nothing to do with the nullable operator.
This will cause the same error:
var dto2 = new DateTimeOffset(dateTimeMinimum);
The offset is too big when using DateTime.Min, If you change it to DateTime.Now, your code will work.
Upvotes: 0