Exemplo n.º 1
0
        /// <summary>
        /// Obtient le standard offset applicable à une règle
        /// </summary>
        /// <param name="year"></param>
        /// <param name="rules"></param>
        /// <param name="rule"></param>
        /// <param name="zoneDate"></param>
        /// <param name="gmtOffset"></param>
        /// <returns></returns>
        /// <remarks>Cette fonction compare des règles définies, comme un changement d'heure ne se
        /// produit pas en même temps qu'un autre on peut appliquer le stdoff entre règles sans
        /// se soucier de point recouvrant.
        /// </remarks>
        private static TimeSpan GetLastSaveOffset(int year, List<Rule> rules, Rule rule, TzTimeZoneRuleDate zoneDate, TimeSpan gmtOffset)
        {
            int bestyear = -1;

            TimeSpan range = TimeSpan.MaxValue;
            TimeSpan stdoff = zoneDate.StandardOffset;

            for (int index = rules.Count - 1; index >= 0; index--) {
                if (rules[index] == rule) continue;

                if (rules[index].LowerYear > year || rules[index].HighYear < bestyear) {
                    continue;
                }

                int yref = (rules[index].HighYear > year) ? year : rules[index].HighYear;

                if (yref == year && rules[index].AtKind == rule.AtKind &&
                    (rules[index].Month > rule.Month || (rules[index].Month == rule.Month && rules[index].Day.DayWork == DayWorkKind.Dom && rule.Day.DayWork == DayWorkKind.Dom && rules[index].Day.DayOfMonth > rule.Day.DayOfMonth))) {
                    if (yref > rules[index].LowerYear)
                        yref--;
                    else
                        continue;
                }

                for (; yref >= rules[index].LowerYear; yref--) {
                    if (!TzUtilities.IsYearType(yref, rules[index].YearType)) continue;

                    DateTime precedent;
                    DateTime current;

                    // On compare la date avec la date limite de zone
                    if (rules[index].AtKind == TimeKind.LocalWallTime) {
                        precedent = TzUtilities.GetDateTime(rules[index], yref, gmtOffset, TimeSpan.Zero, DateTimeKind.Local);
                        if (zoneDate.ToLocalTime() > precedent)
                            break;
                        // On exprime la date courante de la règle à vérifier en local
                        current = TzUtilities.GetDateTime(rule, year, gmtOffset, rules[index].StandardOffset, DateTimeKind.Local);
                    }
                    else {
                        precedent = TzUtilities.GetDateTime(rules[index], yref, gmtOffset, TimeSpan.Zero, DateTimeKind.Utc);
                        if (zoneDate.UtcDate > precedent)
                            break;
                        // On exprime la date courante de la règle à vérifier en utc
                        current = TzUtilities.GetDateTime(rule, year, gmtOffset, rules[index].StandardOffset, DateTimeKind.Utc);
                    }

                    if (precedent > current) {
                        continue;
                    }
                    else {
                        TimeSpan diff = current - precedent;
                        if (range > diff) {
                            stdoff = rules[index].StandardOffset;
                            range = diff;
                            bestyear = precedent.Year;
                        }
                        break;
                    }
                }
            }

            return stdoff;
        }
Exemplo n.º 2
0
        /// <summary>
        /// Obtient le standard offset applicable à une règle
        /// </summary>
        /// <param name="year"></param>
        /// <param name="rules"></param>
        /// <param name="rule"></param>
        /// <param name="zoneDate"></param>
        /// <param name="gmtOffset"></param>
        /// <returns></returns>
        /// <remarks>Cette fonction compare des règles définies, comme un changement d'heure ne se
        /// produit pas en même temps qu'un autre on peut appliquer le stdoff entre règles sans
        /// se soucier de point recouvrant.
        /// </remarks>
        private static TimeSpan GetLastSaveOffset(int year, List <Rule> rules, Rule rule, TzTimeZoneRuleDate zoneDate, TimeSpan gmtOffset)
        {
            int bestyear = -1;

            TimeSpan range  = TimeSpan.MaxValue;
            TimeSpan stdoff = zoneDate.StandardOffset;

            for (int index = rules.Count - 1; index >= 0; index--)
            {
                if (rules[index] == rule)
                {
                    continue;
                }

                if (rules[index].LowerYear > year || rules[index].HighYear < bestyear)
                {
                    continue;
                }

                int yref = (rules[index].HighYear > year) ? year : rules[index].HighYear;

                if (yref == year && rules[index].AtKind == rule.AtKind &&
                    (rules[index].Month > rule.Month || (rules[index].Month == rule.Month && rules[index].Day.DayWork == DayWorkKind.Dom && rule.Day.DayWork == DayWorkKind.Dom && rules[index].Day.DayOfMonth > rule.Day.DayOfMonth)))
                {
                    if (yref > rules[index].LowerYear)
                    {
                        yref--;
                    }
                    else
                    {
                        continue;
                    }
                }

                for (; yref >= rules[index].LowerYear; yref--)
                {
                    if (!TzUtilities.IsYearType(yref, rules[index].YearType))
                    {
                        continue;
                    }

                    DateTime precedent;
                    DateTime current;

                    // On compare la date avec la date limite de zone
                    if (rules[index].AtKind == TimeKind.LocalWallTime)
                    {
                        precedent = TzUtilities.GetDateTime(rules[index], yref, gmtOffset, TimeSpan.Zero, DateTimeKind.Local);
                        if (zoneDate.ToLocalTime() > precedent)
                        {
                            break;
                        }
                        // On exprime la date courante de la règle à vérifier en local
                        current = TzUtilities.GetDateTime(rule, year, gmtOffset, rules[index].StandardOffset, DateTimeKind.Local);
                    }
                    else
                    {
                        precedent = TzUtilities.GetDateTime(rules[index], yref, gmtOffset, TimeSpan.Zero, DateTimeKind.Utc);
                        if (zoneDate.UtcDate > precedent)
                        {
                            break;
                        }
                        // On exprime la date courante de la règle à vérifier en utc
                        current = TzUtilities.GetDateTime(rule, year, gmtOffset, rules[index].StandardOffset, DateTimeKind.Utc);
                    }

                    if (precedent > current)
                    {
                        continue;
                    }
                    else
                    {
                        TimeSpan diff = current - precedent;
                        if (range > diff)
                        {
                            stdoff   = rules[index].StandardOffset;
                            range    = diff;
                            bestyear = precedent.Year;
                        }
                        break;
                    }
                }
            }

            return(stdoff);
        }
Exemplo n.º 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;
        }
Exemplo n.º 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);
        }