public OffsetInfo(ZoneRule zone)
 {
     _gmtOffset = zone.GmtOffset;
     _hasRule = false;
     _cutoverDate = DateTime.MinValue;
     _dstOffset = TimeSpan.Zero;
 }
 public OffsetInfo(ZoneRule zone, Rule rule, DateTime cutoverDate)
 {
     _cutoverDate = cutoverDate;
     _gmtOffset = zone.GmtOffset;
     _dstOffset = rule.Save;
     _hasRule = true;
 }
        private OffsetInfo GetDstOffset(DateTime dt, ZoneRule zone)
        {
            // Keep a handle on the best match
            Rule bestRule = null;
            DateTime cutoverDt = DateTime.MinValue;

            // Check to see if there are any rules for this zone
            if (!Database.Rules.ContainsKey(zone.RuleName))
                //no rule
                return new OffsetInfo(zone);

            // There are matching rules.
            // Now get the exact rule

            // Iterate through all the rules
            foreach (Rule rule in Database.Rules[zone.RuleName])
            {
                if (dt.Year >= rule.From && dt.Year <= rule.To + 1)
                {
                    // This rule may be in the range
                    DateTime newDt = DateTime.MinValue;

                    // Try the year before
                    if (rule.TryGetCutoverDateTime(dt.Year - 1, out newDt) && newDt > cutoverDt && newDt <= dt) //rule can take effect before or on the date
                    {
                        // We have a new winner
                        cutoverDt = newDt;
                        bestRule = rule;
                    }

                    // Try the current year
                    if (rule.TryGetCutoverDateTime(dt.Year, out newDt) && newDt > cutoverDt && newDt <= dt) //rule can take effect before or on the date
                    {
                        // We have a new winner
                        cutoverDt = newDt;
                        bestRule = rule;
                    }
                }
            }

            // Return the right one
            if (bestRule == null)
                return new OffsetInfo(zone);

            return new OffsetInfo(zone, bestRule, cutoverDt);
        }
        /// <summary>
        /// Creates a zone based on a text string
        /// </summary>
        /// <param name="line">A given rule line from the zone file</param>
        /// <param name="defaultZone">The default zone to use if not specified in the line</param>
        /// <remarks>
        /// The format of each line is:
        /// Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
        /// 
        /// Zones can appear in sequential lines in the file
        /// and in this case the zone information from the previous line
        /// is carried on.
        /// 
        /// Note: The format of the file seems pretty inconsistent
        /// so this function is not as elegant as it could be.
        /// </remarks>
        public static Zone Parse(string line, Zone defaultZone)
        {
            // Create the zone we will be returning
              Zone      zone = null;
              DateTime  Since;

              // Split up the line
              string[] arr       = Regex.Replace(line, "\\s+", "\t").Split('\t');
              int      arrOffset = 0;

              try
              {
            if ((arr.Length < 4) || ((defaultZone == null) && (arr[0] != "Zone")))
              throw new Exception("Неверный формат строки(1)");

            if (arr[0] == "Zone")
            {
              // The first token of the string is not empty.
              // Therefore it must be a new zone
              // Create a new zone
              zone = new Zone();
              zone.Name = arr[1];

              Since = DateTime.MinValue;
              arrOffset = 2;
            }
            else
            {
              if (arr[0].Length != 0)
                throw new Exception("Неверный формат строки(2)");

              // The first token of the string is empty
              // Therefore this is another rule of the last zone
              zone  = defaultZone;
              Since = defaultZone.ZoneRules[defaultZone.ZoneRules.Count - 1].Until;
              arrOffset = 1;
            }

            // Create a new ZoneRule for storing these details
            ZoneRule zoneRule = new ZoneRule();
            zone.ZoneRules.Add(zoneRule);

            zoneRule.ZoneName   = zone.Name;
            zoneRule.Since      = Since;
            zoneRule.GmtOffset  = Database.ConvertToTimeSpan(arr[arrOffset]);
            zoneRule.RuleName   = arr[arrOffset + 1];
            zoneRule.Format     = arr[arrOffset + 2];

            arrOffset += 3;

            // Handle until date
            if (arrOffset + 1 > arr.Length)
            {
                // There are no more values in the array.
                // Let's set the Until date as forever
                zoneRule.Until = DateTime.MaxValue;
            }
            else
            {
                // There is a Until value set
                int year  = Convert.ToInt32(arr[arrOffset++]);
                int month = 1;
                int day   = 1;
                int hour  = 0;
                int min   = 0;
                int sec   = 0;

                // Parse the month
                if (arrOffset + 1 < arr.Length)
                {
                  month = Database.ConvertToMonth(arr[arrOffset++]);
                  if (arrOffset + 1 < arr.Length)
                  {
                    if (!Int32.TryParse(arr[arrOffset++], out day))
                    {
                      day = Database.ConvertToDateTime(arr[arrOffset - 1], year, month).Day;
                    }

                    if (arrOffset + 1 < arr.Length)
                    {
                      TimeSpan ts = Database.ConvertToTimeSpan(arr[arrOffset]);
                      hour = ts.Hours;
                      min  = ts.Minutes;
                      sec  = ts.Seconds;
                    }
                  }
                }

                zoneRule.Until = new DateTime(year, month, day, hour, min, sec, DateTimeKind.Local);
              }
            }
            catch (Exception E)
            {
              throw new Exception(String.Format("Ошибка разбора зоны '{0}': {1}", line, E.Message));
            }

            return zone;
        }