Условный оператор Null с параметром DateTimeOffset, допускающим значение NULL

Среда: Visual Studio 2015.

Часовой пояс: UTC + 7:00, Бангкок.

Проблема: в переменной DateTimeOffset, допускающей значение NULL (DateTimeOffset?), использование оператора Null Conditional приводит к исключению, т. е. он по-прежнему вызывает метод, даже если значение равно NULL, т. е. (значение как DateTimeOffset?)? ToLocalTime ( ), он вызывает ToLocalTime и вызывает исключение.

Запрос: я могу разрешить его, не используя условный оператор Null или используя GetValueOrDefault вместо оператора, но я хочу понять, почему он преобразуется в исключение для всех UTC + часовых поясов, он хорошо работает с UTC - Часовые пояса

Код:

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

введите здесь описание изображения

ИЗМЕНИТЬ:

Я понимаю, что есть много способов исправить код, т.е.

    DateTime dateTimeMinimum = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);

Вот мой запрос, когда я не использую условный оператор null

var a1 = value as DateTimeOffset?;

Это не приводит к исключению. Это потому, что условный оператор null разворачивает переменную в следующем блоге

http://www.ninjacrab.com/2016/09/11/c-how-the-null-conditional-operator-works-with-nullable-types/

Мне больше интересно понять, почему он ломается, когда я использую условный оператор null, и работает, когда я просто выполняю приведение при использовании оператора as без использования DateTimeKind.Utc

РЕДАКТИРОВАТЬ2:

Это конструктор DateTimeOffset (код платформы .NET), который прерывается в методе ValidateOffset. Источник - 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);
        }

person Learner    schedule 24.09.2016    source источник
comment
каково значение смещения при возникновении этого исключения? Сообщение об ошибке предполагает, что результирующий год был либо меньше 0, либо больше 10K.   -  person pquest    schedule 24.09.2016
comment
DateTime.MinValue   -  person Learner    schedule 24.09.2016
comment
что такое значение смещения?   -  person pquest    schedule 24.09.2016
comment
Значение смещения отсутствует, поскольку я использую значение DateTime. Проблема в том, что если значение равно NULL, он не должен вызывать метод ToLocalTime, поскольку он использует NULL CONDITIONAL OPERATOR.   -  person Learner    schedule 24.09.2016
comment
Вы используете смещение, поэтому явно есть значение смещения. Проблема не в том, что смещение равно нулю, а в том, что это недопустимое значение.   -  person pquest    schedule 24.09.2016
comment
Почему вы делаете DateTime как DateTimeOffset? Это всегда дает null, по крайней мере, в моем случае.   -  person Andrew    schedule 24.09.2016
comment
@WPF_Learn, вы не получаете исключение нулевой ссылки, вы получаете выход за пределы допустимого диапазона, поэтому нулевое значение не является причиной вашей проблемы.   -  person Andrew    schedule 25.09.2016
comment
@Andrew, я использую условный оператор Null, что означает, что он даже не должен вызывать метод ToLocalTime, который приводит к исключению вне диапазона. См. Переменную a1 в моем коде, если она работает, то условный оператор null тоже должен работать вместо того, чтобы приводить к исключению вне диапазона   -  person Learner    schedule 25.09.2016
comment
Пожалуйста, оставляйте свои комментарии, когда голосуете против.   -  person Learner    schedule 25.09.2016
comment
Опять же, @WPF_Learn, value as DateTimeOffset? не является допустимым преобразованием, он всегда будет давать значение null, не используйте это. Оператор as имеет свойство не генерировать исключения, когда он недопустим, поэтому вы получаете там null. Если вы замените это на var a1 = (DateTimeOffset?)value, вы получите соответствующий System.InvalidCastException.   -  person Andrew    schedule 25.09.2016


Ответы (2)


Проблема в том, что минимальная дата находится в UTC 0, поэтому, если вы хотите, чтобы это но с положительным временем UTC, это означает, что в UTC 0 это будет раньше, чем минимально возможный DateTime.

Проще говоря, вы не можете создать это (минимальная дата UTC +1):

