コード例 #1
0
ファイル: XmlTzConvert.cs プロジェクト: LZorglub/TimeZone
        /// <summary>
        /// Converts the String to a DateTime equivalent using the specified time zone
        /// </summary>
        /// <param name="s"></param>
        /// <param name="timeZone"></param>
        /// <param name="dateTimeKind"></param>
        /// <returns></returns>
        public static DateTime ToDateTime(string s, TzTimeZone timeZone, DateTimeKind dateTimeKind)
        {
            if (string.IsNullOrEmpty(s)) throw new ArgumentException(nameof(s));
            if (timeZone == null) throw new ArgumentNullException(nameof(timeZone));
            if (dateTimeKind == DateTimeKind.Unspecified) throw new ArgumentException(nameof(dateTimeKind));

            System.Text.RegularExpressions.Match m = dateRegex.Match(s);
            if (m.Success)
            {
                DateTime result = new DateTime(Convert.ToInt32(m.Groups["YEAR"].Value), Convert.ToInt32(m.Groups["MONTH"].Value),
                    Convert.ToInt32(m.Groups["DAY"].Value), Convert.ToInt32(m.Groups["HOUR"].Value), Convert.ToInt32(m.Groups["MINUTE"].Value),
                    Convert.ToInt32(m.Groups["SEC"].Value), DateTimeKind.Local);
                if (m.Groups["MS"].Success)
                {
                    result = result.AddMilliseconds(Convert.ToInt32(m.Groups["MS"]));
                }

                // Date is local, check return
                if (m.Groups["UTC"].Success)
                {
                    if (dateTimeKind == DateTimeKind.Utc)
                        return DateTime.SpecifyKind(result, DateTimeKind.Utc);
                    else
                        return timeZone.ToLocalTime(DateTime.SpecifyKind(result, DateTimeKind.Utc));
                }
                else {
                    // Date string is local, check offset
                    if (m.Groups["SGN"].Success)
                    {
                        // Translate in utc
                        if (m.Groups["SGN"].Value == "+")
                            result = result.AddHours(-Convert.ToInt32(m.Groups["SH"].Value)).AddMinutes(-Convert.ToInt32(m.Groups["SM"].Value));
                        else
                            result = result.AddHours(Convert.ToInt32(m.Groups["SH"].Value)).AddMinutes(Convert.ToInt32(m.Groups["SM"].Value));

                        if (dateTimeKind == DateTimeKind.Utc)
                            return DateTime.SpecifyKind(result, DateTimeKind.Utc);
                        else
                            return timeZone.ToLocalTime(DateTime.SpecifyKind(result, DateTimeKind.Utc));
                    }
                    else
                    {
                        // Zone unspecified, suppose to be local
                        if (dateTimeKind == DateTimeKind.Utc)
                            return timeZone.ToUniversalTime(result);
                        else
                            return result;
                    }
                }
            }

            throw new ArgumentException(string.Format("{0} is not a valid format", s));
        }
コード例 #2
0
ファイル: TzUtilities.cs プロジェクト: LZorglub/TimeZone
        /// <summary>
        /// Renseigne une zone en fonction de sa description
        /// </summary>
        /// <param name="zoneDescription">Ensemble de paramètres décrivant la zone</param>
        /// <param name="zone"><see cref="TzTimeZone"/> à renseigner en fonction de la description spécifiée.</param>
        /// <param name="continuation">Indique que la description est un ajout ou non à la zone spécifiée.</param>
        /// <returns><see cref="bool"/> spécifiant qu'un complément de description est attendu ou non.</returns>
        public static bool GetZoneSub(List <string> zoneDescription, ref TzTimeZone zone, bool continuation)
        {
            int i_gmtoff, i_rule, i_format;
            int i_untilyear, i_untilmonth;
            int i_untilday, i_untiltime;

            TzTimeZone zcont = zone;

            if (continuation)
            {
                i_gmtoff     = 0;
                i_rule       = 1;
                i_format     = 2;
                i_untilyear  = 3;
                i_untilmonth = 4;
                i_untilday   = 5;
                i_untiltime  = 6;
            }
            else
            {
                i_gmtoff     = 2;
                i_rule       = 3;
                i_format     = 4;
                i_untilyear  = 5;
                i_untilmonth = 6;
                i_untilday   = 7;
                i_untiltime  = 8;
                zcont.Name   = zoneDescription[1];
            }

            TzTimeZoneRule zoneRule = new TzTimeZoneRule();

            zoneRule.GmtOffset = TzUtilities.GetHMS(zoneDescription[i_gmtoff]);
            zoneRule.Format    = zoneDescription[i_format];
            zoneRule.RuleName  = zoneDescription[i_rule];

            bool hashuntil = zoneDescription.Count > i_untilyear;

            if (hashuntil)
            {
                zoneRule.Until      = new TzTimeZoneRuleUntil();
                zoneRule.Until.Year = Convert.ToInt32(zoneDescription[i_untilyear], CultureInfo.InvariantCulture);

                SetRuleSub(zoneRule.Until,
                           (zoneDescription.Count > i_untilmonth) ? zoneDescription[i_untilmonth] : "Jan",
                           (zoneDescription.Count > i_untilday) ? zoneDescription[i_untilday] : "1",
                           (zoneDescription.Count > i_untiltime) ? zoneDescription[i_untiltime] : "0");
            }
            zcont.ZoneRules.Add(zoneRule);

            return(hashuntil);
        }
