예제 #1
0
        /// <summary>
        /// Obtient la description de zone correspondant à la date spécifiée
        /// </summary>
        /// <param name="point"><see cref="DateTime"/> spécifié.</param>
        /// <returns><see cref="ZoneRuleAssociate"/> contenant le <see cref="DateTime"/> spécifié</returns>
        private ZoneRuleAssociate GetZoneRule(DateTime point)
        {
            if (point.Kind == DateTimeKind.Unspecified)
            {
                throw new ArgumentException("Unspecified date time kind", nameof(point));
            }

            ZoneRuleAssociate za = new ZoneRuleAssociate();

            za.standardOffset = TimeSpan.Zero;

            TimeSpan stdoff = TimeSpan.Zero;

            bool utc = (point.Kind == DateTimeKind.Utc);

            // Traduction de la date en règle
            TzTimeZoneRuleUntil rulePoint = new TzTimeZoneRuleUntil();

            rulePoint.Year   = point.Year;
            rulePoint.At     = point.TimeOfDay;
            rulePoint.AtKind = (utc) ? TimeKind.UniversalTime : TimeKind.LocalWallTime;
            rulePoint.Month  = point.Month;
            rulePoint.Day    = new DayOfRule(DayWorkKind.Dom, point.DayOfWeek, point.Day);

            for (int index = ZoneRules.Count - 1; index >= 0; index--)
            {
                TzTimeZoneRule temp = ZoneRules[index];

                DateTime startTime = (utc) ? temp.StartZone.UtcDate : temp.StartZone.ToLocalTime();
                DateTime endTime   = (utc) ? temp.EndZone.UtcDate : temp.EndZone.ToLocalTime();
                stdoff = temp.StartZone.StandardOffset;

                if (point >= startTime && point < endTime)
                {
                    if (!TzTimeInfo.Rules.ContainsKey(temp.RuleName))
                    {
                        // Max time
                        za.zoneRule       = temp;
                        za.standardOffset = temp.FixedStandardOffset;
                        break;
                    }
                    else
                    {
                        // Trouver la dernière règle applicable au point
                        Rule lastRule = GetLastStandardOffset(TzTimeInfo.Rules[temp.RuleName], rulePoint, temp.StartZone, temp.GmtOffset, RuleSearchKind.LessThanOrEqual);

                        za.zoneRule       = temp;
                        za.standardOffset = (lastRule == null) ? stdoff : lastRule.StandardOffset;
                        za.Letter         = (lastRule == null) ? string.Empty : lastRule.Letter;
                        break;
                    }
                }
            }
            return(za);
        }
예제 #2
0
        /// <summary>
        /// Obtient la description de zone correspondant à la date spécifiée
        /// </summary>
        /// <param name="point"><see cref="DateTime"/> spécifié.</param>
        /// <returns><see cref="ZoneRuleAssociate"/> contenant le <see cref="DateTime"/> spécifié</returns>
        private ZoneRuleAssociate GetZoneRule(DateTime point)
        {
            if (point.Kind == DateTimeKind.Unspecified) throw new ArgumentException("Unspecified date time kind", "point");

            ZoneRuleAssociate za = new ZoneRuleAssociate();
            za.standardOffset = TimeSpan.Zero;

            TimeSpan gmtoff = TimeSpan.Zero;
            TimeSpan stdoff = TimeSpan.Zero;

            bool utc = (point.Kind == DateTimeKind.Utc);

            // Traduction de la date en règle
            TzTimeZoneRuleUntil rulePoint = new TzTimeZoneRuleUntil();
            rulePoint.Year = point.Year;
            rulePoint.At = point.TimeOfDay;
            rulePoint.AtKind = (utc) ? TimeKind.UniversalTime : TimeKind.LocalWallTime;
            rulePoint.Month = point.Month;
            rulePoint.Day = new DayOfRule(DayWorkKind.Dom, point.DayOfWeek, point.Day);

            for (int index = ZoneRules.Count-1; index >= 0; index--) {
                TzTimeZoneRule temp = ZoneRules[index];
                gmtoff = temp.GmtOffset;

                DateTime startTime = (utc) ? temp.StartZone.UtcDate : temp.StartZone.ToLocalTime();
                DateTime endTime = (utc) ? temp.EndZone.UtcDate : temp.EndZone.ToLocalTime();
                stdoff = temp.StartZone.StandardOffset;

                if (point >= startTime && point < endTime) {
                    if (!TzTimeInfo.Rules.ContainsKey(temp.RuleName)) {
                        // Max time
                        za.zoneRule = temp;
                        za.standardOffset = temp.FixedStandardOffset;
                        break;
                    }
                    else {
                        // Trouver la dernière règle applicable au point
                        Rule lastRule = GetLastStandardOffset(TzTimeInfo.Rules[temp.RuleName], rulePoint, temp.StartZone, temp.GmtOffset);

                        za.zoneRule = temp;
                        za.standardOffset = (lastRule == null) ? stdoff : lastRule.StandardOffset;
                        za.Letter = (lastRule == null) ? string.Empty : lastRule.Letter;
                        break;
                    }
                }
            }
            return za;
        }