new DateTimeOffset(DateTime.MinValue, new TimeSpan(1, 0, 0))

Потому что это создаст DateTimeOffset на 31 декабря -0001 23:00 по всемирному координированному времени.

Исключение происходит именно здесь:

var dto = <something null> ?? (DateTime)value;

Поскольку dto выводится как DateTimeOffset, вы выполняете (DateTimeOffset)(DateTime)value, а затем возникает исключение. Это приведение пытается создать отрицательную дату, которую невозможно представить.

Попробуйте этот код, чтобы убедиться, что проблема не связана с пустой переменной:

var dateTimeMinimum = DateTime.MinValue;
var value = (object)dateTimeMinimum; // Mimic the WPF converter behavior
DateTimeOffset dto = (DateTime)value;

ОБНОВЛЕНИЕ

Поскольку вы все еще мне не верите, попробуйте следующее:

var dto = (value as DateTimeOffset?)?.ToLocalTime() ?? new DateTimeOffset();

Это не подведет. Почему? Потому что ToLocalTime не выполняется и никогда не выполнялось, и все это время не удавалось, как я вам сказал, приведение с минимального DateTime к DateTimeOffset с положительным часовым поясом.


Кстати, вы не можете просто преобразовать DateTime в DateTimeOffset? с помощью оператора as; который всегда будет возвращать null. Этот оператор предназначен для совместимых классов.

В конце концов, даже исправив это, я думаю, что ваш код слишком сложен для понимания и поддержки. Что именно вы здесь пытаетесь сделать?

person Andrew    schedule 24.09.2016
comment
Это то, что мне нужно, чтобы получить значение NULL, и для этого я использую условный оператор Null. Если это Null, я пытаюсь разобрать его на DateTime. - person Learner; 25.09.2016
comment
var dto = (значение как DateTimeOffset?) ?. ToLocalTime () ?? (DateTime) значение; - person Learner; 25.09.2016
comment
Но это не ноль, вы получите исключение ... Минимальное значение DateTime в формате UTC, преобразованное в LocalTime (UTC +7), вызывает это исключение. Нет нулевой проблемы. - person Andrew; 25.09.2016
comment
Следующие работы: var dateTimeMinimum = DateTime.MinValue; значение переменной = (объект) dateTimeMinimum; // Имитация поведения конвертера WPF var a1 = value as DateTimeOffset ?; // Это работает - person Learner; 25.09.2016
comment
Когда я не использую условный оператор Null, это не приводит к исключению. Смотрите код до переменной a1 в моем коде. - person Learner; 25.09.2016
comment
@WPF_Learn, пожалуйста, постарайтесь понять, что я вам объясняю. Вы настаиваете на нулевом значении, но проблема не в этом. Пожалуйста, проверьте обновление, которое я добавил к своему ответу. Надеюсь, вы поймете, что происходит. - person Andrew; 25.09.2016
comment
Полагаю, это означает, что вы поняли проблему и сумели заставить ее работать? :) - person Andrew; 27.09.2016

Это не имеет ничего общего с оператором, допускающим значение NULL.

Это вызовет ту же ошибку:

var dto2 = new DateTimeOffset(dateTimeMinimum);

Смещение слишком велико при использовании DateTime.Min, если вы измените его на DateTime.Now, ваш код будет работать.

person Ziv Weissman    schedule 24.09.2016
comment
Спасибо за ответ. Если вы видите переменную a1 в моем коде, это приводит к значению NULL, и тогда выполнение не переходит в блок if. Таким же образом, если значение переменной приводится к DateTimeOffset ?, оно должно быть NULL, это не должно приводить к исключению. - person Learner; 24.09.2016
comment
Оператор as в этом случае всегда будет возвращать NULL, потому что вы пытаетесь выполнить преобразование между DateTime и DateTimeOffset, которые не связаны между собой классами, в то время как оператор NULL пытается получить значение. Я не так хорошо знаком с закулисным оператором null Roslyn, но я считаю, что ваша строка действует как явное приведение - person Ziv Weissman; 25.09.2016