コード例 #3
0
ファイル: TzUtilities.cs プロジェクト: LZorglub/TimeZone
        /// <summary>
        /// Obtient une zone en fonction de sa description
        /// </summary>
        /// <param name="zoneDescription">Ensemble de paramètres décrivant la zone</param>
        /// <param name="zone"><see cref="TzTimeZone"/> à renseigner en fonction de la description spécifiée.</param>
        /// <param name="append">Indique que la description est un ajout ou non à la zone spécifiée.</param>
        /// <returns><see cref="bool"/> spécifiant qu'un complément de description est attendu ou non.</returns>
        /// <remarks>
        /// The format of each line is:
        /// Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
        /// </remarks>
        public static bool GetZone(List <string> zoneDescription, ref TzTimeZone zone, bool append)
        {
            if (!append && (zoneDescription.Count < 5 || zoneDescription.Count > 9))
            {
                throw new ArgumentException("Wrong number of fields on zone line");
            }

            if (append && (zoneDescription.Count < 3 || zoneDescription.Count > 7))
            {
                throw new ArgumentException("Wrong number of fields on zone continuation line");
            }

            return(GetZoneSub(zoneDescription, ref zone, append));
        }
コード例 #4
0
        /// <summary>
        /// Returns the local time in the specified time zone that correspond to the specified date in the current time zone.
        /// </summary>
        /// <param name="datetime">A date and time in the current time zone</param>
        /// <param name="zone">The time zone in which the local time is converted.</param>
        /// <returns>A local <see cref="DateTime"/> in the specified time zone.</returns>
        public DateTime ToTimeZone(DateTime datetime, TzTimeZone zone)
        {
            if (zone == null)
            {
                throw new ArgumentNullException(nameof(zone));
            }
            if (datetime.Kind == DateTimeKind.Unspecified)
            {
                throw new ArgumentException("Unspecified date time kind", nameof(datetime));
            }

            if (datetime.Kind == DateTimeKind.Utc)
            {
                return(datetime);
            }

            DateTime utc = this.ToUniversalTime(datetime);

            return(zone.ToLocalTime(utc));
        }
