return(iReturnHash);
}
-#ifndef FEATURE_CORECLR // FCalls used by System.TimeZone
-
-FCIMPL0(LONG, COMNlsInfo::nativeGetTimeZoneMinuteOffset)
-{
- FCALL_CONTRACT;
-
- TIME_ZONE_INFORMATION timeZoneInfo;
-
- GetTimeZoneInformation(&timeZoneInfo);
-
- //
- // In Win32, UTC = local + offset. So for Pacific Standard Time, offset = 8.
- // In NLS+, Local time = UTC + offset. So for PST, offset = -8.
- // So we have to reverse the sign here.
- //
- return (timeZoneInfo.Bias * -1);
-}
-FCIMPLEND
-
-FCIMPL0(Object*, COMNlsInfo::nativeGetStandardName)
-{
- FCALL_CONTRACT;
-
- STRINGREF refRetVal = NULL;
- HELPER_METHOD_FRAME_BEGIN_RET_1(refRetVal);
-
- TIME_ZONE_INFORMATION timeZoneInfo;
- GetTimeZoneInformation(&timeZoneInfo);
-
- refRetVal = StringObject::NewString(timeZoneInfo.StandardName);
-
- HELPER_METHOD_FRAME_END();
- return OBJECTREFToObject(refRetVal);
-}
-FCIMPLEND
-
-FCIMPL0(Object*, COMNlsInfo::nativeGetDaylightName)
-{
- FCALL_CONTRACT;
-
- STRINGREF refRetVal = NULL;
- HELPER_METHOD_FRAME_BEGIN_RET_1(refRetVal);
-
- TIME_ZONE_INFORMATION timeZoneInfo;
- GetTimeZoneInformation(&timeZoneInfo);
- // Instead of returning null when daylight saving is not used, now we return the same result as the OS.
- //In this case, if daylight saving time is used, the standard name is returned.
-
-#if 0
- if (result == TIME_ZONE_ID_UNKNOWN || timeZoneInfo.DaylightDate.wMonth == 0) {
- // If daylight saving time is not used in this timezone, return null.
- //
- // Windows NT/2000: TIME_ZONE_ID_UNKNOWN is returned if daylight saving time is not used in
- // the current time zone, because there are no transition dates.
- //
- // For Windows 9x, a zero in the wMonth in DaylightDate means daylight saving time
- // is not specified.
- //
- // If the current timezone uses daylight saving rule, but user unchekced the
- // "Automatically adjust clock for daylight saving changes", the value
- // for DaylightBias will be 0.
- return (I2ARRAYREF)NULL;
- }
-#endif // 0
-
- refRetVal = StringObject::NewString(timeZoneInfo.DaylightName);
-
- HELPER_METHOD_FRAME_END();
- return OBJECTREFToObject(refRetVal);
-}
-FCIMPLEND
-
-FCIMPL1(Object*, COMNlsInfo::nativeGetDaylightChanges, int year)
-{
- FCALL_CONTRACT;
-
- I2ARRAYREF pResultArray = NULL;
- HELPER_METHOD_FRAME_BEGIN_RET_1(pResultArray);
-
- TIME_ZONE_INFORMATION timeZoneInfo;
- DWORD result = GetTimeZoneInformation(&timeZoneInfo);
-
- if (result == TIME_ZONE_ID_UNKNOWN || timeZoneInfo.DaylightBias == 0
- || timeZoneInfo.DaylightDate.wMonth == 0
- ) {
- // If daylight saving time is not used in this timezone, return null.
- //
- // If the current timezone uses daylight saving rule, but user unchekced the
- // "Automatically adjust clock for daylight saving changes", the value
- // for DaylightBias will be 0.
- goto lExit;
- }
-
- pResultArray = (I2ARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_I2, 17);
-
- //
- // The content of timeZoneInfo.StandardDate is 8 words, which
- // contains year, month, day, dayOfWeek, hour, minute, second, millisecond.
- //
- memcpyNoGCRefs(pResultArray->m_Array,
- (LPVOID)&timeZoneInfo.DaylightDate,
- 8 * sizeof(INT16));
-
- //
- // The content of timeZoneInfo.DaylightDate is 8 words, which
- // contains year, month, day, dayOfWeek, hour, minute, second, millisecond.
- //
- memcpyNoGCRefs(((INT16*)pResultArray->m_Array) + 8,
- (LPVOID)&timeZoneInfo.StandardDate,
- 8 * sizeof(INT16));
-
- ((INT16*)pResultArray->m_Array)[16] = (INT16)timeZoneInfo.DaylightBias * -1;
-
-lExit: ;
- HELPER_METHOD_FRAME_END();
- return OBJECTREFToObject(pResultArray);
-}
-FCIMPLEND
-
-#endif // FEATURE_CORECLR
-
inline BOOL IsInvariantLocale(STRINGREF localeName)
{
return localeName->GetStringLength() == 0;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
- //
- // Currently, this is the only supported timezone.
- // The values of the timezone is from the current system timezone setting in the
- // control panel.
- //
#if FEATURE_CORECLR
[Obsolete("System.CurrentSystemTimeZone has been deprecated. Please investigate the use of System.TimeZoneInfo.Local instead.")]
#endif
[Serializable]
- internal class CurrentSystemTimeZone : TimeZone {
- // <BUGBUG>BUGBUG :
- // One problem is when user changes the current timezone. We
- // are not able to update currentStandardName/currentDaylightName/
- // currentDaylightChanges.
- // We need WM_TIMECHANGE to do this or use
- // RegNotifyChangeKeyValue() to monitor </BUGBUG>
- //
- private const long TicksPerMillisecond = 10000;
- private const long TicksPerSecond = TicksPerMillisecond * 1000;
- private const long TicksPerMinute = TicksPerSecond * 60;
-
+ internal class CurrentSystemTimeZone : TimeZone
+ {
// The per-year information is cached in in this instance value. As a result it can
// be cleaned up by CultureInfo.ClearCachedData, which will clear the instance of this object
private Hashtable m_CachedDaylightChanges = new Hashtable();
private String m_standardName;
private String m_daylightName;
- [System.Security.SecuritySafeCritical] // auto-generated
- internal CurrentSystemTimeZone() {
- m_ticksOffset = nativeGetTimeZoneMinuteOffset() * TicksPerMinute;
- m_standardName = null;
- m_daylightName = null;
+ internal CurrentSystemTimeZone()
+ {
+ TimeZoneInfo local = TimeZoneInfo.Local;
+
+ m_ticksOffset = local.BaseUtcOffset.Ticks;
+ m_standardName = local.StandardName;
+ m_daylightName = local.DaylightName;
}
- public override String StandardName {
- [System.Security.SecuritySafeCritical] // auto-generated
- get {
- if (m_standardName == null) {
- m_standardName = nativeGetStandardName();
- }
- return (m_standardName);
+ public override String StandardName
+ {
+ get
+ {
+ return m_standardName;
}
}
- public override String DaylightName {
- [System.Security.SecuritySafeCritical] // auto-generated
- get {
- if (m_daylightName == null) {
- m_daylightName = nativeGetDaylightName();
- if (m_daylightName == null) {
- m_daylightName = this.StandardName;
- }
- }
- return (m_daylightName);
+ public override String DaylightName
+ {
+ get
+ {
+ return m_daylightName;
}
}
- internal long GetUtcOffsetFromUniversalTime(DateTime time, ref Boolean isAmbiguousLocalDst) {
+ internal long GetUtcOffsetFromUniversalTime(DateTime time, ref Boolean isAmbiguousLocalDst)
+ {
// Get the daylight changes for the year of the specified time.
TimeSpan offset = new TimeSpan(m_ticksOffset);
DaylightTime daylightTime = GetDaylightChanges(time.Year);
isAmbiguousLocalDst= false;
- if (daylightTime == null || daylightTime.Delta.Ticks == 0) {
+ if (daylightTime == null || daylightTime.Delta.Ticks == 0)
+ {
return offset.Ticks;
}
DateTime endTime = daylightTime.End - offset - daylightTime.Delta;
DateTime ambiguousStart;
DateTime ambiguousEnd;
- if (daylightTime.Delta.Ticks > 0) {
+
+ if (daylightTime.Delta.Ticks > 0)
+ {
ambiguousStart = endTime - daylightTime.Delta;
ambiguousEnd = endTime;
- } else {
+ }
+ else
+ {
ambiguousStart = startTime;
ambiguousEnd = startTime - daylightTime.Delta;
}
Boolean isDst = false;
- if (startTime > endTime) {
+ if (startTime > endTime)
+ {
// In southern hemisphere, the daylight saving time starts later in the year, and ends in the beginning of next year.
// Note, the summer in the southern hemisphere begins late in the year.
isDst = (time < endTime || time >= startTime);
}
- else {
+ else
+ {
// In northern hemisphere, the daylight saving time starts in the middle of the year.
- isDst = (time>=startTime && time<endTime);
+ isDst = (time >= startTime && time < endTime);
}
- if (isDst) {
+
+ if (isDst)
+ {
offset += daylightTime.Delta;
// See if the resulting local time becomes ambiguous. This must be captured here or the
// DateTime will not be able to round-trip back to UTC accurately.
- if (time >= ambiguousStart && time < ambiguousEnd ) {
+ if (time >= ambiguousStart && time < ambiguousEnd )
+ {
isAmbiguousLocalDst = true;
}
}
return offset.Ticks;
}
- public override DateTime ToLocalTime(DateTime time) {
- if (time.Kind == DateTimeKind.Local) {
+ public override DateTime ToLocalTime(DateTime time)
+ {
+ if (time.Kind == DateTimeKind.Local)
+ {
return time;
}
Boolean isAmbiguousLocalDst = false;
Int64 offset = GetUtcOffsetFromUniversalTime(time, ref isAmbiguousLocalDst);
long tick = time.Ticks + offset;
- if (tick>DateTime.MaxTicks) {
+ if (tick > DateTime.MaxTicks)
+ {
return new DateTime(DateTime.MaxTicks, DateTimeKind.Local);
}
- if (tick<DateTime.MinTicks) {
+ if (tick < DateTime.MinTicks)
+ {
return new DateTime(DateTime.MinTicks, DateTimeKind.Local);
}
- return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst);
+ return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst);
}
// Private object for locking instead of locking on a public type for SQL reliability work.
private static Object s_InternalSyncObject;
- private static Object InternalSyncObject {
- get {
- if (s_InternalSyncObject == null) {
+ private static Object InternalSyncObject
+ {
+ get
+ {
+ if (s_InternalSyncObject == null)
+ {
Object o = new Object();
Interlocked.CompareExchange<Object>(ref s_InternalSyncObject, o, null);
}
}
}
-
- [System.Security.SecuritySafeCritical] // auto-generated
- public override DaylightTime GetDaylightChanges(int year) {
- if (year < 1 || year > 9999) {
+ public override DaylightTime GetDaylightChanges(int year)
+ {
+ if (year < 1 || year > 9999)
+ {
throw new ArgumentOutOfRangeException(nameof(year), Environment.GetResourceString("ArgumentOutOfRange_Range", 1, 9999));
}
Contract.EndContractBlock();
- Object objYear = (Object)year;
-
- if (!m_CachedDaylightChanges.Contains(objYear)) {
- lock (InternalSyncObject) {
+ Object objYear = (Object) year;
- if (!m_CachedDaylightChanges.Contains(objYear)) {
+ if (!m_CachedDaylightChanges.Contains(objYear))
+ {
+ DaylightTime currentDaylightChanges = null;
- //
- // rawData is an array of 17 short (16 bit) numbers.
- // The first 8 numbers contains the
- // year/month/day/dayOfWeek/hour/minute/second/millisecond for the starting time of daylight saving time.
- // The next 8 numbers contains the
- // year/month/day/dayOfWeek/hour/minute/second/millisecond for the ending time of daylight saving time.
- // The last short number is the delta to the standard offset in minutes.
- //
- short[] rawData = nativeGetDaylightChanges(year);
+ if (TimeZoneInfo.Local.SupportsDaylightSavingTime)
+ {
+ DateTime start;
+ DateTime end;
+ TimeSpan delta;
- if (rawData == null) {
- //
- // If rawData is null, it means that daylight saving time is not used
- // in this timezone. So keep currentDaylightChanges as the empty array.
- //
- m_CachedDaylightChanges.Add(objYear, new DaylightTime(DateTime.MinValue, DateTime.MinValue, TimeSpan.Zero));
- } else {
- DateTime start;
- DateTime end;
- TimeSpan delta;
+ foreach (var rule in TimeZoneInfo.Local.GetAdjustmentRules())
+ {
+ if (rule.DateStart.Year <= year && rule.DateEnd.Year >= year && rule.DaylightDelta != TimeSpan.Zero)
+ {
+ start = TimeZoneInfo.TransitionTimeToDateTime(year, rule.DaylightTransitionStart);
+ end = TimeZoneInfo.TransitionTimeToDateTime(year, rule.DaylightTransitionEnd);
+ delta = rule.DaylightDelta;
- //
- // Store the start of daylight saving time.
- //
-
- start = GetDayOfWeek(year, (rawData[0] != 0), rawData[1], rawData[2],
- rawData[3],
- rawData[4], rawData[5], rawData[6], rawData[7]);
+ currentDaylightChanges = new DaylightTime(start, end, delta);
+ break;
+ }
+ }
+ }
- //
- // Store the end of daylight saving time.
- //
- end = GetDayOfWeek(year, (rawData[8] != 0), rawData[9], rawData[10],
- rawData[11],
- rawData[12], rawData[13], rawData[14], rawData[15]);
+ if (currentDaylightChanges == null)
+ {
+ currentDaylightChanges = new DaylightTime(DateTime.MinValue, DateTime.MinValue, TimeSpan.Zero);
+ }
- delta = new TimeSpan(rawData[16] * TicksPerMinute);
- DaylightTime currentDaylightChanges = new DaylightTime(start, end, delta);
- m_CachedDaylightChanges.Add(objYear, currentDaylightChanges);
- }
+ lock (InternalSyncObject)
+ {
+ if (!m_CachedDaylightChanges.Contains(objYear))
+ {
+ m_CachedDaylightChanges.Add(objYear, currentDaylightChanges);
}
}
}
return result;
}
- public override TimeSpan GetUtcOffset(DateTime time) {
- if (time.Kind == DateTimeKind.Utc) {
+ public override TimeSpan GetUtcOffset(DateTime time)
+ {
+ if (time.Kind == DateTimeKind.Utc)
+ {
return TimeSpan.Zero;
}
- else {
+ else
+ {
return new TimeSpan(TimeZone.CalculateUtcOffset(time, GetDaylightChanges(time.Year)).Ticks + m_ticksOffset);
}
}
- //
- // Return the (numberOfSunday)th day of week in a particular year/month.
- //
- private static DateTime GetDayOfWeek(int year, bool fixedDate, int month, int targetDayOfWeek, int numberOfSunday, int hour, int minute, int second, int millisecond) {
- DateTime time;
-
- if (fixedDate) {
- //
- // Create a Fixed-Date transition time based on the supplied parameters
- // For Fixed-Dated transition times, the 'numberOfSunday' parameter actually
- // represents the day of the month.
- //
-
- // if the day is out of range for the month then use the last day of the month
- int day = DateTime.DaysInMonth(year, month);
-
- time = new DateTime(year, month, (day < numberOfSunday) ? day : numberOfSunday,
- hour, minute, second, millisecond, DateTimeKind.Local);
- }
- else if (numberOfSunday <= 4) {
- //
- // Get the (numberOfSunday)th Sunday.
- //
-
- time = new DateTime(year, month, 1, hour, minute, second, millisecond, DateTimeKind.Local);
-
- int dayOfWeek = (int)time.DayOfWeek;
- int delta = targetDayOfWeek - dayOfWeek;
- if (delta < 0) {
- delta += 7;
- }
- delta += 7 * (numberOfSunday - 1);
-
- if (delta > 0) {
- time = time.AddDays(delta);
- }
- } else {
- //
- // If numberOfSunday is greater than 4, we will get the last sunday.
- //
- Calendar cal = GregorianCalendar.GetDefaultInstance();
- time = new DateTime(year, month, cal.GetDaysInMonth(year, month), hour, minute, second, millisecond, DateTimeKind.Local);
- // This is the day of week for the last day of the month.
- int dayOfWeek = (int)time.DayOfWeek;
- int delta = dayOfWeek - targetDayOfWeek;
- if (delta < 0) {
- delta += 7;
- }
-
- if (delta > 0) {
- time = time.AddDays(-delta);
- }
- }
- return (time);
- }
-
- [System.Security.SecurityCritical] // auto-generated
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal extern static int nativeGetTimeZoneMinuteOffset();
- [System.Security.SecurityCritical] // auto-generated
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal extern static String nativeGetDaylightName();
- [System.Security.SecurityCritical] // auto-generated
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal extern static String nativeGetStandardName();
- [System.Security.SecurityCritical] // auto-generated
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal extern static short[] nativeGetDaylightChanges(int year);
} // class CurrentSystemTimeZone
}
}
}
+#if PLATFORM_UNIX
+ // The rules we use in Unix cares mostly about the start and end dates but doesn’t fill the transition start and end info.
+ // as the rules now is public, we should fill it properly so the caller doesn’t have to know how we use it internally
+ // and can use it as it is used in Windows
- // ---- SECTION: public methods --------------*
+ private AdjustmentRule[] GetFilledRules()
+ {
+ Contract.Assert(m_adjustmentRules != null, "m_adjustmentRules expected to be not null");
+ AdjustmentRule[] rules = new AdjustmentRule[m_adjustmentRules.Length];
+
+ for (int i = 0; i < m_adjustmentRules.Length; i++)
+ {
+ var rule = m_adjustmentRules[i];
+ var start = rule.DateStart.Kind == DateTimeKind.Utc ?
+ new DateTime(TimeZoneInfo.ConvertTime(rule.DateStart, this).Ticks, DateTimeKind.Unspecified) :
+ rule.DateStart;
+ var end = rule.DateEnd.Kind == DateTimeKind.Utc ?
+ new DateTime(TimeZoneInfo.ConvertTime(rule.DateEnd, this).Ticks - 1, DateTimeKind.Unspecified) :
+ rule.DateEnd;
+
+ var startTransition = TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, start.Hour, start.Minute, start.Second), start.Month, start.Day);
+ var endTransition = TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, end.Hour, end.Minute, end.Second), end.Month, end.Day);
+
+ rules[i] = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(start.Date, end.Date, rule.DaylightDelta, startTransition, endTransition);
+ }
+
+ return rules;
+ }
+#endif // PLATFORM_UNIX
+ // ---- SECTION: public methods --------------*
//
// GetAdjustmentRules -
//
// returns a cloned array of AdjustmentRule objects
//
- public AdjustmentRule [] GetAdjustmentRules() {
- if (m_adjustmentRules == null) {
- return new AdjustmentRule[0];
+ public AdjustmentRule [] GetAdjustmentRules()
+ {
+ if (m_adjustmentRules == null)
+ {
+ return Array.Empty<AdjustmentRule>();
}
- else {
- return (AdjustmentRule[])m_adjustmentRules.Clone();
+ else
+ {
+#if PLATFORM_UNIX
+ return GetFilledRules();
+#else
+ return (AdjustmentRule[]) m_adjustmentRules.Clone();
+#endif // PLATFORM_UNIX
}
}
-
//
// GetAmbiguousTimeOffsets -
//
// As we get the associated rule using the adjusted targetTime, we should use the adjusted year (targetTime.Year) too as after adding the baseOffset,
// sometimes the year value can change if the input datetime was very close to the beginning or the end of the year. Examples of such cases:
- // \93Libya Standard Time\94 when used with the date 2011-12-31T23:59:59.9999999Z
+ // Libya Standard Time when used with the date 2011-12-31T23:59:59.9999999Z
// "W. Australia Standard Time" used with date 2005-12-31T23:59:00.0000000Z
DateTime targetTime = time + baseOffset;
year = targetTime.Year;
//
// Helper function that converts a year and TransitionTime into a DateTime
//
- static private DateTime TransitionTimeToDateTime(Int32 year, TransitionTime transitionTime) {
+ internal static DateTime TransitionTimeToDateTime(Int32 year, TransitionTime transitionTime) {
DateTime value;
DateTime timeOfDay = transitionTime.TimeOfDay;