summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Erhardt <eerhardt@microsoft.com>2015-08-11 17:19:55 -0500
committerEric Erhardt <eerhardt@microsoft.com>2015-08-14 16:26:49 -0500
commit1aacfed602bb116042cb170e4790260714cd3b50 (patch)
tree07f8cd7fde94e4a4e5df7d1cd986f37d35d2994c
parent467e441764ae6eafd9f03961ab08422d8b96c19e (diff)
downloadcoreclr-1aacfed602bb116042cb170e4790260714cd3b50.tar.gz
coreclr-1aacfed602bb116042cb170e4790260714cd3b50.tar.bz2
coreclr-1aacfed602bb116042cb170e4790260714cd3b50.zip
Make use of TimeZoneInfo.CachedData on Linux.
Fix https://github.com/dotnet/corefx/issues/2497.
-rw-r--r--src/mscorlib/src/System/TimeZoneInfo.cs223
1 files changed, 94 insertions, 129 deletions
diff --git a/src/mscorlib/src/System/TimeZoneInfo.cs b/src/mscorlib/src/System/TimeZoneInfo.cs
index a9b1e8e501..2d475b4677 100644
--- a/src/mscorlib/src/System/TimeZoneInfo.cs
+++ b/src/mscorlib/src/System/TimeZoneInfo.cs
@@ -225,11 +225,11 @@ namespace System {
return kind;
}
-#if FEATURE_WIN32_REGISTRY
public Dictionary<string, TimeZoneInfo> m_systemTimeZones;
public ReadOnlyCollection<TimeZoneInfo> m_readOnlySystemTimeZones;
public bool m_allSystemTimeZonesRead;
+#if FEATURE_WIN32_REGISTRY
[System.Security.SecuritySafeCritical]
private static TimeZoneInfo GetCurrentOneYearLocal() {
// load the data from the OS
@@ -696,16 +696,11 @@ namespace System {
static public DateTimeOffset ConvertTimeBySystemTimeZoneId(DateTimeOffset dateTimeOffset, String destinationTimeZoneId) {
return ConvertTime(dateTimeOffset, FindSystemTimeZoneById(destinationTimeZoneId));
}
-#endif // FEATURE_WIN32_REGISTRY
-
-#if FEATURE_WIN32_REGISTRY
static public DateTime ConvertTimeBySystemTimeZoneId(DateTime dateTime, String destinationTimeZoneId) {
return ConvertTime(dateTime, FindSystemTimeZoneById(destinationTimeZoneId));
}
-#endif // FEATURE_WIN32_REGISTRY
-#if FEATURE_WIN32_REGISTRY
static public DateTime ConvertTimeBySystemTimeZoneId(DateTime dateTime, String sourceTimeZoneId, String destinationTimeZoneId) {
if (dateTime.Kind == DateTimeKind.Local && String.Compare(sourceTimeZoneId, TimeZoneInfo.Local.Id, StringComparison.OrdinalIgnoreCase) == 0) {
// TimeZoneInfo.Local can be cleared by another thread calling TimeZoneInfo.ClearCachedData.
@@ -941,8 +936,6 @@ namespace System {
return m_id.ToUpper(CultureInfo.InvariantCulture).GetHashCode();
}
-
-#if FEATURE_WIN32_REGISTRY
//
// GetSystemTimeZones -
//
@@ -963,29 +956,8 @@ namespace System {
lock (cachedData) {
if (cachedData.m_readOnlySystemTimeZones == null) {
- PermissionSet permSet = new PermissionSet(PermissionState.None);
- permSet.AddPermission(new RegistryPermission(RegistryPermissionAccess.Read, c_timeZonesRegistryHivePermissionList));
- permSet.Assert();
-
- using (RegistryKey reg = Registry.LocalMachine.OpenSubKey(
- c_timeZonesRegistryHive,
-#if FEATURE_MACL
- RegistryKeyPermissionCheck.Default,
- System.Security.AccessControl.RegistryRights.ReadKey
-#else
- false
-#endif
- )) {
-
- if (reg != null) {
- foreach (string keyName in reg.GetSubKeyNames()) {
- TimeZoneInfo value;
- Exception ex;
- TryGetTimeZone(keyName, false, out value, out ex, cachedData); // populate the cache
- }
- }
- cachedData.m_allSystemTimeZonesRead = true;
- }
+ PopulateAllSystemTimeZones(cachedData);
+ cachedData.m_allSystemTimeZonesRead = true;
List<TimeZoneInfo> list;
if (cachedData.m_systemTimeZones != null) {
@@ -1005,8 +977,42 @@ namespace System {
}
return cachedData.m_readOnlySystemTimeZones;
}
-#endif // FEATURE_WIN32_REGISTRY
+ private static void PopulateAllSystemTimeZones(CachedData cachedData)
+ {
+#if FEATURE_WIN32_REGISTRY
+ PermissionSet permSet = new PermissionSet(PermissionState.None);
+ permSet.AddPermission(new RegistryPermission(RegistryPermissionAccess.Read, c_timeZonesRegistryHivePermissionList));
+ permSet.Assert();
+
+ using (RegistryKey reg = Registry.LocalMachine.OpenSubKey(
+ c_timeZonesRegistryHive,
+#if FEATURE_MACL
+ RegistryKeyPermissionCheck.Default,
+ System.Security.AccessControl.RegistryRights.ReadKey
+#else
+ false
+#endif
+ )) {
+
+ if (reg != null) {
+ foreach (string keyName in reg.GetSubKeyNames()) {
+ TimeZoneInfo value;
+ Exception ex;
+ TryGetTimeZone(keyName, false, out value, out ex, cachedData); // populate the cache
+ }
+ }
+ }
+#elif PLATFORM_UNIX // FEATURE_WIN32_REGISTRY
+ string timeZoneDirectory = GetTimeZoneDirectory();
+ foreach (string timeZoneId in GetTimeZoneIds(timeZoneDirectory))
+ {
+ TimeZoneInfo value;
+ Exception ex;
+ TryGetTimeZone(timeZoneId, false, out value, out ex, cachedData); // populate the cache
+ }
+#endif // FEATURE_WIN32_REGISTRY
+ }
//
// HasSameRules -
@@ -2119,7 +2125,7 @@ namespace System {
// the data from the Win32 API
return GetLocalTimeZoneFromWin32Data(timeZoneInformation, dstDisabled);
-#else // FEATURE_WIN32_REGISTRY
+#elif PLATFORM_UNIX // FEATURE_WIN32_REGISTRY
// Without Registry support, create the TimeZoneInfo from a TZ file
return GetLocalTimeZoneFromTzFile();
#endif // FEATURE_WIN32_REGISTRY
@@ -2127,25 +2133,10 @@ namespace System {
#if PLATFORM_UNIX
- static public TimeZoneInfo FindSystemTimeZoneById(string id)
+ private static TimeZoneInfoResult TryGetTimeZoneByFile(string id, out TimeZoneInfo value, out Exception e)
{
- if (id == null)
- {
- throw new ArgumentNullException("id");
- }
- else if (id.Length == 0 || id.Contains("\0"))
- {
- throw new TimeZoneNotFoundException(Environment.GetResourceString("TimeZoneNotFound_MissingData", id));
- }
-
- // Special case for Utc since there is code that expects only 1 instance of the UTC time zone info to be created.
- // see CachedData.GetCorrespondingKind()
- if (string.Equals(id, c_utcId, StringComparison.OrdinalIgnoreCase))
- {
- return TimeZoneInfo.Utc;
- }
-
- // UNIXTODO: Issue #2497 use CachedData
+ value = null;
+ e = null;
string timeZoneDirectory = GetTimeZoneDirectory();
string timeZoneFilePath = Path.Combine(timeZoneDirectory, id);
@@ -2154,64 +2145,36 @@ namespace System {
{
rawData = File.ReadAllBytes(timeZoneFilePath);
}
- catch (UnauthorizedAccessException e)
+ catch (UnauthorizedAccessException ex)
{
- throw new SecurityException(Environment.GetResourceString("Security_CannotReadFileData", id), e);
- }
- catch (FileNotFoundException e)
- {
- throw new TimeZoneNotFoundException(Environment.GetResourceString("TimeZoneNotFound_MissingData", id), e);
+ e = ex;
+ return TimeZoneInfoResult.SecurityException;
}
- catch (DirectoryNotFoundException e)
+ catch (FileNotFoundException ex)
{
- throw new TimeZoneNotFoundException(Environment.GetResourceString("TimeZoneNotFound_MissingData", id), e);
+ e = ex;
+ return TimeZoneInfoResult.TimeZoneNotFoundException;
}
- catch (IOException e)
+ catch (DirectoryNotFoundException ex)
{
- throw new InvalidTimeZoneException(Environment.GetResourceString("InvalidTimeZone_InvalidFileData", id, timeZoneFilePath), e);
+ e = ex;
+ return TimeZoneInfoResult.TimeZoneNotFoundException;
}
-
- TimeZoneInfo result = GetTimeZoneFromTzData(rawData, id);
- if (result == null)
+ catch (IOException ex)
{
- throw new InvalidTimeZoneException(Environment.GetResourceString("InvalidTimeZone_InvalidFileData", id, timeZoneFilePath));
+ e = new InvalidTimeZoneException(Environment.GetResourceString("InvalidTimeZone_InvalidFileData", id, timeZoneFilePath), ex);
+ return TimeZoneInfoResult.InvalidTimeZoneException;
}
- return result;
- }
-
- /// <summary>
- /// Returns a collection of TimeZoneInfo instances containing all valid TimeZones
- /// from the local machine. The entries in the collection are sorted by
- /// 'DisplayName'.
- ///
- /// This method does *not* throw TimeZoneNotFoundException or
- /// InvalidTimeZoneException, but it can throw a SecurityException.
- /// </summary>
- static public ReadOnlyCollection<TimeZoneInfo> GetSystemTimeZones()
- {
- // UNIXTODO: Issue #2497 use CachedData
- List<TimeZoneInfo> timeZoneInfos = new List<TimeZoneInfo>();
+ value = GetTimeZoneFromTzData(rawData, id);
- string timeZoneDirectory = GetTimeZoneDirectory();
- foreach (string timeZoneId in GetTimeZoneIds(timeZoneDirectory))
+ if (value == null)
{
- if (!string.IsNullOrEmpty(timeZoneId))
- {
- try
- {
- TimeZoneInfo timeZoneInfo = FindSystemTimeZoneById(timeZoneId);
- timeZoneInfos.Add(timeZoneInfo);
- }
- catch (InvalidTimeZoneException) { }
- catch (TimeZoneNotFoundException) { }
- }
+ e = new InvalidTimeZoneException(Environment.GetResourceString("InvalidTimeZone_InvalidFileData", id, timeZoneFilePath));
+ return TimeZoneInfoResult.InvalidTimeZoneException;
}
- // sort and copy the TimeZoneInfo's into a ReadOnlyCollection for the user
- timeZoneInfos.Sort(new TimeZoneInfoComparer());
-
- return new ReadOnlyCollection<TimeZoneInfo>(timeZoneInfos);
+ return TimeZoneInfoResult.Success;
}
/// <summary>
@@ -2495,7 +2458,7 @@ namespace System {
return tzDirectory;
}
-#else // PLATFORM_UNIX
+#elif FEATURE_WIN32_REGISTRY // PLATFORM_UNIX
//
// GetLocalTimeZoneFromWin32Data -
@@ -2527,7 +2490,6 @@ namespace System {
}
#endif // PLATFORM_UNIX
-#if FEATURE_WIN32_REGISTRY
//
// FindSystemTimeZoneById -
//
@@ -2550,7 +2512,7 @@ namespace System {
if (id == null) {
throw new ArgumentNullException("id");
}
- else if (id.Length == 0 || id.Length > c_maxKeyLength || id.Contains("\0")) {
+ else if (!IsValidSystemTimeZoneId(id)) {
throw new TimeZoneNotFoundException(Environment.GetResourceString("TimeZoneNotFound_MissingData", id));
}
@@ -2569,17 +2531,37 @@ namespace System {
return value;
}
else if (result == TimeZoneInfoResult.InvalidTimeZoneException) {
+#if FEATURE_WIN32_REGISTRY
throw new InvalidTimeZoneException(Environment.GetResourceString("InvalidTimeZone_InvalidRegistryData", id), e);
+#elif PLATFORM_UNIX
+ Contract.Assert(e is InvalidTimeZoneException,
+ "TryGetTimeZone must create an InvalidTimeZoneException when it returns TimeZoneInfoResult.InvalidTimeZoneException");
+ throw e;
+#endif
}
else if (result == TimeZoneInfoResult.SecurityException) {
+#if FEATURE_WIN32_REGISTRY
throw new SecurityException(Environment.GetResourceString("Security_CannotReadRegistryData", id), e);
+#elif PLATFORM_UNIX
+ throw new SecurityException(Environment.GetResourceString("Security_CannotReadFileData", id), e);
+#endif
}
else {
throw new TimeZoneNotFoundException(Environment.GetResourceString("TimeZoneNotFound_MissingData", id), e);
}
}
+
+ private static bool IsValidSystemTimeZoneId(string id)
+ {
+ bool isValid = id.Length != 0 && !id.Contains("\0");
+
+#if FEATURE_WIN32_REGISTRY
+ isValid &= id.Length <= c_maxKeyLength;
#endif // FEATURE_WIN32_REGISTRY
+ return isValid;
+ }
+
//
// GetUtcOffset -
//
@@ -2639,7 +2621,7 @@ namespace System {
}
}
return baseOffset;
-#else
+#elif PLATFORM_UNIX
// Use the standard code path for the Macintosh since there isn't a faster way of handling current-year-only time zones
return GetUtcOffsetFromUtc(time, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst);
#endif // FEATURE_WIN32_REGISTRY
@@ -3037,10 +3019,7 @@ namespace System {
}
return true;
}
-#endif // FEATURE_WIN32_REGISTRY
-
-#if FEATURE_WIN32_REGISTRY
//
// TryCompareStandardDate -
//
@@ -3060,10 +3039,7 @@ namespace System {
&& timeZone.StandardDate.Second == registryTimeZoneInfo.StandardDate.Second
&& timeZone.StandardDate.Milliseconds == registryTimeZoneInfo.StandardDate.Milliseconds;
}
-#endif // FEATURE_WIN32_REGISTRY
-
-#if FEATURE_WIN32_REGISTRY
//
// TryCompareTimeZoneInformationToRegistry -
//
@@ -3139,10 +3115,7 @@ namespace System {
PermissionSet.RevertAssert();
}
}
-#endif // FEATURE_WIN32_REGISTRY
-
-#if FEATURE_WIN32_REGISTRY
//
// TryGetLocalizedNameByMuiNativeResource -
//
@@ -3222,11 +3195,7 @@ namespace System {
return String.Empty;
}
}
-#endif // FEATURE_WIN32_REGISTRY
-
-
-#if FEATURE_WIN32_REGISTRY
//
// TryGetLocalizedNameByNativeResource -
//
@@ -3255,10 +3224,7 @@ namespace System {
}
return String.Empty;
}
-#endif // FEATURE_WIN32_REGISTRY
-
-#if FEATURE_WIN32_REGISTRY
//
// TryGetLocalizedNamesByRegistryKey -
//
@@ -3309,11 +3275,7 @@ namespace System {
return true;
}
-#endif // FEATURE_WIN32_REGISTRY
-
-
-#if FEATURE_WIN32_REGISTRY
//
// TryGetTimeZoneByRegistryKey -
//
@@ -3436,7 +3398,6 @@ namespace System {
#endif // FEATURE_WIN32_REGISTRY
-#if FEATURE_WIN32_REGISTRY
//
// TryGetTimeZone -
//
@@ -3469,7 +3430,12 @@ namespace System {
// fall back to reading from the local machine
// when the cache is not fully populated
if (!cachedData.m_allSystemTimeZonesRead) {
+#if FEATURE_WIN32_REGISTRY
result = TryGetTimeZoneByRegistryKey(id, out match, out e);
+#elif PLATFORM_UNIX
+ result = TryGetTimeZoneByFile(id, out match, out e);
+#endif // FEATURE_WIN32_REGISTRY
+
if (result == TimeZoneInfoResult.Success) {
if (cachedData.m_systemTimeZones == null)
cachedData.m_systemTimeZones = new Dictionary<string, TimeZoneInfo>();
@@ -3496,7 +3462,6 @@ namespace System {
return result;
}
-#endif // FEATURE_WIN32_REGISTRY
#if PLATFORM_UNIX
// TZFILE(5) BSD File Formats Manual TZFILE(5)
@@ -3669,7 +3634,7 @@ namespace System {
TZifType transitionType = TZif_GetEarlyDateTransitionType(transitionTypes);
DateTime endTransitionDate = dts[index];
- TimeSpan transitionOffset = TZif_CalculateTranistionOffsetFromBase(transitionType.UtcOffset, timeZoneBaseUtcOffset);
+ TimeSpan transitionOffset = TZif_CalculateTransitionOffsetFromBase(transitionType.UtcOffset, timeZoneBaseUtcOffset);
TimeSpan daylightDelta = transitionType.IsDst ? transitionOffset : TimeSpan.Zero;
TimeSpan baseUtcDelta = transitionType.IsDst ? TimeSpan.Zero : transitionOffset;
@@ -3690,7 +3655,7 @@ namespace System {
DateTime endTransitionDate = dts[index];
- TimeSpan transitionOffset = TZif_CalculateTranistionOffsetFromBase(startTransitionType.UtcOffset, timeZoneBaseUtcOffset);
+ TimeSpan transitionOffset = TZif_CalculateTransitionOffsetFromBase(startTransitionType.UtcOffset, timeZoneBaseUtcOffset);
TimeSpan daylightDelta = startTransitionType.IsDst ? transitionOffset : TimeSpan.Zero;
TimeSpan baseUtcDelta = startTransitionType.IsDst ? TimeSpan.Zero : transitionOffset;
@@ -3740,7 +3705,7 @@ namespace System {
// just use the last transition as the rule which will be used until the end of time
TZifType transitionType = transitionTypes[typeOfLocalTime[index - 1]];
- TimeSpan transitionOffset = TZif_CalculateTranistionOffsetFromBase(transitionType.UtcOffset, timeZoneBaseUtcOffset);
+ TimeSpan transitionOffset = TZif_CalculateTransitionOffsetFromBase(transitionType.UtcOffset, timeZoneBaseUtcOffset);
TimeSpan daylightDelta = transitionType.IsDst ? transitionOffset : TimeSpan.Zero;
TimeSpan baseUtcDelta = transitionType.IsDst ? TimeSpan.Zero : transitionOffset;
@@ -3759,7 +3724,7 @@ namespace System {
index++;
}
- private static TimeSpan TZif_CalculateTranistionOffsetFromBase(TimeSpan transitionOffset, TimeSpan timeZoneBaseUtcOffset)
+ private static TimeSpan TZif_CalculateTransitionOffsetFromBase(TimeSpan transitionOffset, TimeSpan timeZoneBaseUtcOffset)
{
TimeSpan result = transitionOffset - timeZoneBaseUtcOffset;
@@ -3829,7 +3794,7 @@ namespace System {
if (parsedBaseOffset.HasValue)
{
TimeSpan baseOffset = parsedBaseOffset.Value.Negate(); // offsets are backwards in POSIX notation
- baseOffset = TZif_CalculateTranistionOffsetFromBase(baseOffset, timeZoneBaseUtcOffset);
+ baseOffset = TZif_CalculateTransitionOffsetFromBase(baseOffset, timeZoneBaseUtcOffset);
// having a daylightSavingsName means there is a DST rule
if (!string.IsNullOrEmpty(daylightSavingsName))
@@ -3844,8 +3809,8 @@ namespace System {
else
{
daylightSavingsTimeSpan = parsedDaylightSavings.Value.Negate(); // offsets are backwards in POSIX notation
- daylightSavingsTimeSpan = TZif_CalculateTranistionOffsetFromBase(daylightSavingsTimeSpan, timeZoneBaseUtcOffset);
- daylightSavingsTimeSpan = TZif_CalculateTranistionOffsetFromBase(daylightSavingsTimeSpan, baseOffset);
+ daylightSavingsTimeSpan = TZif_CalculateTransitionOffsetFromBase(daylightSavingsTimeSpan, timeZoneBaseUtcOffset);
+ daylightSavingsTimeSpan = TZif_CalculateTransitionOffsetFromBase(daylightSavingsTimeSpan, baseOffset);
}
TransitionTime dstStart = TZif_CreateTransitionTimeFromPosixRule(start, startTime);