예제 #3
0
        /// <summary>
        /// Obtient la règle applicable à un point
        /// </summary>
        /// <param name="rules">Ensemble des règles à parcourir</param>
        /// <param name="date"><see cref="TzTimeZoneRuleUntil"/> représentant la date du ponit</param>
        /// <param name="startZone">Limite inférieure de validité des règles</param>
        /// <param name="gmtOffset"></param>
        /// <returns></returns>
        internal static Rule GetLastStandardOffset(List<Rule> rules, TzTimeZoneRuleUntil date, TzTimeZoneRuleDate startZone, TimeSpan gmtOffset)
        {
            Rule lastRule = null;
            TimeSpan range = TimeSpan.MaxValue;

            DateTime ruleTime;

            // En fonction du kind de la date Until on compare des dates UTC ou des dates locales
            DateTimeKind kind = (date.AtKind == TimeKind.LocalWallTime) ? DateTimeKind.Local : DateTimeKind.Utc;
            DateTime startTimeZone = (kind == DateTimeKind.Local) ? startZone.ToLocalTime() : startZone.UtcDate;

            // On pré-calcule la date de la règle dans le type permettant l'absence du daylight saving time
            // Une date en LocalWallTime sera exprimée en local
            // Une date en LocalStandardTime sera exprimée en UTC
            // Une date en UTC sera exprimée en UTC
            DateTime dateTime = TzUtilities.GetDateTime(date, date.Year, gmtOffset, TimeSpan.Zero, kind);

            int bestYear = -1;
            int yref = 0;

            // On parcourt toutes les règles car rien ne nous assure quelles sont dans un ordre spécifique
            for(int i=rules.Count - 1; i >= 0; i--) {
                Rule rule = rules[i];

                // Les années ne pouvant pas rivaliser avec la meilleure année obtenue sont ignorées
                if (rule.HighYear < bestYear) continue;

                // Certaines règles sont déclenchées le 1er janvier de l'année, avec le décalage
                // GMT on peut se retrouver dans le cas LowerYear = date.Year+1.
                // Exemple : je cherche la règle applicable le 31 décembre 2010 23:00 local, si on est sur une règle
                // applicable le 1 janvier 2011 utc alors le décalage gmt peut faire en sorte que la règle soit retenue
                if (rule.LowerYear == date.Year + 1 && rule.Month == 1 && date.Month == 12 && rule.AtKind != date.AtKind) {
                    yref = rule.LowerYear;
                }
                else {
                    // Année de début supérieure au point recherché, la règle n'est pas applicable
                    if (rule.LowerYear > date.Year) continue;

                    // On prend l'année qui convient le mieux au point
                    yref = (rule.HighYear > date.Year) ? date.Year : rule.HighYear;

                    // Cas particulier des changements à cheval sur deux années
                    // Si les dates à comparer ne sont pas de même kind la règle applicable en année N
                    // peut se retrouver applicable en N-1
                    if (yref == date.Year && yref < rule.HighYear && date.AtKind != rule.AtKind) {
                        if (rule.Month == 1 && date.Month == 12) yref++;
                    }

                    // Au pire il faudra calculer la date pour l'année désirée et l'année précédente
                    // Par exemple si on recherche la dernière règle applicable au mois de janvier il
                    // faudra remonter au mois x de l'année précédente
                    // Exmple : on a une règle définie pour le mois de mars de 2000 à 2050, on recherche
                    // pour un point en janvier 2010. La date précédemment retenue est 2010, mais comme
                    // janvier < mars si on reste en 2010 elle sera exclue. Il faut démarrer en 2009 pour
                    // cette règle

                    // On économise l'examen de l'année yref si la règle est applicable après le point recherché
                    // Seul une comparaison de même kind peut être effectuée.
                    if (yref == date.Year && rule.AtKind == date.AtKind &&
                        (rule.Month > date.Month ||
                          (rule.Month == date.Month && rule.Day.DayWork == DayWorkKind.Dom && date.Day.DayWork == DayWorkKind.Dom && rule.Day.DayOfMonth > date.Day.DayOfMonth))
                        ) {
                        if (yref > rule.LowerYear)
                            yref--;
                        else
                            continue;
                    }
                }

                for (; yref >= rule.LowerYear; yref--) {

                    if (!TzUtilities.IsYearType(yref, rule.YearType)) continue;

                    // On est obligé de rechercher la règle précédente si pour comparer les dates
                    // on a besoin du daylight saving time
                    if ((rule.AtKind == TimeKind.LocalWallTime && kind == DateTimeKind.Utc) ||
                        (rule.AtKind != TimeKind.LocalWallTime && kind == DateTimeKind.Local)) {
                        TimeSpan stdoff = GetLastSaveOffset(yref, rules, rule, startZone, gmtOffset);

                        ruleTime = TzUtilities.GetDateTime(rule, yref, gmtOffset, stdoff, kind);
                    }
                    else {
                        // Si les deux règles sont en locales ou utc/standardoffset on peut les comparer directement
                        ruleTime = TzUtilities.GetDateTime(rule, yref, gmtOffset, TimeSpan.Zero, kind);
                    }

                    Debug.Assert(ruleTime.Kind == dateTime.Kind);

                    // Si la date d'application de la règle est supérieure au point recherché alors
                    // on recherche pour l'année précédente si on n'a pas atteint la borne inférieure
                    if (ruleTime > dateTime && ruleTime.Year > rule.LowerYear) {
                        continue;
                    }

                    // Si la règle est comprise dans les bornes [startTimeZone, dateTime] alors elle
                    // est applicable, on la retient si elle est meilleure que celle déjà retenue.
                    if (ruleTime >= startTimeZone && dateTime >= ruleTime) {
                        TimeSpan diff = dateTime - ruleTime;
                        if (range > diff) {
                            lastRule = rule;
                            range = diff;
                            bestYear = ruleTime.Year;
                        }
                    }
                    break;
                }
            }

            return lastRule;
        }
