Подтвердить что ты не робот

Проблема вокруг даты utc - TimeZoneInfo.ConvertTimeToUtc приводит к изменению даты

Наличие проблемы, при которой дата, которую я хочу сохранить, изменяется с выбранной даты на экране, если пользователи выбирают часовой пояс, который находится впереди x количество часов. Например. они выбирают UTC + 2 Athens и дату 25/02/2016 из всплывающего окна календаря, тогда записанная дата будет 24/02/2016. Я сузил рассуждения до того, что выбранное время datetime записывается как, например, 25/02/2016 00:00:00 и с 2-часовым смещением, это берет его на 24/02/2016 22:00:00 Никогда не работая с часовыми поясами раньше или датами/временем UTC, это очень запутывает.

Вот код -

     oObject.RefDate = itTimeAndDate.ParseDateAndTimeNoUTCMap(Request, TextBox_RefDate.Text);
        if (!string.IsNullOrEmpty(oObject.TimeZoneDetails))
        {
TimeZoneInfo oTimeZone = TimeZoneInfo.FindSystemTimeZoneById(oObject.TimeZoneDetails);
            oObject.RefDate = itTimeAndDate.GetUTCUsingTimeZone(oTimeZone, oObject.RefDate);  
        }

RefDate будет приравниваться к чем-то вроде 25/02/2016 00:00:00 после возврата из ParseDateAndTimeNoUTCMap * (код ниже) *

static public itDateTime ParseDateAndTimeNoUTCMap(HttpRequest oTheRequest, string sValue)
        {
            DateTime? oResult = ParseDateAndTimeNoUTCMapNull(oTheRequest, sValue);
            if (oResult != null)
                return new itDateTime(oResult.Value);
            return null;
        }

        /// <summary>
        /// Translate a string that has been entered by a user to a UTC date / time - mapping using the
        /// current time zone
        /// </summary>
        /// <param name="oTheRequest">Request context</param>
        /// <param name="sValue">Date / time string entered by a user</param>
        /// <returns>UTC date / time object</returns>
        static public DateTime? ParseDateAndTimeNoUTCMapNull(HttpRequest oTheRequest, string sValue)
        {
            try
            {
                if (string.IsNullOrEmpty(sValue))
                    return null;
                sValue = sValue.Trim();
                if (string.IsNullOrEmpty(sValue))
                    return null;

                if (oTheRequest != null)
                {
                    const DateTimeStyles iStyles = DateTimeStyles.AllowInnerWhite | DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite;
                    // Create array of CultureInfo objects
                    CultureInfo[] aCultures = new CultureInfo[oTheRequest.UserLanguages.Length + 1];
                    for (int iCount = oTheRequest.UserLanguages.GetLowerBound(0); iCount <= oTheRequest.UserLanguages.GetUpperBound(0);
                         iCount++)
                    {
                        string sLocale = oTheRequest.UserLanguages[iCount];
                        if (!string.IsNullOrEmpty(sLocale))
                        {

                            // Remove quality specifier, if present.
                            if (sLocale.Contains(";"))
                                sLocale = sLocale.Substring(0, sLocale.IndexOf(';'));
                            try
                            {
                                aCultures[iCount] = new CultureInfo(sLocale, false);
                            }
                            catch (Exception) { }
                        }
                        else
                        {
                            aCultures[iCount] = CultureInfo.CurrentCulture;
                        }
                    }
                    aCultures[oTheRequest.UserLanguages.Length] = CultureInfo.InvariantCulture;
                    // Parse input using each culture.
                    foreach (CultureInfo culture in aCultures)
                    {
                        DateTime oInputDate;
                        if (DateTime.TryParse(sValue, culture.DateTimeFormat, iStyles, out oInputDate))
                            return oInputDate;
                    }
                }
                return DateTime.Parse(sValue);
            }
            catch (Exception)
            {
            }
            return null;
        }

После возврата из вышеперечисленного выполняются следующие строки:

TimeZoneInfo oTimeZone = TimeZoneInfo.FindSystemTimeZoneById(oObject.TimeZoneDetails);
        oObject.RefDate = itTimeAndDate.GetUTCUsingTimeZone(oTimeZone, oObject.RefDate);  

В рамках GetUTCUsingTimeZone проблема кажется мне самой.

static public itDateTime GetUTCUsingTimeZone(TimeZoneInfo oTimeZone, itDateTime oDateTime)
    {
        if (oDateTime == null || oTimeZone == null)
         return oDateTime;
         DateTime oLocal = DateTime.SpecifyKind(oDateTime.Value, DateTimeKind.Unspecified);
        DateTime oResult = TimeZoneInfo.ConvertTimeToUtc(oLocal, oTimeZone);

        return new itDateTime(oResult);
    }

Я проверил TimezoneInfo для значения смещения, а oResult всегда равен oLocal param - смещению. Итак, 25/02/2016 00:00:00 с 3-часовым смещением будет равняться 24/02/2016 21:00:00 Когда смещение -часов, оно переходит в другое прямое, поэтому o Result = oLocal + the offset, если это имеет смысл. Поэтому основная проблема смены даты не возникает в этих случаях.

Очевидно, это не то, что я хочу. Я хочу, чтобы дата была выбрана пользователем для их часовой пояс. Кто-нибудь видел что-то подобное раньше? Любое возможное решение?

Я не совсем уверен, что я сделал неправильно.

4b9b3361

Ответ 1

Исправление заключалось в следующем: после захвата значения из db и перед его повторным отображением -

static public itDateTime FixUTCUsingTimeZone(TimeZoneInfo oTimeZone, itDateTime oDateTime)
{
    if (oDateTime == null || oTimeZone == null)
        return oDateTime;

    DateTime oTime = DateTime.SpecifyKind(oDateTime.Value, DateTimeKind.Unspecified);
    DateTime oResult = TimeZoneInfo.ConvertTimeFromUtc(oTime, oTimeZone);

    return new itDateTime(oResult);

}

Итак, по существу, просто выполняем обратное преобразование ConvertTimeToUtc, выполненное ранее. Не уверен, почему это не было сделано изначально, но там вы идете.

Ответ 2

Если вам нужно поддерживать правильный часовой пояс, вы должны использовать тип DateTimeOffset вместо типа DateTime.

DateTimeOffset поддерживает смещение от UTC, поэтому вы никогда не теряете информацию о часовом поясе и не имеете много полезных методов, например UtcDateTime

Из уст лошадей:

https://msdn.microsoft.com/en-us/library/system.datetimeoffset(v=vs.110).aspx

https://docs.microsoft.com/en-us/dotnet/standard/datetime/choosing-between-datetime