web-dev-qa-db-fra.com

Calcul de l'heure d'été à partir de la date seulement

Je travaille avec un Arduino et une puce d'horloge en temps réel. La puce compense les années bissextiles et autres, de sorte qu’elle aura toujours la date correcte, mais elle ne gère pas l’heure avancée, je suppose en raison de complications régionales. L'horloge peut me donner le jour, le mois et l'année (1 basé) et le jour de la semaine (dimanche = 0 au samedi = 6).

Étant donné que je dois comparer les dates et les heures entrées par l'utilisateur, j'ai besoin de connaître la date et l'heure ajustées pour l'heure d'été. Si la date actuelle est en heure avancée, je peux simplement ajouter une heure à l'heure de l'horloge et j'ai ce dont j'ai besoin.

La partie difficile est de déterminer si je suis en heure d'été ou non, car cela change d'année en année. Je tiens seulement à ce que cela fonctionne chez moi (heure des Rocheuses) Il ne semble pas y avoir de bibliothèque de dates complète pour ma plate-forme, et j’ai l’impression que ce serait exagéré de toute façon. Existe-t-il une formule simple pour déterminer si je suis ou non sous DST?

16
Muggz

Ceci est en fait trompeusement simple. Quelques faits nous aideront à:

  1. Dans la plupart des États-Unis, l'heure d'été commence le deuxième dimanche de mars et se termine le premier dimanche de novembre, à deux heures du matin.
  2. Le deuxième dimanche de mars aura toujours lieu entre le 8 et le 14 inclus.
  3. Le premier dimanche de novembre sera toujours entre le 1er et le 7 inclus.
  4. La numérotation des jours de la semaine est assez pratique car le jour - jour de la semaine vous donnera le dimanche précédent.

Ces faits conduisent au code suivant (C #, mais trivialement portable sur votre plate-forme):

    public bool IsDST(int day, int month, int dow)
    {
        //January, february, and december are out.
        if (month < 3 || month > 11) { return false; }
        //April to October are in
        if (month > 3 && month < 11) { return true; }
        int previousSunday = day - dow;
        //In march, we are DST if our previous sunday was on or after the 8th.
        if (month == 3) { return previousSunday >= 8; }
        //In november we must be before the first sunday to be dst.
        //That means the previous sunday must be before the 1st.
        return previousSunday <= 0;
    }

Il s’avère que vous n’avez même pas besoin de connaître l’année pour le faire, tant que vous pouvez faire confiance à la valeur de votre jour de la semaine.

J'ai écrit un test unitaire rapide et vérifié que ce code était conforme à TimeZone.IsDayLightSavingsTime() pour toutes les dates comprises entre 1800 et 2200. Je ne tenais pas compte de la règle des 2 heures du matin, mais vous pouvez facilement vérifier si le jour de la semaine est dimanche et la date indiquée. entre 8 et 14 (en mars) ou entre 1 et 7 (en novembre).

37
captncraig

Code pour l'Europe centrale (testé chaque jour entre 2014 et 3000 ans)

    public static bool IsDst(int day, int month, int dow)
    {
        if (month < 3 || month > 10)  return false; 
        if (month > 3 && month < 10)  return true; 

        int previousSunday = day - dow;

        if (month == 3) return previousSunday >= 25;
        if (month == 10) return previousSunday < 25;

        return false; // this line never gonna happend
    }

Fonction de test

    static void Main(string[] args)
    {
        TimeZoneInfo tzf2 = TimeZoneInfo.FindSystemTimeZoneById("Central Europe Standard Time");

        var date = new DateTime(2014, 01, 1, 5, 0,0);
        bool wasSummer = false;

        while (date <= new DateTime(3000,1,1))
        {                                         
            var dow = (int) date.DayOfWeek;

            var isDst = IsDst(date.Day, date.Month, dow);               

            DateTime f2 = TimeZoneInfo.ConvertTime(date, tzf2);
            var isSummer = f2.IsDaylightSavingTime();

            if (isSummer != isDst)
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(date);
            }

            if (isSummer != wasSummer)
            {
                Console.WriteLine(date.AddDays(-1).ToShortDateString());
            }

            date = date.AddDays(1);
            wasSummer = isSummer;
        }

        Console.ReadKey();

}

11
akworob

