diff options
author | Tarek Mahmoud Sayed <tarekms@microsoft.com> | 2018-04-19 13:14:12 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-04-19 13:14:12 -0700 |
commit | 3b3ef2add7b99d8acc3f0a156527d45a6f9e295b (patch) | |
tree | 934d5413fb8bf723e7d0b0790663415d2ecfe41b /src | |
parent | fa89b5a850adcbb9dd7451ee883d35961a529919 (diff) | |
download | coreclr-3b3ef2add7b99d8acc3f0a156527d45a6f9e295b.tar.gz coreclr-3b3ef2add7b99d8acc3f0a156527d45a6f9e295b.tar.bz2 coreclr-3b3ef2add7b99d8acc3f0a156527d45a6f9e295b.zip |
Fix reading Time zone rules using Julian days (#17672)
Diffstat (limited to 'src')
-rw-r--r-- | src/mscorlib/Resources/Strings.resx | 61 | ||||
-rw-r--r-- | src/mscorlib/shared/System/TimeZoneInfo.Unix.cs | 139 |
2 files changed, 138 insertions, 62 deletions
diff --git a/src/mscorlib/Resources/Strings.resx b/src/mscorlib/Resources/Strings.resx index 5d37b50f3c..49c8e93249 100644 --- a/src/mscorlib/Resources/Strings.resx +++ b/src/mscorlib/Resources/Strings.resx @@ -1,17 +1,17 @@ <?xml version="1.0" encoding="utf-8"?> <root> - <!-- - Microsoft ResX Schema - + <!-- + Microsoft ResX Schema + Version 2.0 - - The primary goals of this format is to allow a simple XML format - that is mostly human readable. The generation and parsing of the - various data types are done through the TypeConverter classes + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes associated with the data types. - + Example: - + ... ado.net/XML headers & schema ... <resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="version">2.0</resheader> @@ -26,36 +26,36 @@ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> <comment>This is a comment</comment> </data> - - There are any number of "resheader" rows that contain simple + + There are any number of "resheader" rows that contain simple name/value pairs. - - Each data row contains a name, and value. The row also contains a - type or mimetype. Type corresponds to a .NET class that support - text/value conversion through the TypeConverter architecture. - Classes that don't support this are serialized and stored with the + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the mimetype set. - - The mimetype is used for serialized objects, and tells the - ResXResourceReader how to depersist the object. This is currently not + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not extensible. For a given mimetype the value must be set accordingly: - - Note - application/x-microsoft.net.object.binary.base64 is the format - that the ResXResourceWriter will generate, however the reader can + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can read any of the formats listed below. - + mimetype: application/x-microsoft.net.object.binary.base64 - value : The object must be serialized with + value : The object must be serialized with : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. - + mimetype: application/x-microsoft.net.object.soap.base64 - value : The object must be serialized with + value : The object must be serialized with : System.Runtime.Serialization.Formatters.Soap.SoapFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.bytearray.base64 - value : The object must be serialized into a byte array + value : The object must be serialized into a byte array : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> @@ -2734,8 +2734,11 @@ <data name="InvalidTimeZone_InvalidRegistryData" xml:space="preserve"> <value>The time zone ID '{0}' was found on the local computer, but the registry information was corrupt.</value> </data> - <data name="InvalidTimeZone_JulianDayNotSupported" xml:space="preserve"> - <value>Julian dates in POSIX strings are unsupported.</value> + <data name="InvalidTimeZone_InvalidJulianDay" xml:space="preserve"> + <value>Invalid Julian day in POSIX strings.</value> + </data> + <data name="InvalidTimeZone_NJulianDayNotSupported" xml:space="preserve"> + <value>Julian n day in POSIX strings is not supported.</value> </data> <data name="InvalidTimeZone_NoTTInfoStructures" xml:space="preserve"> <value>There are no ttinfo structures in the tzfile. At least one ttinfo structure is required in order to construct a TimeZoneInfo object.</value> diff --git a/src/mscorlib/shared/System/TimeZoneInfo.Unix.cs b/src/mscorlib/shared/System/TimeZoneInfo.Unix.cs index 2dcaf67bfd..cc386a111f 100644 --- a/src/mscorlib/shared/System/TimeZoneInfo.Unix.cs +++ b/src/mscorlib/shared/System/TimeZoneInfo.Unix.cs @@ -1117,6 +1117,37 @@ namespace System return result; } + private static DateTime ParseTimeOfDay(string time) + { + DateTime timeOfDay; + TimeSpan? timeOffset = TZif_ParseOffsetString(time); + if (timeOffset.HasValue) + { + // This logic isn't correct and can't be corrected until https://github.com/dotnet/corefx/issues/2618 is fixed. + // Some time zones use time values like, "26", "144", or "-2". + // This allows the week to sometimes be week 4 and sometimes week 5 in the month. + // For now, strip off any 'days' in the offset, and just get the time of day correct + timeOffset = new TimeSpan(timeOffset.Value.Hours, timeOffset.Value.Minutes, timeOffset.Value.Seconds); + if (timeOffset.Value < TimeSpan.Zero) + { + timeOfDay = new DateTime(1, 1, 2, 0, 0, 0); + } + else + { + timeOfDay = new DateTime(1, 1, 1, 0, 0, 0); + } + + timeOfDay += timeOffset.Value; + } + else + { + // default to 2AM. + timeOfDay = new DateTime(1, 1, 1, 2, 0, 0); + } + + return timeOfDay; + } + private static TransitionTime TZif_CreateTransitionTimeFromPosixRule(string date, string time) { if (string.IsNullOrEmpty(date)) @@ -1138,48 +1169,90 @@ namespace System throw new InvalidTimeZoneException(SR.Format(SR.InvalidTimeZone_UnparseablePosixMDateString, date)); } - DateTime timeOfDay; - TimeSpan? timeOffset = TZif_ParseOffsetString(time); - if (timeOffset.HasValue) - { - // This logic isn't correct and can't be corrected until https://github.com/dotnet/corefx/issues/2618 is fixed. - // Some time zones use time values like, "26", "144", or "-2". - // This allows the week to sometimes be week 4 and sometimes week 5 in the month. - // For now, strip off any 'days' in the offset, and just get the time of day correct - timeOffset = new TimeSpan(timeOffset.Value.Hours, timeOffset.Value.Minutes, timeOffset.Value.Seconds); - if (timeOffset.Value < TimeSpan.Zero) - { - timeOfDay = new DateTime(1, 1, 2, 0, 0, 0); - } - else - { - timeOfDay = new DateTime(1, 1, 1, 0, 0, 0); - } - - timeOfDay += timeOffset.Value; - } - else + return TransitionTime.CreateFloatingDateRule(ParseTimeOfDay(time), month, week, day); + } + else + { + if (date[0] != 'J') { - // default to 2AM. - timeOfDay = new DateTime(1, 1, 1, 2, 0, 0); + // should be n Julian day format which we don't support. + // + // This specifies the Julian day, with n between 0 and 365. February 29 is counted in leap years. + // + // n would be a relative number from the begining of the year. which should handle if the + // the year is a leap year or not. + // + // In leap year, n would be counted as: + // + // 0 30 31 59 60 90 335 365 + // |-------Jan--------|-------Feb--------|-------Mar--------|....|-------Dec--------| + // + // while in non leap year we'll have + // + // 0 30 31 58 59 89 334 364 + // |-------Jan--------|-------Feb--------|-------Mar--------|....|-------Dec--------| + // + // + // For example if n is specified as 60, this means in leap year the rule will start at Mar 1, + // while in non leap year the rule will start at Mar 2. + // + // If we need to support n format, we'll have to have a floating adjustment rule support this case. + + throw new InvalidTimeZoneException(SR.InvalidTimeZone_NJulianDayNotSupported); } - return TransitionTime.CreateFloatingDateRule(timeOfDay, month, week, day); + // Julian day + TZif_ParseJulianDay(date, out int month, out int day); + return TransitionTime.CreateFixedDateRule(ParseTimeOfDay(time), month, day); } - else + } + + /// <summary> + /// Parses a string like Jn or n into month and day values. + /// </summary> + /// <returns> + /// true if the parsing succeeded; otherwise, false. + /// </returns> + private static void TZif_ParseJulianDay(string date, out int month, out int day) + { + // Jn + // This specifies the Julian day, with n between 1 and 365.February 29 is never counted, even in leap years. + Debug.Assert(date[0] == 'J'); + Debug.Assert(!String.IsNullOrEmpty(date)); + month = day = 0; + + int index = 1; + + if (index >= date.Length || ((uint)(date[index] - '0') > '9'-'0')) { - // Jn - // This specifies the Julian day, with n between 1 and 365.February 29 is never counted, even in leap years. + throw new InvalidTimeZoneException(SR.InvalidTimeZone_InvalidJulianDay); + } + + int julianDay = 0; - // n - // This specifies the Julian day, with n between 0 and 365.February 29 is counted in leap years. + do + { + julianDay = julianDay * 10 + (int) (date[index] - '0'); + index++; + } while (index < date.Length && ((uint)(date[index] - '0') <= '9'-'0')); - // These two rules cannot be expressed with the current AdjustmentRules - // One of them *could* be supported if we relaxed the TransitionTime validation rules, and allowed - // "IsFixedDateRule = true, Month = 0, Day = n" to mean the nth day of the year, picking one of the rules above + int[] days = GregorianCalendarHelper.DaysToMonth365; - throw new InvalidTimeZoneException(SR.InvalidTimeZone_JulianDayNotSupported); + if (julianDay == 0 || julianDay > days[days.Length - 1]) + { + throw new InvalidTimeZoneException(SR.InvalidTimeZone_InvalidJulianDay); } + + int i = 1; + while (i < days.Length && julianDay > days[i]) + { + i++; + } + + Debug.Assert(i > 0 && i < days.Length); + + month = i; + day = julianDay - days[i - 1]; } /// <summary> |