예제 #4
0
        /// <summary>
        /// Obtient la règle applicable à un point
        /// </summary>
        /// <param name="rules">Ensemble des règles à parcourir</param>
        /// <param name="date"><see cref="TzTimeZoneRuleUntil"/> représentant la date du point</param>
        /// <param name="startZone">Limite inférieure de validité des règles</param>
        /// <param name="gmtOffset"></param>
        /// <param name="ruleSearch">Indique si on recherche une régle inférieure ou égal au point</param>
        /// <returns></returns>
        internal static Rule GetLastStandardOffset(List <Rule> rules, TzTimeZoneRuleUntil date, TzTimeZoneRuleDate startZone, TimeSpan gmtOffset,
                                                   RuleSearchKind ruleSearch)
        {
            Rule     lastRule = null;
            TimeSpan range    = TimeSpan.MaxValue;

            DateTime ruleTime;

            // En fonction du kind de la date Until on compare des dates UTC ou des dates locales
            DateTimeKind kind          = (date.AtKind == TimeKind.LocalWallTime) ? DateTimeKind.Local : DateTimeKind.Utc;
            DateTime     startTimeZone = (kind == DateTimeKind.Local) ? startZone.ToLocalTime() : startZone.UtcDate;

            // On pré-calcule la date de la règle dans le type permettant l'absence du daylight saving time
            // Une date en LocalWallTime sera exprimée en local
            // Une date en LocalStandardTime sera exprimée en UTC
            // Une date en UTC sera exprimée en UTC
            DateTime dateTime = TzUtilities.GetDateTime(date, date.Year, gmtOffset, TimeSpan.Zero, kind);

            int bestYear = -1;
            int yref     = 0;

            // On parcourt toutes les règles car rien ne nous assure quelles sont dans un ordre spécifique
            for (int i = rules.Count - 1; i >= 0; i--)
            {
                Rule rule = rules[i];

                // Les années ne pouvant pas rivaliser avec la meilleure année obtenue sont ignorées
                if (rule.HighYear < bestYear)
                {
                    continue;
                }

                // Certaines règles sont déclenchées le 1er janvier de l'année, avec le décalage
                // GMT on peut se retrouver dans le cas LowerYear = date.Year+1.
                // Exemple : je cherche la règle applicable le 31 décembre 2010 23:00 local, si on est sur une règle
                // applicable le 1 janvier 2011 utc alors le décalage gmt peut faire en sorte que la règle soit retenue
                if (rule.LowerYear == date.Year + 1 && rule.Month == 1 && date.Month == 12 && rule.AtKind != date.AtKind)
                {
                    yref = rule.LowerYear;
                }
                else
                {
                    // Année de début supérieure au point recherché, la règle n'est pas applicable
                    if (rule.LowerYear > date.Year)
                    {
                        continue;
                    }

                    // On prend l'année qui convient le mieux au point
                    yref = (rule.HighYear > date.Year) ? date.Year : rule.HighYear;

                    // Cas particulier des changements à cheval sur deux années
                    // Si les dates à comparer ne sont pas de même kind la règle applicable en année N
                    // peut se retrouver applicable en N-1
                    if (yref == date.Year && yref < rule.HighYear && date.AtKind != rule.AtKind)
                    {
                        if (rule.Month == 1 && date.Month == 12)
                        {
                            yref++;
                        }
                    }

                    // Au pire il faudra calculer la date pour l'année désirée et l'année précédente
                    // Par exemple si on recherche la dernière règle applicable au mois de janvier il
                    // faudra remonter au mois x de l'année précédente
                    // Exmple : on a une règle définie pour le mois de mars de 2000 à 2050, on recherche
                    // pour un point en janvier 2010. La date précédemment retenue est 2010, mais comme
                    // janvier < mars si on reste en 2010 elle sera exclue. Il faut démarrer en 2009 pour
                    // cette règle

                    // On économise l'examen de l'année yref si la règle est applicable après le point recherché
                    // Seul une comparaison de même kind peut être effectuée.
                    if (yref == date.Year && rule.AtKind == date.AtKind &&
                        (rule.Month > date.Month ||
                         (rule.Month == date.Month && rule.Day.DayWork == DayWorkKind.Dom && date.Day.DayWork == DayWorkKind.Dom && rule.Day.DayOfMonth > date.Day.DayOfMonth))
                        )
                    {
                        if (yref > rule.LowerYear)
                        {
                            yref--;
                        }
                        else
                        {
                            continue;
                        }
                    }
                }

                for (; yref >= rule.LowerYear; yref--)
                {
                    if (!TzUtilities.IsYearType(yref, rule.YearType))
                    {
                        continue;
                    }

                    // On est obligé de rechercher la règle précédente si pour comparer les dates
                    // on a besoin du daylight saving time
                    if ((rule.AtKind == TimeKind.LocalWallTime && kind == DateTimeKind.Utc) ||
                        (rule.AtKind != TimeKind.LocalWallTime && kind == DateTimeKind.Local))
                    {
                        TimeSpan stdoff = GetLastSaveOffset(yref, rules, rule, startZone, gmtOffset);

                        ruleTime = TzUtilities.GetDateTime(rule, yref, gmtOffset, stdoff, kind);
                    }
                    else
                    {
                        // Si les deux règles sont en locales ou utc/standardoffset on peut les comparer directement
                        ruleTime = TzUtilities.GetDateTime(rule, yref, gmtOffset, TimeSpan.Zero, kind);
                    }

                    Debug.Assert(ruleTime.Kind == dateTime.Kind);

                    // Si la date d'application de la règle est supérieure au point recherché alors
                    // on recherche pour l'année précédente si on n'a pas atteint la borne inférieure
                    if (ruleTime > dateTime && ruleTime.Year > rule.LowerYear)
                    {
                        continue;
                    }

                    // Si la règle est comprise dans les bornes [startTimeZone, dateTime] ou [startTimeZone, dateTime[ alors elle
                    // est applicable, on la retient si elle est meilleure que celle déjà retenue.
                    if (ruleTime >= startTimeZone &&
                        ((ruleTime <= dateTime && ruleSearch == RuleSearchKind.LessThanOrEqual) ||
                         (ruleTime < dateTime && ruleSearch == RuleSearchKind.LessThan)))
                    {
                        TimeSpan diff = dateTime - ruleTime;
                        if (range > diff)
                        {
                            lastRule = rule;
                            range    = diff;
                            bestYear = ruleTime.Year;
                        }
                    }
                    break;
                }
            }

            return(lastRule);
        }