The Labs \ Source Viewer \ SSCLI \ System \ CurrentSystemTimeZone

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. /*============================================================
  16. **
  17. ** Class: CurrentTimeZone
  18. **
  19. **
  20. ** Purpose:
  21. ** This class represents the current system timezone.  It is
  22. ** the only meaningful implementation of the TimeZone class
  23. ** available in this version.
  24. **
  25. ** The only TimeZone that we support in version 1 is the
  26. ** CurrentTimeZone as determined by the system timezone.
  27. **
  28. **
  29. ============================================================*/
  30. namespace System
  31. {
  32.     using System;
  33.     using System.Text;
  34.     using System.Threading;
  35.     using System.Collections;
  36.     using System.Globalization;
  37.     using System.Runtime.CompilerServices;
  38.    
  39.     //
  40.     // Currently, this is the only supported timezone.
  41.     // The values of the timezone is from the current system timezone setting in the
  42.     // control panel.
  43.     //
  44.     [Serializable()]
  45.     internal class CurrentSystemTimeZone : TimeZone
  46.     {
  47.         //
  48.         private const long TicksPerMillisecond = 10000;
  49.         private const long TicksPerSecond = TicksPerMillisecond * 1000;
  50.         private const long TicksPerMinute = TicksPerSecond * 60;
  51.        
  52.         // The per-year information is cached in in this instance value. As a result it can
  53.         // be cleaned up by CultureInfo.ClearCachedData, which will clear the instance of this object
  54.         private Hashtable m_CachedDaylightChanges = new Hashtable();
  55.        
  56.         // Standard offset in ticks to the Universal time if
  57.         // no daylight saving is in used.
  58.         // E.g. the offset for PST (Pacific Standard time) should be -8 * 60 * 60 * 1000 * 10000.
  59.         // (1 millisecond = 10000 ticks)
  60.         private long m_ticksOffset;
  61.         private string m_standardName;
  62.         private string m_daylightName;
  63.        
  64.         internal CurrentSystemTimeZone()
  65.         {
  66.             m_ticksOffset = nativeGetTimeZoneMinuteOffset() * TicksPerMinute;
  67.             m_standardName = null;
  68.             m_daylightName = null;
  69.         }
  70.        
  71.         public override string StandardName {
  72.             get {
  73.                 if (m_standardName == null) {
  74.                     m_standardName = nativeGetStandardName();
  75.                 }
  76.                 return (m_standardName);
  77.             }
  78.         }
  79.        
  80.         public override string DaylightName {
  81.             get {
  82.                 if (m_daylightName == null) {
  83.                     m_daylightName = nativeGetDaylightName();
  84.                     if (m_daylightName == null) {
  85.                         m_daylightName = this.StandardName;
  86.                     }
  87.                 }
  88.                 return (m_daylightName);
  89.             }
  90.         }
  91.        
  92.         internal long GetUtcOffsetFromUniversalTime(DateTime time, ref bool isAmbiguousLocalDst)
  93.         {
  94.             // Get the daylight changes for the year of the specified time.
  95.             TimeSpan offset = new TimeSpan(m_ticksOffset);
  96.             DaylightTime daylightTime = GetDaylightChanges(time.Year);
  97.             isAmbiguousLocalDst = false;
  98.            
  99.             if (daylightTime == null || daylightTime.Delta.Ticks == 0) {
  100.                 return offset.Ticks;
  101.             }
  102.            
  103.             // The start and end times represent the range of universal times that are in DST for that year.
  104.             // Within that there is an ambiguous hour, usually right at the end, but at the beginning in
  105.             // the unusual case of a negative daylight savings delta.
  106.             DateTime startTime = daylightTime.Start - offset;
  107.             DateTime endTime = daylightTime.End - offset - daylightTime.Delta;
  108.             DateTime ambiguousStart;
  109.             DateTime ambiguousEnd;
  110.             if (daylightTime.Delta.Ticks > 0) {
  111.                 ambiguousStart = endTime - daylightTime.Delta;
  112.                 ambiguousEnd = endTime;
  113.             }
  114.             else {
  115.                 ambiguousStart = startTime;
  116.                 ambiguousEnd = startTime - daylightTime.Delta;
  117.             }
  118.            
  119.             bool isDst = false;
  120.             if (startTime > endTime) {
  121.                 // In southern hemisphere, the daylight saving time starts later in the year, and ends in the beginning of next year.
  122.                 // Note, the summer in the southern hemisphere begins late in the year.
  123.                 isDst = (time < endTime || time >= startTime);
  124.             }
  125.             else {
  126.                 // In northern hemisphere, the daylight saving time starts in the middle of the year.
  127.                 isDst = (time >= startTime && time < endTime);
  128.             }
  129.             if (isDst) {
  130.                 offset += daylightTime.Delta;
  131.                
  132.                 // See if the resulting local time becomes ambiguous. This must be captured here or the
  133.                 // DateTime will not be able to round-trip back to UTC accurately.
  134.                 if (time >= ambiguousStart && time < ambiguousEnd) {
  135.                     isAmbiguousLocalDst = true;
  136.                 }
  137.             }
  138.             return offset.Ticks;
  139.         }
  140.        
  141.         public override DateTime ToLocalTime(DateTime time)
  142.         {
  143.             if (time.Kind == DateTimeKind.Local) {
  144.                 return time;
  145.             }
  146.             bool isAmbiguousLocalDst = false;
  147.             Int64 offset = GetUtcOffsetFromUniversalTime(time, ref isAmbiguousLocalDst);
  148.             long tick = time.Ticks + offset;
  149.             if (tick > DateTime.MaxTicks) {
  150.                 return new DateTime(DateTime.MaxTicks, DateTimeKind.Local);
  151.             }
  152.             if (tick < DateTime.MinTicks) {
  153.                 return new DateTime(DateTime.MinTicks, DateTimeKind.Local);
  154.             }
  155.             return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst);
  156.         }
  157.        
  158.         // Private object for locking instead of locking on a public type for SQL reliability work.
  159.         private static object s_InternalSyncObject;
  160.         private static object InternalSyncObject {
  161.             get {
  162.                 if (s_InternalSyncObject == null) {
  163.                     object o = new object();
  164.                     Interlocked.CompareExchange(ref s_InternalSyncObject, o, null);
  165.                 }
  166.                 return s_InternalSyncObject;
  167.             }
  168.         }
  169.        
  170.        
  171.         public override DaylightTime GetDaylightChanges(int year)
  172.         {
  173.             if (year < 1 || year > 9999) {
  174.                 throw new ArgumentOutOfRangeException("year", String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("ArgumentOutOfRange_Range"), 1, 9999));
  175.             }
  176.            
  177.             object objYear = (object)year;
  178.            
  179.             if (!m_CachedDaylightChanges.Contains(objYear)) {
  180.                 BCLDebug.Log("Getting TimeZone information for: " + objYear);
  181.                
  182.                 lock (InternalSyncObject) {
  183.                    
  184.                     if (!m_CachedDaylightChanges.Contains(objYear)) {
  185.                        
  186.                         //
  187.                         // rawData is an array of 17 short (16 bit) numbers.
  188.                         // The first 8 numbers contains the
  189.                         // year/month/day/dayOfWeek/hour/minute/second/millisecond for the starting time of daylight saving time.
  190.                         // The next 8 numbers contains the
  191.                         // year/month/day/dayOfWeek/hour/minute/second/millisecond for the ending time of daylight saving time.
  192.                         // The last short number is the delta to the standard offset in minutes.
  193.                         //
  194.                         short[] rawData = nativeGetDaylightChanges();
  195.                        
  196.                         if (rawData == null) {
  197.                             //
  198.                             // If rawData is null, it means that daylight saving time is not used
  199.                             // in this timezone. So keep currentDaylightChanges as the empty array.
  200.                             //
  201.                             m_CachedDaylightChanges.Add(objYear, new DaylightTime(DateTime.MinValue, DateTime.MinValue, TimeSpan.Zero));
  202.                         }
  203.                         else {
  204.                             DateTime start;
  205.                             DateTime end;
  206.                             TimeSpan delta;
  207.                            
  208.                             //
  209.                             // Store the start of daylight saving time.
  210.                             //
  211.                            
  212.                             start = GetDayOfWeek(year, rawData[1], rawData[2], rawData[3], rawData[4], rawData[5], rawData[6], rawData[7]);
  213.                            
  214.                             //
  215.                             // Store the end of daylight saving time.
  216.                             //
  217.                             end = GetDayOfWeek(year, rawData[9], rawData[10], rawData[11], rawData[12], rawData[13], rawData[14], rawData[15]);
  218.                            
  219.                             delta = new TimeSpan(rawData[16] * TicksPerMinute);
  220.                             DaylightTime currentDaylightChanges = new DaylightTime(start, end, delta);
  221.                             m_CachedDaylightChanges.Add(objYear, currentDaylightChanges);
  222.                         }
  223.                     }
  224.                 }
  225.             }
  226.            
  227.             DaylightTime result = (DaylightTime)m_CachedDaylightChanges[objYear];
  228.            
  229.             return result;
  230.         }
  231.        
  232.         public override TimeSpan GetUtcOffset(DateTime time)
  233.         {
  234.             if (time.Kind == DateTimeKind.Utc) {
  235.                 return TimeSpan.Zero;
  236.             }
  237.             else {
  238.                 return new TimeSpan(TimeZone.CalculateUtcOffset(time, GetDaylightChanges(time.Year)).Ticks + m_ticksOffset);
  239.             }
  240.         }
  241.        
  242.         //
  243.         // Return the (numberOfSunday)th day of week in a particular year/month.
  244.         //
  245.         private static DateTime GetDayOfWeek(int year, int month, int targetDayOfWeek, int numberOfSunday, int hour, int minute, int second, int millisecond)
  246.         {
  247.             DateTime time;
  248.            
  249.             if (numberOfSunday <= 4) {
  250.                 //
  251.                 // Get the (numberOfSunday)th Sunday.
  252.                 //
  253.                
  254.                 time = new DateTime(year, month, 1, hour, minute, second, millisecond, DateTimeKind.Local);
  255.                
  256.                 int dayOfWeek = (int)time.DayOfWeek;
  257.                 int delta = targetDayOfWeek - dayOfWeek;
  258.                 if (delta < 0) {
  259.                     delta += 7;
  260.                 }
  261.                 delta += 7 * (numberOfSunday - 1);
  262.                
  263.                 if (delta > 0) {
  264.                     time = time.AddDays(delta);
  265.                 }
  266.             }
  267.             else {
  268.                 //
  269.                 // If numberOfSunday is greater than 4, we will get the last sunday.
  270.                 //
  271.                 Calendar cal = GregorianCalendar.GetDefaultInstance();
  272.                 time = new DateTime(year, month, cal.GetDaysInMonth(year, month), hour, minute, second, millisecond, DateTimeKind.Local);
  273.                 // This is the day of week for the last day of the month.
  274.                 int dayOfWeek = (int)time.DayOfWeek;
  275.                 int delta = dayOfWeek - targetDayOfWeek;
  276.                 if (delta < 0) {
  277.                     delta += 7;
  278.                 }
  279.                
  280.                 if (delta > 0) {
  281.                     time = time.AddDays(-delta);
  282.                 }
  283.             }
  284.             return (time);
  285.         }
  286.        
  287.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  288.         static internal extern int nativeGetTimeZoneMinuteOffset();
  289.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  290.         static internal extern string nativeGetDaylightName();
  291.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  292.         static internal extern string nativeGetStandardName();
  293.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  294.         static internal extern short[] nativeGetDaylightChanges();
  295.     }
  296.     // class CurrentSystemTimeZone
  297. }

Developer Fusion