S'il est facile de calculer si une date donnée est à l'heure d'été pour un lieu particulier en vertu des règles actuelles, notez que l'heure d'été est à la merci des politiciens et peut changer à tout moment. J'ai une horloge fabriquée avant 2007 qui s'adapte automatiquement à l'heure avancée, et je dois maintenant la modifier quatre fois par an: deux fois lorsque le changement effectif se produit et deux fois lorsqu'il se modifie de manière erronée. aux anciennes dates.

Dans ce cas, vous pouvez peut-être ignorer DST complètement par le simple fait de demander à l'utilisateur de saisir le fuseau horaire avec la date et l'heure. Vous pouvez également faire comme la plupart des appareils grand public et laisser l’utilisateur ajuster l’heure au fuseau horaire local deux fois par an.

Mais si vous avez vraiment besoin de gérer DST et que vous voulez vraiment faire les choses correctement, utilisez la base de données zoneinfo et assurez-vous qu’elle peut être mise à jour d’une manière ou d’une autre. Si vous ne pouvez pas le faire pour une raison quelconque, autorisez au moins l'utilisateur à remplacer les règles. Et même si cela est trop difficile, donnez au moins à l'utilisateur la possibilité de désactiver les réglages automatiques (contrairement à mon réveil stupide).

2
Anomie

J'essaie avec cette approche et je pense que c'est simple et précis:

// pour le premier dimanche de mars comme si DoW = 1 pour le dimanche si (mois == 3 && jour> = 8 && jour <= 14 && DoW = 1) renvoie True 

// pour le deuxième dimanche de novembre comme si DoW = 1 pour le dimanche si (mois == 11 && jour> = 1 && jour <= 7 && DoW = 1) renvoie True 

0
Ed Truong

Le 14 mars et le 7 novembre font toujours partie de la semaine au cours de laquelle les économies sont légères aux états-unis. Elles peuvent être le dimanche ou le samedi ou le jour de la semaine entre les deux, mais elles font toujours partie de cette semaine. le code ci-dessous trouvera le dimanche de ces deux dates dans l'année de la date en question. Il ajoute ensuite 2 heures à cette date pour obtenir l'heure à laquelle l'heure d'été a réellement lieu. vous comparez ensuite la date en question avec les dates de début et de fin de l'heure avancée et ajustez l'heure en fonction du décalage horaire. Ce processus peut fonctionner pour les dates de début et de fin des autres pays. vous pouvez configurer une table contenant tous les codes de pays et les codes postaux, ainsi que les dates de début et de fin de l'heure avancée et le décalage horaire pour les deux périodes. les dates seraient les 7, 14, 21 et 28, du premier au quatrième dimanche du mois. vous mettrez dans le jour maximum pour le dernier dimanche du mois ex 30 septembre ou 31 octobre. 

2ème dimanche de mars:

cdate("3/14/" & format(now(),"yyyy"))-format(cdate("3/14/" & format(now(),"yyyy")),"W")+1+2/24

1er dimanche de novembre:

cdate("11/7/" & format(now(),"yyyy"))-format(cdate("11/7/" & format(now(),"yyyy")),"W")+1+2/24

Ex.

If(now() < cdate("3/14/" & format(now(),"yyyy"))-format(cdate("3/14/" & format(now(),"yyyy")),"W")+1+2/24, dateadd("H",-5,now()), if(now() < cdate("11/7/" & format(now(),"yyyy"))-format(cdate("11/7/" & format(now(),"yyyy")),"W")+1+2/24, dateadd("H",-6,now()), dateadd("H",-5,now())))

t_SQL exemple