コード例 #5
0
        /// <summary>
        /// Construit l'ensemble des règles chargées
        /// </summary>
        /// <remarks>
        /// La date de début d'une zone ne correspond pas forcement à la date de fin de zone précédente. Exemple avec Pacific/Samoa ou
        /// on perd 1 journée le 30 décembre 2011
        /// Zone Pacific/Apia	 12:33:04 -	LMT	1892 Jul  5
        ///	-11:26:56 -	LMT	1911
        ///	-11:30	-	-1130	1950
        ///	-11:00	WS	-11/-10	2011 Dec 29 24:00
        ///	 13:00	WS	+13/+14
        ///
        /// Certaines dates de fin peuvent coincider avec des règles de changement
        /// Exemple : America/Grand_Turk
        /// -5:00	US	E%sT	2015 Nov Sun>=1 2:00 coincide avec la régle Rule	US	2007	max	-	Nov	Sun>=1	2:00	0	S
        /// -4:00	-	AST	2018 Mar 11 3:00
        /// 1/11/2015 01:00 EDT (sunday locale) => 05:00u
        /// 1/11/2015 02:00 AST (sunday locale) => 06:00u
        ///
        /// -4:00	-	AST	2018 Mar 11 3:00 coincide avec la régle Rule	US	2007	max	-	Mar	Sun>=8	2:00 (07:00 +5)	1:00	D
        /// -5:00	US	E%sT
        /// 11/03/2018 02:00 AST => 06:00u
        /// 11/03/2018 03:00 EDT => 07:00u
        /// 11/03/2018 04:00 EDT => 08:00u
        ///
        /// La date de début UTC de la zone suivante est la date de fin UTC de la zone précédente, ce n'est pas vrai pour ce qui concerne les
        /// dates locales. Exemple de samoa qui avance de 24 heures.
        ///
        /// Date de début : On construit la date à partir de la date utc précédente et on applique la régle courante pour obtenir la date locale
        /// Date de fin : On construit la date à partir de la date until en appliquant le changement DST précédent strictement inférieure
        /// </remarks>
        private static void BuildZone()
        {
            var ienum = _zones.GetEnumerator();

            while (ienum.MoveNext())
            {
                TzTimeZone temp = ienum.Current.Value;

                TimeSpan stdoff = TimeSpan.Zero;

                // Coordonnées de la zone
                var tz = _countryCode.SelectMany(e => e.TZone).FirstOrDefault(e => e.TZName == temp.Name);
                temp.Coordinates = tz.coordinates;
                temp.Comment     = tz.comment;

                for (int index = 0; index < temp.ZoneRules.Count; index++)
                {
                    TzTimeZoneRule zr = temp.ZoneRules[index];

                    // Zone avec un décalage fixe
                    if (zr.RuleName != "-" && !_rules.ContainsKey(zr.RuleName))
                    {
                        zr.FixedStandardOffset = TzUtilities.GetHMS(zr.RuleName);

                        // Dans ce cas précis le formatage % n'est pas autorisé
                        if (zr.Format.Contains("%"))
                        {
                            throw new ArgumentException("%s in ruleless zone " + temp.Name + "(" + temp.Filename + "," + temp.LineNumber + ")");
                        }
                    }

                    #region Start date
                    if (index == 0)
                    {
                        zr.StartZone = TzTimeZoneRuleDate.MinValue;
                    }
                    else
                    {
                        DateTime previousUTCDate        = temp.ZoneRules[index - 1].EndZone.UtcDate;
                        TimeSpan previousStandardOffset = temp.ZoneRules[index - 1].EndZone.StandardOffset;

                        if (zr.RuleName == "-")
                        {
                            zr.StartZone = new TzTimeZoneRuleDate(previousUTCDate, zr.GmtOffset, TimeSpan.Zero);
                        }
                        else if (!_rules.ContainsKey(zr.RuleName))
                        {
                            zr.StartZone = new TzTimeZoneRuleDate(previousUTCDate, zr.GmtOffset, zr.FixedStandardOffset);
                        }
                        else
                        {
                            // Il faut rechercher si pour la date UTC on aurait une régle qui s'applique
                            Rule lastRule = TzTimeZone.GetRuleAtPoint(_rules[zr.RuleName], previousUTCDate, zr.GmtOffset, previousStandardOffset);
                            zr.StartZone = new TzTimeZoneRuleDate(previousUTCDate, zr.GmtOffset, lastRule?.StandardOffset ?? previousStandardOffset);
                        }
                    }
                    #endregion

                    #region End date
                    if (zr.Until == null)
                    {
                        zr.EndZone = TzTimeZoneRuleDate.MaxValue;
                    }
                    else if (zr.RuleName == "-")
                    {
                        stdoff     = TimeSpan.Zero;
                        zr.EndZone = new TzTimeZoneRuleDate(TzUtilities.GetDateTime(zr.Until, zr.Until.Year, zr.GmtOffset, stdoff, DateTimeKind.Utc), zr.GmtOffset, stdoff);
                    }
                    else if (!_rules.ContainsKey(zr.RuleName))
                    {
                        // Zone avec décalage fixe
                        stdoff     = zr.FixedStandardOffset;
                        zr.EndZone = new TzTimeZoneRuleDate(TzUtilities.GetDateTime(zr.Until, zr.Until.Year, zr.GmtOffset, stdoff, DateTimeKind.Utc), zr.GmtOffset, stdoff);
                    }
                    else
                    {
                        Rule lastRule = TzTimeZone.GetLastStandardOffset(_rules[zr.RuleName], zr.Until, zr.StartZone, zr.GmtOffset, RuleSearchKind.LessThan);
                        if (lastRule != null)
                        {
                            stdoff = lastRule.StandardOffset;
                        }
                        zr.EndZone = new TzTimeZoneRuleDate(TzUtilities.GetDateTime(zr.Until, zr.Until.Year, zr.GmtOffset, stdoff, DateTimeKind.Utc), zr.GmtOffset, stdoff);
                    }
                    #endregion
                }
            }
        }
