Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue around utc date - TimeZoneInfo.ConvertTimeToUtc results in date change

Having an issue whereby the date I wish to save is changing from the onscreen selected date if the users selects a timezone that is ahead x number of hours. E.g. they choose UTC+2 Athens and date of 25/02/2016 from the calendar pop-up, then date recorded will be 24/02/2016. I've narrowed the reasoning down to the fact that the selected datetime is recorded as for example 25/02/2016 00:00:00 and with the 2 hour offset, this takes it to 24/02/2016 22:00:00 Having never worked with timezones before, or UTC dates/times, this is highly confusing.

Here is the code -

     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 would equate to something like 25/02/2016 00:00:00 once returned from ParseDateAndTimeNoUTCMap * (code below)*

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;
        }

Once returned from the above, the following lines are executed -

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

It is within GetUTCUsingTimeZone that the problem seems to occur to me.

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);
    }

I have checked TimezoneInfo for the offset value, and oResult always equates to the oLocal param - the offset. So 25/02/2016 00:00:00 with a 3 hour offset would equate to 24/02/2016 21:00:00 When the offset is -hours, it goes in the other direct, so oResult = oLocal + the offset, if that makes sense. So the main issue of the date changing is not occurring in those instances.

Obviously this is not what I want. I want the date to be what the user has selected, for their timezone. Has anyone seen something like this before? Any possible solution?

I'm not entirely sure what I've done wrong.

like image 524
Terry Delahunt Avatar asked Feb 24 '16 14:02

Terry Delahunt


People also ask

How do I print the date in UTC format?

Therefore, you must convert a DateTime value to UTC by calling the DateTime. ToUniversalTime method before formatting it. In contrast, DateTimeOffset values perform this conversion automatically; there is no need to call the DateTimeOffset.

How do I convert DateTimeOffset to local time?

In performing the conversion to local time, the method first converts the current DateTimeOffset object's date and time to Coordinated Universal Time (UTC) by subtracting the offset from the time. It then converts the UTC date and time to local time by adding the local time zone offset.

How do I get Central time zone in C#?

DateTime currentTime = TimeZoneInfo. ConvertTime(DateTime. Now, TimeZoneInfo. FindSystemTimeZoneById("Central Standard Time"));


1 Answers

If you need to maintain the correct timezone, you should be using the DateTimeOffset type instead of DateTime type.

DateTimeOffset maintains the offset from UTC so you never lose your timezone information and has a lot of useful methods like UtcDateTime

From the horses mouth:

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

like image 137
on3al Avatar answered Sep 19 '22 05:09

on3al