CASE WHEN [date2check] <DATEADD (hh, 2, CAST ('3/14 /' + CAST (DATEPART (yyyy, [date2check]) AS nvarchar (4)) as date/heure) + 1 - DATEPART (4 w, CAST ('3/14 /' + CAST (DATEPART (aaaa, [date2check]) AS nvarchar (4)) AS datetime)) THEN dateadd (hh, - DST_GMT_TM_ZN_DIFF, [date2check]) ELSE CAS WHEN [date2check] <DATEADD (hh, 2, CAST ('11/7/'+ CAST (DATEPART (yyyy, [Date2check]) AS nvarchar (4)) AS date/heure) + 1 - DATEPART (4 w, CAST ('11/7/'+ CAST (DATEPART (aaaa, [date2check]) AS nvarchar (4)) AS date/heure)) ALORS dateadd (hh, - STD_GMT_TM_ZN_DIFF, [date2check]), ELSE dateadd (hh, - DST_GMT_TM_ZN_DIFF, [date2check]) END END

0
Rob

ce code utilise mktime pour obtenir un jour de la semaine. Il utilisait un jour de la semaine pour calculer l'heure d'été. Si vous ne voulez pas utiliser mktime, vous pouvez utiliser le programme second_sunday. Commencez avec 3/14/2007, qui est mercredi. Le jour de la semaine avance d’un jour pour chaque année et de deux jours pour chaque saut après 2004.

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/timeb.h>


int isDst(int month, int dayOfMonth, int hour, int dayOfWeek);

int main(int argc, char *argv[])
{
   int isdst, dayOfWeek;
   char buf[80];
   struct tm tmData;

   if( argc == 1 )
   {
      printf("\nsyntax: %s mm/dd/yyyy_hh:mm:00", argv[0]);
      return -1;
   }

   // 0123456789A12
   // 03/12/2018_12
   strcpy(buf, argv[1]);
   tmData.tm_mon   = atoi(&buf[0]) - 1;  //month -1
   tmData.tm_mday  = atoi(&buf[3]); //day of month
   tmData.tm_year  = atoi(&buf[6]) - 1900; // year - 1900
   tmData.tm_hour  = atoi(&buf[11]); // hour
   tmData.tm_min   = 0; //minutes (not used)
   tmData.tm_sec   = 0; //seconds (not used)
   //tmData.tm_min   = atoi(&buf[14]);
   //tmData.tm_sec   = atoi(&buf[27]);

   //day light saving time variable.
   //NOT used in this calculation.
   //Tells mktime the input date is in day light saving time
   tmData.tm_isdst = 0; //

   mktime(&tmData);
   dayOfWeek = tmData.tm_wday;

   printf("%02d/%02d/%2d_%02d dayWk=%d ",
      tmData.tm_mon+1, tmData.tm_mday, tmData.tm_year, tmData.tm_hour, dayOfWeek);
   isdst = isDst(tmData.tm_mon+1, tmData.tm_mday, tmData.tm_hour, dayOfWeek);
   printf("isdst=%d\n", isdst);

   return 0;
}


int isDst(int month, int dayOfMonth, int hour, int dayOfWeek)
{
   int second_sunday, first_sunday;

   if( month  > 3 && month < 11  ) return 1; //4,5,6,7,8,9,10
   if( month  < 3 || month == 12 ) return 0; //1, 2 or 12
   if( month == 3 )
   {
      //The 2nd Sunday in March is 8,9,10,11,12,13,14
      if( dayOfMonth < 8  ) return 0;
      if( dayOfMonth > 14 ) return 1;

      //To get here dayOfMonth >= 8 && dayOfMonth <= 14
      second_sunday = dayOfMonth - dayOfWeek;
      if( second_sunday < 8 ) second_sunday += 7;
printf("2nd_Sunday=%2d ", second_sunday);
      if( dayOfMonth > second_sunday ) return 1;
      if( dayOfMonth < second_sunday ) return 0;

      //To get here dayOfMonth = second_sunday
      if( hour >= 2 ) return 1;
      else return 0;
   }

   if( month == 11 )
   {
      //The 1st Sunday in Nov is 1,2,3,4,5,6,7
      if( dayOfMonth > 7 ) return 0;

      //To get here dayOfMonth >= 1 && dayOfMonth <= 7
      first_sunday = dayOfMonth - dayOfWeek;
      if( first_sunday < 1 ) first_sunday += 7;
printf("1st_Sunday=%2d ", first_sunday);
      if( dayOfMonth > first_sunday ) return 0;
      if( dayOfMonth < first_sunday ) return 1;

      //To get here dayOfMonth = first_sunday
      if( hour >= 2 ) return 0;
      else return 1;
   }
   return -1;
}
/**************
   Compile via   cl.exe  isDst.c

   Begin and End dates for day light saving time
   03/11/2007_01:00:00    11/04/2007_01:00:00
   03/09/2008_01:00:00    11/02/2008_01:00:00
   03/08/2009_01:00:00    11/01/2009_01:00:00
   03/14/2010_01:00:00    11/07/2010_01:00:00
   03/13/2011_01:00:00    11/06/2011_01:00:00
   03/11/2012_01:00:00    11/04/2012_01:00:00
   03/10/2013_01:00:00    11/03/2013_01:00:00
   03/09/2014_01:00:00    11/02/2014_01:00:00
   03/08/2015_01:00:00    11/01/2015_01:00:00
   03/13/2016_01:00:00    11/06/2016_01:00:00
   03/12/2017_01:00:00    11/05/2017_01:00:00
   03/11/2018_01:00:00    11/04/2018_01:00:00
   03/10/2019_01:00:00    11/03/2019_01:00:00
   03/08/2020_01:00:00    11/01/2020_01:00:00
   03/14/2021_01:00:00    11/07/2021_01:00:00
   03/13/2022_01:00:00    11/06/2022_01:00:00
   03/12/2023_01:00:00    11/05/2023_01:00:00
   03/10/2024_01:00:00    11/03/2024_01:00:00
   03/09/2025_01:00:00    11/02/2025_01:00:00
   03/08/2026_01:00:00    11/01/2026_01:00:00
   03/14/2027_01:00:00    11/07/2027_01:00:00
   03/12/2028_01:00:00    11/05/2028_01:00:00
   03/11/2029_01:00:00    11/04/2029_01:00:00
   03/10/2030_01:00:00    11/03/2030_01:00:00
   03/09/2031_01:00:00    11/02/2031_01:00:00
   03/14/2032_01:00:00    11/07/2032_01:00:00

   isDst.exe 03/11/2007_02:00:00  >> dst.txt
   isDst.exe 03/09/2008_02:00:00  >> dst.txt
   isDst.exe 03/08/2009_02:00:00  >> dst.txt
   isDst.exe 03/14/2010_02:00:00  >> dst.txt
   isDst.exe 03/13/2011_02:00:00  >> dst.txt
   isDst.exe 03/11/2012_02:00:00  >> dst.txt
   isDst.exe 03/10/2013_02:00:00  >> dst.txt
   isDst.exe 03/09/2014_02:00:00  >> dst.txt
   isDst.exe 03/08/2015_02:00:00  >> dst.txt
   isDst.exe 03/13/2016_02:00:00  >> dst.txt
   isDst.exe 03/12/2017_02:00:00  >> dst.txt
   isDst.exe 03/11/2018_02:00:00  >> dst.txt
   isDst.exe 03/10/2019_02:00:00  >> dst.txt
   isDst.exe 03/08/2020_02:00:00  >> dst.txt
   isDst.exe 03/14/2021_02:00:00  >> dst.txt
   isDst.exe 03/13/2022_02:00:00  >> dst.txt
   isDst.exe 03/12/2023_02:00:00  >> dst.txt
   isDst.exe 03/10/2024_02:00:00  >> dst.txt
   isDst.exe 03/09/2025_02:00:00  >> dst.txt
   isDst.exe 03/08/2026_02:00:00  >> dst.txt
   isDst.exe 03/14/2027_02:00:00  >> dst.txt
   isDst.exe 03/12/2028_02:00:00  >> dst.txt
   isDst.exe 03/11/2029_02:00:00  >> dst.txt
   isDst.exe 03/10/2030_02:00:00  >> dst.txt
   isDst.exe 03/09/2031_02:00:00  >> dst.txt
   isDst.exe 03/14/2032_02:00:00  >> dst.txt
   isDst.exe 11/04/2007_02:00:00  >> dst.txt
   isDst.exe 11/02/2008_02:00:00  >> dst.txt
   isDst.exe 11/01/2009_02:00:00  >> dst.txt
   isDst.exe 11/07/2010_02:00:00  >> dst.txt
   isDst.exe 11/06/2011_02:00:00  >> dst.txt
   isDst.exe 11/04/2012_02:00:00  >> dst.txt
   isDst.exe 11/03/2013_02:00:00  >> dst.txt
   isDst.exe 11/02/2014_02:00:00  >> dst.txt
   isDst.exe 11/01/2015_02:00:00  >> dst.txt
   isDst.exe 11/06/2016_02:00:00  >> dst.txt
   isDst.exe 11/05/2017_02:00:00  >> dst.txt
   isDst.exe 11/04/2018_02:00:00  >> dst.txt
   isDst.exe 11/03/2019_02:00:00  >> dst.txt
   isDst.exe 11/01/2020_02:00:00  >> dst.txt
   isDst.exe 11/07/2021_02:00:00  >> dst.txt
   isDst.exe 11/06/2022_02:00:00  >> dst.txt
   isDst.exe 11/05/2023_02:00:00  >> dst.txt
   isDst.exe 11/03/2024_02:00:00  >> dst.txt
   isDst.exe 11/02/2025_02:00:00  >> dst.txt
   isDst.exe 11/01/2026_02:00:00  >> dst.txt
   isDst.exe 11/07/2027_02:00:00  >> dst.txt
   isDst.exe 11/05/2028_02:00:00  >> dst.txt
   isDst.exe 11/04/2029_02:00:00  >> dst.txt
   isDst.exe 11/03/2030_02:00:00  >> dst.txt
   isDst.exe 11/02/2031_02:00:00  >> dst.txt
   isDst.exe 11/07/2032_02:00:00  >> dst.txt
   https://stackoverflow.com/questions/5590429/calculating-daylight-saving-time-from-only-date
***************/



/*****
The previous programs used mktime to compute day_of_week.
It used day_of_week to compute 2nd_sunday in march and
1st_sunday in Nov.
If you don't want to use mktime, you can use this program to
compute 2nd_sunday.  The same technique will compute 1st_sunday.

On 03/14/2007, the day of the week is Wed, or 3.
Every year after 2007, the day of the week advances 1 day.
on leap years, the day of the week advances 2 days.
Must include the no. of leap years sinc 2004.
******/

#include <stdio.h>
#include <string.h>
#include <time.h>

int secondSunday(year);

int main(int argc, char *argv[])
{
   int year, second_sunday;

   if( argc == 1 )
   {
      printf("\nsyntax: %s year, with year >= 2007.\n", argv[0]);
      return -1;
   }

   year = atoi(argv[1]);
   if( year < 2007 )
   {
      printf("\nsyntax: %s year, with year >= 2007.\n", argv[0]);
      return -1;
   }

   second_sunday = secondSunday(year);
   printf("second_sunday=%d\n", second_sunday);

   return 0;
}


int secondSunday(year)
{
   //On 03/14/2007, the day of the week is Wed, or 3.
   int no_years, no_leaps, day_of_week, second_sunday;

   no_years = year - 2007;
   no_leaps = (year - 2004)/4;
   day_of_week = 3 + (no_years + no_leaps) % 7;
   second_sunday = 14 - day_of_week;
   if( second_sunday < 8 ) second_sunday += 7;

   //printf("no_years=%d,no_leaps=%d,day_of_week=%d, second_sunday=%d\n",
   //no_years, no_leaps, day_of_week, second_sunday);

   return second_sunday;
}
/**************
   Compile via   cl.exe  second_sunday.c

   second_sunday.exe 2007
   second_sunday.exe 2008
   second_sunday.exe 2009
   second_sunday.exe 2010
   second_sunday.exe 2011
   second_sunday.exe 2012
   second_sunday.exe 2013
   second_sunday.exe 2014
   second_sunday.exe 2015
   second_sunday.exe 2016
   second_sunday.exe 2017
   second_sunday.exe 2018
   second_sunday.exe 2019
   second_sunday.exe 2020
   second_sunday.exe 2021
   second_sunday.exe 2022
   second_sunday.exe 2023
   second_sunday.exe 2024
   second_sunday.exe 2025
   second_sunday.exe 2026
   second_sunday.exe 2027
   second_sunday.exe 2028
   second_sunday.exe 2029
   second_sunday.exe 2030
   second_sunday.exe 2031
   second_sunday.exe 2032
***************/
0
Grog Klingon

Voici ma réponse et toute correction est la bienvenue. On suppose que les années se situent entre 2000 et 2099 inclus. Plus de détails disponibles via le lien de référence.

  int timezone = 0;  // Set to correct initial value depending on where you are (or via GPS if you like).
  // Calculate day of week for Daylight savings time.
  int day_of_week = (day_of_month + int(2.6 * (((month + 12 - 3) % 12) + 1) - 0.2) - 40 + 
              (month < 3 ? year-1 : year) + int((month < 3 ? year-1 : year)/4) + 5) % 7;
  // Adjust timezone based on Daylight savings time for northern hemisphere, USA
  if ((month  >  3 && month < 11 ) || 
      (month ==  3 && day_of_month >= 8 && day_of_week == 0 && hour >= 2) ||  // DST starts 2nd Sunday of March;  2am
      (month == 11 && day_of_month <  8 && day_of_week >  0) ||
      (month == 11 && day_of_month <  8 && day_of_week == 0 && hour < 2)) {   // DST ends 1st Sunday of November; 2am
    timezone++;
  }

Référence de calcul du jour de la semaine: Comment déterminer le jour de la semaine en fonction du mois, du jour et de l’année
La référence du test DST se fait via cet article, à l’aide de captncraig et de mon propre raisonnement et interprétation de sa réponse.

0
user35060