コード例 #6
0
        /// <summary>
        /// Charge un ensemble de zone/rule/link
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="filename"></param>
        private static void LoadFile(TextReader reader, string filename)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }
            if (filename == null)
            {
                throw new ArgumentNullException(nameof(filename));
            }

            string line = null;

            bool       zoneContinuation = false;
            TzTimeZone lastZone         = null;
            int        index            = 0;

            while ((line = reader.ReadLine()) != null)
            {
                index++;

                if (string.IsNullOrEmpty(line))
                {
                    continue;
                }

                List <string> cutline = TzUtilities.GetFields(line, -1);

                if (cutline == null || cutline.Count == 0)
                {
                    continue;
                }
                else
                {
                    if (zoneContinuation)
                    {
                        zoneContinuation = TzUtilities.GetZone(cutline, ref lastZone, true);
                    }
                    else
                    {
                        switch (cutline[0])
                        {
                        case RULE:
                            Debug.Assert(cutline.Count == 10);
                            Rule r = TzUtilities.GetRule(cutline);
                            //if (!filename.StartsWith("solar") && r.StandardOffset != TimeSpan.Zero && r.StandardOffset != TimeSpan.FromHours(1))
                            //    Console.WriteLine(r.ToString());

                            r.Filename = filename; r.LineNumber = index;
                            if (!_rules.ContainsKey(r.Name))
                            {
                                _rules.Add(r.Name, new List <Rule>());
                            }
                            _rules[r.Name].Add(r);

                            break;

                        case LINK:
                            Debug.Assert(cutline.Count == 3);
                            _links.Add(cutline[2], cutline[1]);
                            break;

                        case ZONE:
                            TzTimeZone z = new TzTimeZone();
                            z.Filename       = filename; z.LineNumber = index;
                            zoneContinuation = TzUtilities.GetZone(cutline, ref z, false);
                            _zones.Add(z.Name, z);
                            lastZone = z;
                            break;
                        }
                    }
                }
            }
        }
コード例 #7
0
ファイル: TzTimeZone.cs プロジェクト: LZorglub/TimeZone
        /// <summary>
        /// Returns the local time in the specified time zone that correspond to the specified date in the current time zone.
        /// </summary>
        /// <param name="datetime">A date and time in the current time zone</param>
        /// <param name="zone">The time zone in which the local time is converted.</param>
        /// <returns>A local <see cref="DateTime"/> in the specified time zone.</returns>
        public DateTime ToTimeZone(DateTime datetime, TzTimeZone zone)
        {
            if (datetime.Kind == DateTimeKind.Unspecified) throw new ArgumentException("Unspecified date time kind", "datetime");

            if (datetime.Kind == DateTimeKind.Utc) return datetime;

            DateTime utc = this.ToUniversalTime(datetime);
            return zone.ToLocalTime(utc);
        }
