Learner
Learner

Reputation: 1542

Null conditional operator with nullable DateTimeOffset

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

enter image description here

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

Answers (2)

Andrew
Andrew

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

Ziv Weissman
Ziv Weissman

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

Related Questions