UTC日期问题 - TimeZoneInfo.ConvertTimeToUtc导致日期更改

17
遇到一个问题,当用户选择一个比当前时区提前x小时的时区时,我想保存的日期会从屏幕上选择的日期发生变化。 例如,他们选择 UTC + 2雅典 和 25/02/2016 的日期,然后记录的日期将是 24/02/2016 。 我已经缩小了原因,这是因为选择的日期时间被记录为例如 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 在从 ParseDateAndTimeNoUTCMap 返回时会类似于 25/02/2016 00:00:00 (参见下面的代码)。

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 - 偏移量。因此,带有3小时偏移量的25/02/2016 00:00:00将等同于24/02/2016 21:00:00。当偏移量为负数时,它会朝另一个方向进行,因此oResult = oLocal + 偏移量,如果这样说是有道理的。所以日期改变的主要问题并不发生在那些情况下。

显然,这不是我想要的。我想要的是用户选择的时间,以其所在时区为准。有人遇到过类似的问题吗?有可能的解决方案吗?

我不确定自己哪里做错了。


@Ian Kemp - 我通过对用户输入的数据执行ConvertTimeFromUtc操作(在执行ConvertTimeToUtc操作并保存到数据库后)来解决了这个问题,当用户返回到此页面时。 因此,页面显示用户选择的日期,而记录在数据库中的是其本地等效日期,如果这样说有意义的话。 感谢您的回复。 - Terry Delahunt
1
不太清楚。日期本身并没有时区的概念,只有日期时间值才有。您使用哪种UI组件?包含时区的意图是什么? - H H
11
如果您找到了答案,应该"自问自答"(即自己添加一个答案)。 - rogerdpack
5
“Triple double triple second” 是 @rogerdpack 的评论,虽然很多人这么做(他们不应该),把答案放在问题中是不好的做法并且被反对。相反,自我回答是鼓励的,所以请将答案放在回答中。 - Ajean
1
通常我会觉得那些“你应该使用X”之类的评论很烦人,但是在C#中编写了很多日期时间处理代码后,我发现NodaTime非常有用。或许值得一看。--http://nodatime.org/ - Ben Jenkinson
显示剩余3条评论
2个回答

1

0
解决方法是在从数据库中获取值并重新显示之前运行以下内容 -
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的相反操作。不确定为什么最初没有这样做,但就是这样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接