コード例 #8
0
ファイル: TzUtilities.cs プロジェクト: LZorglub/TimeZone
        /// <summary>
        /// Renseigne une zone en fonction de sa description
        /// </summary>
        /// <param name="zoneDescription">Ensemble de paramètres décrivant la zone</param>
        /// <param name="zone"><see cref="TzTimeZone"/> à renseigner en fonction de la description spécifiée.</param>
        /// <param name="continuation">Indique que la description est un ajout ou non à la zone spécifiée.</param>
        /// <returns><see cref="bool"/> spécifiant qu'un complément de description est attendu ou non.</returns>
        public static bool GetZoneSub(List<string> zoneDescription, ref TzTimeZone zone, bool continuation)
        {
            int i_gmtoff, i_rule, i_format;
            int i_untilyear, i_untilmonth;
            int i_untilday, i_untiltime;

            TzTimeZone zcont = zone;

            if (continuation) {
                i_gmtoff = 0;
                i_rule = 1;
                i_format = 2;
                i_untilyear = 3;
                i_untilmonth = 4;
                i_untilday = 5;
                i_untiltime = 6;
            }
            else {
                i_gmtoff = 2;
                i_rule = 3;
                i_format = 4;
                i_untilyear = 5;
                i_untilmonth = 6;
                i_untilday = 7;
                i_untiltime = 8;
                zcont.Name = zoneDescription[1];
            }

            TzTimeZoneRule zoneRule = new TzTimeZoneRule();
            zoneRule.GmtOffset = TzUtilities.GetHMS(zoneDescription[i_gmtoff]);
            zoneRule.Format = zoneDescription[i_format];
            zoneRule.RuleName = zoneDescription[i_rule];

            bool hashuntil = zoneDescription.Count > i_untilyear;
            if (hashuntil) {
                zoneRule.Until = new TzTimeZoneRuleUntil();
                zoneRule.Until.Year = Convert.ToInt32(zoneDescription[i_untilyear]);

                SetRuleSub(zoneRule.Until,
                    (zoneDescription.Count > i_untilmonth) ? zoneDescription[i_untilmonth] : "Jan",
                    (zoneDescription.Count > i_untilday) ? zoneDescription[i_untilday] : "1",
                    (zoneDescription.Count > i_untiltime) ? zoneDescription[i_untiltime] : "0");
            }
            zcont.ZoneRules.Add(zoneRule);

            return hashuntil;
        }
コード例 #9
0
ファイル: TzUtilities.cs プロジェクト: LZorglub/TimeZone
        /// <summary>
        /// Obtient une zone en fonction de sa description
        /// </summary>
        /// <param name="zoneDescription">Ensemble de paramètres décrivant la zone</param>
        /// <param name="zone"><see cref="TzTimeZone"/> à renseigner en fonction de la description spécifiée.</param>
        /// <param name="append">Indique que la description est un ajout ou non à la zone spécifiée.</param>
        /// <returns><see cref="bool"/> spécifiant qu'un complément de description est attendu ou non.</returns>
        /// <remarks>
        /// The format of each line is:
        /// Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
        /// </remarks>
        public static bool GetZone(List<string> zoneDescription, ref TzTimeZone zone, bool append)
        {
            if (!append && (zoneDescription.Count < 5 || zoneDescription.Count > 9)) {
                throw new ArgumentException("Wrong number of fields on zone line");
            }

            if (append && (zoneDescription.Count < 3 || zoneDescription.Count > 7)) {
                throw new ArgumentException("Wrong number of fields on zone continuation line");
            }

            return GetZoneSub(zoneDescription, ref zone, append);
        }
コード例 #10
0
        /// <summary>
        /// Converts the String to a DateTime equivalent using the specified time zone
        /// </summary>
        /// <param name="s"></param>
        /// <param name="timeZone"></param>
        /// <param name="dateTimeKind"></param>
        /// <returns></returns>
        public static DateTime ToDateTime(string s, TzTimeZone timeZone, DateTimeKind dateTimeKind)
        {
            if (string.IsNullOrEmpty(s))
            {
                throw new ArgumentNullException(nameof(s));
            }
            if (timeZone == null)
            {
                throw new ArgumentNullException(nameof(timeZone));
            }
            if (dateTimeKind == DateTimeKind.Unspecified)
            {
                throw new ArgumentException("Datetime kind unspecified", nameof(dateTimeKind));
            }

            System.Text.RegularExpressions.Match m = dateRegex.Match(s);
            if (m.Success)
            {
                DateTime result = new DateTime(Convert.ToInt32(m.Groups["YEAR"].Value, CultureInfo.InvariantCulture),
                                               Convert.ToInt32(m.Groups["MONTH"].Value, CultureInfo.InvariantCulture),
                                               Convert.ToInt32(m.Groups["DAY"].Value, CultureInfo.InvariantCulture),
                                               Convert.ToInt32(m.Groups["HOUR"].Value, CultureInfo.InvariantCulture),
                                               Convert.ToInt32(m.Groups["MINUTE"].Value, CultureInfo.InvariantCulture),
                                               Convert.ToInt32(m.Groups["SEC"].Value, CultureInfo.InvariantCulture), DateTimeKind.Local);
                if (m.Groups["MS"].Success)
                {
                    result = result.AddMilliseconds(Convert.ToInt32(m.Groups["MS"], CultureInfo.InvariantCulture));
                }

                // Date is local, check return
                if (m.Groups["UTC"].Success)
                {
                    if (dateTimeKind == DateTimeKind.Utc)
                    {
                        return(DateTime.SpecifyKind(result, DateTimeKind.Utc));
                    }
                    else
                    {
                        return(timeZone.ToLocalTime(DateTime.SpecifyKind(result, DateTimeKind.Utc)));
                    }
                }
                else
                {
                    // Date string is local, check offset
                    if (m.Groups["SGN"].Success)
                    {
                        // Translate in utc
                        if (m.Groups["SGN"].Value == "+")
                        {
                            result = result.AddHours(-Convert.ToInt32(m.Groups["SH"].Value, CultureInfo.InvariantCulture))
                                     .AddMinutes(-Convert.ToInt32(m.Groups["SM"].Value, CultureInfo.InvariantCulture));
                        }
                        else
                        {
                            result = result.AddHours(Convert.ToInt32(m.Groups["SH"].Value, CultureInfo.InvariantCulture))
                                     .AddMinutes(Convert.ToInt32(m.Groups["SM"].Value, CultureInfo.InvariantCulture));
                        }

                        if (dateTimeKind == DateTimeKind.Utc)
                        {
                            return(DateTime.SpecifyKind(result, DateTimeKind.Utc));
                        }
                        else
                        {
                            return(timeZone.ToLocalTime(DateTime.SpecifyKind(result, DateTimeKind.Utc)));
                        }
                    }
                    else
                    {
                        // Zone unspecified, suppose to be local
                        if (dateTimeKind == DateTimeKind.Utc)
                        {
                            return(timeZone.ToUniversalTime(result));
                        }
                        else
                        {
                            return(result);
                        }
                    }
                }
            }

            throw new ArgumentException($"{s} is an invalid format");
        }
コード例 #11
0
ファイル: TzTimeInfo.cs プロジェクト: LZorglub/TimeZone
        /// <summary>
        /// Charge un ensemble de zone/rule/link
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="filename"></param>
        private static void LoadFile(TextReader reader, string filename)
        {
            if (reader == null)
                throw new ArgumentNullException("reader");

            string line = null;

            bool zoneContinuation = false;
            TzTimeZone lastZone = null;
            int index = 0;

            while ((line = reader.ReadLine()) != null) {
                index++;

                if (string.IsNullOrEmpty(line)) continue;

                List<string> cutline = TzUtilities.GetFields(line, -1);

                if (cutline == null || cutline.Count == 0)
                    continue;
                else {
                    if (zoneContinuation) {
                        zoneContinuation = TzUtilities.GetZone(cutline, ref lastZone, true);
                    }
                    else {
                        switch (cutline[0]) {
                            case RULE:
                                Debug.Assert(cutline.Count == 10);
                                Rule r = TzUtilities.GetRule(cutline);
                                //if (!filename.StartsWith("solar") && r.StandardOffset != TimeSpan.Zero && r.StandardOffset != TimeSpan.FromHours(1))
                                //    Console.WriteLine(r.ToString());

                                r.Filename = filename; r.LineNumber = index;
                                if (!_rules.ContainsKey(r.Name))
                                    _rules.Add(r.Name, new List<Rule>());
                                _rules[r.Name].Add(r);

                                break;
                            case LINK:
                                Debug.Assert(cutline.Count == 3);
                                _links.Add(cutline[2], cutline[1]);
                                break;
                            case ZONE:
                                TzTimeZone z = new TzTimeZone();
                                z.Filename = filename; z.LineNumber = index;
                                zoneContinuation = TzUtilities.GetZone(cutline, ref z, false);
                                _zones.Add(z.Name, z);
                                lastZone = z;
                                break;
                        }
                    }
                }
            }
        }