conversion date en utctime

conversion date en utctime - Algo - Programmation

Marsh Posté le 28-01-2013 à 14:28:24    

bonjour à vous,
 
petit casse tête pour moi aujourd'hui, je cherche à réaliser une conversion de date (format YY/MM/DD,HH/MM/SS)  vers un format UNIX (secondes écoulées depuis le 01/01/1970)....
facile me direz vous... suffit d'utiliser les api date(...) time(...) ou tout autre en fonction du langage utilisé.... mais non
 
c'est pour un system embarqué qui ne connait que les instructions de base, boucle et test :o
 
dire que 1 an c'est 31556926 secondes et un mois c'est 2629743 secondes ça ne marche pas à cause des décalage bissextile, des mois à 30/31/29 jours...
 
une idée ?
ça doit bien exister quelque part ce genre d’algorithme pour ne pas réinventer la poudre :o
 
:)


---------------
Mes ventes vers Grenoble & Gresivaudan
Reply

Marsh Posté le 28-01-2013 à 14:28:24   

Reply

Marsh Posté le 28-01-2013 à 15:46:32    

Je pense qu'il va falloir faire le truc à la main quand même.
Tu peux regarder la : Formule de calcul du nombre de jour dans une année

Reply

Marsh Posté le 28-01-2013 à 16:51:51    

cool, merci.
 
je me disais bien que cette formule devait être quelque part.
 
maintenant reste à espérer que mon system accepte la récursivité :)


---------------
Mes ventes vers Grenoble & Gresivaudan
Reply

Marsh Posté le 28-01-2013 à 18:08:14    

En fait c'est pas trop compliqué à faire à la main, aux calculs du mois de février près, si tu n'as pas à tenir compte des "leap seconds", sinon, ça va vite être prise de tête, comme la page suivante va te le montrer: http://en.wikipedia.org/wiki/Unix_time
A+,


Message édité par gilou le 28-01-2013 à 18:31:50

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 28-01-2013 à 21:09:20    

Tiens, j'ai pondu vite fait ceci qui fait le boulot (sans faire de vérification sur la validité des paramètres passés):
 

Code :
  1. #include <stdio.h>
  2. long int date2unix(int year, int month, int day, int hour, int min, int sec)
  3. {
  4.     int MINSECS  =  60;
  5.     int HOURSECS =  60 * MINSECS;
  6.     int DAYSECS  =  24 * HOURSECS;
  7.     int MONTHSECS[12] = {31*DAYSECS, 28*DAYSECS, 31*DAYSECS,
  8.                          30*DAYSECS, 31*DAYSECS, 30*DAYSECS,
  9.                          31*DAYSECS, 31*DAYSECS, 30*DAYSECS,
  10.                          31*DAYSECS, 30*DAYSECS, 31*DAYSECS
  11.                         };
  12.     int YEARSECS = 365 * DAYSECS;
  13.     // unix 0 = 1 January 1970 00:00:00
  14.     // elapsed years since 1970 * nb of sec in a standard year
  15.     int i;
  16.     long int unix = 0;
  17.     for (i = 1970; i < year; ++i) {
  18.         unix += YEARSECS;
  19.         // special case of leap year
  20.         if ((i % 400 == 0) || (( i % 100 != 0) && (i % 4 == 0 ))) {
  21.             unix += DAYSECS;
  22.         }
  23.     }
  24.     // elapsed months * nb of sec in each month
  25.     for (i = 1; i < month; ++i) {
  26.         unix += MONTHSECS[i-1];
  27.         // take care of february special case
  28.         if (i == 2) {
  29.             if ((year % 400 == 0) || (( year % 100 != 0) && (year % 4 == 0 ))) {
  30.                 unix += DAYSECS;
  31.             }
  32.         }
  33.     }
  34.     // elapsed days * nb of sec in a day
  35.     for (i = 1; i < day; ++i) {
  36.         unix += DAYSECS;
  37.     }
  38.     // elapsed hours * nb of sec in a hour
  39.     for (i = 0; i < hour; ++i) {
  40.         unix += HOURSECS;
  41.     }
  42.     // elapsed minutes * nb of sec in a minute
  43.     unix += min * MINSECS;
  44.     // elapsed seconds
  45.     unix += sec;
  46.     return unix;
  47. }
  48. int main()
  49. {
  50.     // cf http://en.wikipedia.org/wiki/Unix_time pour les valeurs de test
  51.     long int udate = date2unix(2013, 1, 28, 4, 12, 28); // 1359346348
  52.     printf("%ld\n", udate - 1359346348);
  53.     udate = date2unix(2001, 9, 9, 1, 46, 40); // 1,000,000,000 s
  54.     printf("%ld\n", udate);
  55.     return 0;
  56. }


 
A+,


Message édité par gilou le 29-01-2013 à 13:46:50

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 28-01-2013 à 22:14:29    

Le même en version compacte et un poil plus optimisée:

Code :
  1. long int date2unix(int year, int month, int day, int hour, int min, int sec)
  2. {
  3.     // cumulative seconds elapsed before month
  4.     int msec[12] = { 0, 2678400, 5097600, 7776000, 10368000, 13046400,
  5.                      15638400, 18316800, 20995200, 23587200, 26265600, 28857600 };
  6.     long int unix = ((year - 1970) * 31536000) + msec[month - 1] + ((day - 1) * 86400) + (hour * 3600) + (min * 60) + sec;
  7.     // adjust for leap days elapsed
  8.     int i, nb_leap = 0;
  9.     for (i = 1972; i < year; ++i) {
  10.         if ((i % 400 == 0) || (( i % 100 != 0) && (i % 4 == 0 ))) ++nb_leap;
  11.     }
  12.     if ((month > 2) && ((year % 400 == 0) || (( year % 100 != 0) && (year % 4 == 0 )))) ++nb_leap;
  13.     unix += nb_leap * 86400;
  14.     return unix;
  15. }


 
A+,


Message édité par gilou le 29-01-2013 à 11:19:49

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 29-01-2013 à 07:54:02    

hou purée j'aurai pas fais mieux... ma version était loin d'être optimisée .
 
merci je vais m'en servir volontiers :)


---------------
Mes ventes vers Grenoble & Gresivaudan
Reply

Marsh Posté le 29-01-2013 à 11:29:12    

J'ai fait une petite édition au code:
<< for (i = 1972; i != year; ++i) {
>> for (i = 1972; i < year; ++i) {
ça rajoute deux tours de boucle pas utiles (sauf pour year = 1970 ou 1971), mais permet d"avoir une fonction qui a pour domaine toute la plage des dates valides à partir du 1er Janvier 1970 00:00:00
 
Si tu passes des dates pas nécessairement valides a cette fonction, il faudra au moins vérifier que 1<= month <= 12
 
Il y a surement moyen d'optimiser un peu plus et diviser le nb de boucles par 4  (ne pas faire les tours de boucle inutiles, voire même peut être pas de boucle du tout). Je posterai cela un peu plus tard.
 
A+,


Message édité par gilou le 29-01-2013 à 11:37:38

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 29-01-2013 à 12:33:32    

Code :
  1. long int date2unix(int year, int month, int day, int hour, int min, int sec)
  2. {
  3.     // cumulative seconds elapsed before month
  4.     int msec[12] = { 0, 2678400, 5097600, 7776000, 10368000, 13046400,
  5.                      15638400, 18316800, 20995200, 23587200, 26265600, 28857600 };
  6.     long int unix = ((year - 1970) * 31536000) + msec[month - 1] + ((day - 1) * 86400) + (hour * 3600) + (min * 60) + sec;
  7.     // adjust for leap days elapsed
  8.     int i;
  9.     for (i = 1972; i < year; i += 4) {
  10.         if ((i % 400 == 0) || ( i % 100 != 0)) unix += 86400;
  11.     }
  12.     if ((month > 2) && ((year % 400 == 0) || (( year % 100 != 0) && (year % 4 == 0 )))) unix += 86400;
  13.     return unix;
  14. }


Voila: cette version démarre à la première année bissextile qui suit 1970, et incrémente le compteur de 4 en 4, donc on ne fait pas de tour de boucle inutile.
Au passage, j'ai supprimé aussi l'emploi de la variable, et fait directement l'incrémentation du nb de secondes.
S'il y avait pas de condition zarbi supplémentaire, on pourrait même éviter le boucle et faire une simple multiplication.
 
A+,


Message édité par gilou le 29-01-2013 à 12:43:56

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 29-01-2013 à 13:16:25    

mince c'est furieux ça.  
sur quel données tu t'es basé pour pondre un algo pareil ?
 
je suis quand même assez étonné, bravo. j'aimerais bien arriver a faire des algo aussi facilement :)


---------------
Mes ventes vers Grenoble & Gresivaudan
Reply

Marsh Posté le 29-01-2013 à 13:16:25   

Reply

Marsh Posté le 29-01-2013 à 14:13:35    

Je t'ai ajouté des commentaires dans la toute première version pour que ce soit plus clair.
C'est juste un bête comptage du nb de secondes écoulées depuis le premier janvier 1970 à minuit jusqu'à la date donnée.
 
Ensuite, il y a quelques optimisations pour supprimer les boucles lorsque c'est possible ou ne pas faire de tour de boucle inutile sinon, vu que tu fais de l'embarqué, ou ça peut être important.
 
Si tu as besoin de valider une date avant de la convertir en temps unix, ceci devrait faire l'affaire:

Code :
  1. int validdate(int year, int month, int day, int hour, int min, int sec) {
  2.     int mday[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  3.     return ((1970 <= year)
  4.             && (1 <= month) && (month <= 12)
  5.             && (1 <= day)   && ((day <= mday[month - 1])
  6.                               || ((day == 29) && (month == 2) && ((year % 400 == 0) || ((year % 100 != 0) && (year % 4 == 0)))))
  7.             && (0 <= hour)  && (hour <= 23)
  8.             && (0 <= min)   && (min <= 59)
  9.             && (0 <= sec)   && (sec <= 59 )
  10.             )?1:0;
  11. }


 
Une dernière note: ma fonction date2unix (et la validation de date) ne tient pas compte des leap seconds, ce qui fait qu'au moment ou on insère une leap second (au plus deux fois par an) il peut y avoir un décalage d'une seconde avec le temps unix qui en tient compte. Comme dans la seconde qui suit, le temps unix recule d'une seconde, ce n'est a priori pas important en général, mais c'est à toi de voir si c'est critique pour ton application (si elle se synchronise avec des machines unix qui utilisent un temps unix qui en tient compte), car on a vu des problèmes survenir a cause de cela http://en.wikipedia.org/wiki/Leap_ [...] eap_second
 
EDIT: en y repensant, le code est OK même pour les dates avec une leap second, ce qui me laisse penser que la fonction codée dans les bécanes doit être similaire à la mienne.
par contre, pour validdate, on pourrait modifier le code en faisant le remplacement suivant:
>>      && (0 <= sec)   && ((sec <= 59) || ((sec == 60) && (min == 59) && (hour == 23) && (((day == 30) && (month == 6)) || ((day == 31) && (month == 12)))))
<<      && (0 <= sec)   && (sec <= 59 )
Ce qui validerait des dates correspondant a des leap second (sec vaut 60 les 30 juins et 31 décembre à minuit), ça le validerait aussi pour des années ou on n'a rien ajouté, mais ça ne devrait pas trop prêter à conséquence.
A+,


Message édité par gilou le 29-01-2013 à 16:06:03

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 29-01-2013 à 16:24:22    

Tiens en cherchant un peu sur internet, j'au vu que effectivement, ce qui est utilisé est proche de ma fonction:
http://ww1.microchip.com/downloads [...] 01412A.pdf
au détail près que leur implémentation est buggée, puisqu'ils considèrent que une année est bissextile si elle est multiple de 4, ce qui est faux.
 
En cherchant un peu, j'ai trouvé ceci: http://www.madore.org/~david/compu [...] conds.html
tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
    (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
    ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
(ou tm_year est year - 1900 et tm_yday est le numéro du jours dans l'année, en démarrant à 0).
Ça devrait permettre d'optimiser un peu plus le code, il y avait donc moyen de virer la dernière boucle et d'avoir des multiplications...
A+,

Message cité 1 fois
Message édité par gilou le 29-01-2013 à 17:36:29

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 29-01-2013 à 16:59:12    

Bon ben je crois qu'il est assez difficile ou inutile d'optimiser plus que ceci:

Code :
  1. long int date2unix(int year, int month, int day, int hour, int min, int sec)
  2. {
  3.     // convert day to yday
  4.     static int eday[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
  5.     day = eday[month - 1] + day - 1;
  6.     if ((month > 2) && ((year % 400 == 0) || (( year % 100 != 0) && (year % 4 == 0 )))) day += 1;
  7.     return sec + min*60 + hour*3600 + day*86400 + (year-1970)*31536000 + (((year-1969)/4) - ((year-1901)/100) + ((year-1601)/400))*86400;
  8. }


Faire attention à ne pas avoir de troncatures sur ton système, je ne sais pas de quelle taille sont tes ints et tes longs.
Faire aussi attention que ça atteindra sa valeur maximale (si tes longs font au plus 2147483647) relativement rapidement, on en est déjà à 1359480060, donc si c'est pour de l'embarqué spatial ou autre qui doit être encore fonctionnel dans 40 ans...
A+,


Message édité par gilou le 29-01-2013 à 18:01:49

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 29-01-2013 à 18:14:58    

gilou a écrit :


... au détail près que leur implémentation est buggée, puisqu'ils considèrent que une année est bissextile si elle est multiple de 4, ce qui est faux.


 
C'est vrai jusqu'au 28/02/2100 inclus ... D'ici-là, leur code ne sera plus utilisé :)
 
Le bug de l'an 2038 est beaucoup plus préoccupant !


Message édité par Farian le 29-01-2013 à 18:16:57
Reply

Marsh Posté le 29-01-2013 à 19:25:13    

Citation :

C'est vrai jusqu'au 28/02/2100 inclus ... D'ici-là, leur code ne sera plus utilisé

C'est vrai, mais leur commentaire dans la doc à ce sujet est faux: Leap Year Calculation up to 2399

Citation :

Le bug de l'an 2038 est beaucoup plus préoccupant !

Clairement, à moins que tout int soit au moins en 64 bits à cette date, mais on peut en douter. Y'aura sans doute des trucs embarqués qui seront encore en activité.
A propos de début de système de datation bizarre, une idée du pourquoi celui des fichier sous DOS/Windows commence le 1er janvier 1601 à minuit?  
EDIT: J'aurais d'abord du regarder sur Wikipedia: http://en.wikipedia.org/wiki/1601
 
A+,


Message édité par gilou le 29-01-2013 à 19:27:35

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 30-01-2013 à 16:23:37    

Que ceux qui ont pensé à traiter le cas de l'heure d'été et d'hiver lèvent la main...  :whistle:  
 
Ben oui, suivant la date que tu vas passer en paramètre, tu peux te retrouver avec une heure en moins et donc passer au jour précédent :/ Dans mon appli Astres (cf ma signature), je m'étais basé comme vous sur le nb de secondes par an pour générer, dans une stat, en axe X, des dates comprises entre une date de début et de fin. Ben, perdu.  :sweat: Suivant les dates en entrée, l'heure d'été et d'hiver peuvent se compenser ou pas. J'ai mis un certain temps à comprendre la cause du bug...


---------------
Astres, outil de help-desk GPL : http://sourceforge.net/projects/astres, ICARE, gestion de conf : http://sourceforge.net/projects/icare, Outil Planeta Calandreta : https://framalibre.org/content/planeta-calandreta
Reply

Marsh Posté le 30-01-2013 à 18:45:47    

rufo a écrit :

Que ceux qui ont pensé à traiter le cas de l'heure d'été et d'hiver lèvent la main...  :whistle:  
 
Ben oui, suivant la date que tu vas passer en paramètre, tu peux te retrouver avec une heure en moins et donc passer au jour précédent :/ Dans mon appli Astres (cf ma signature), je m'étais basé comme vous sur le nb de secondes par an pour générer, dans une stat, en axe X, des dates comprises entre une date de début et de fin. Ben, perdu.  :sweat: Suivant les dates en entrée, l'heure d'été et d'hiver peuvent se compenser ou pas. J'ai mis un certain temps à comprendre la cause du bug...

C'est traité dans le code lien suivant que j'avais filé il y a deux ou trois posts: http://ww1.microchip.com/downloads [...] 01412A.pdf
A+,


Message édité par gilou le 30-01-2013 à 18:46:32

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 31-01-2013 à 09:27:38    

heureusement pour moi la date que je récupère viens d'un réseau qui n'a pas de décalage été/hiver et est en GMT+0
 
en tout cas merci pour tout


---------------
Mes ventes vers Grenoble & Gresivaudan
Reply

Marsh Posté le 02-02-2013 à 03:06:37    

Au cas ou ça intéresse qqu'un voici d'ou sortent les facteurs (((year-1969)/4) - ((year-1901)/100) + ((year-1601)/400))*86400;
En fait, on s'intéresse a la période 1970...year-1  (la contribution de la dernière année dans la formule est donnée par day*86400 (ou day est le yearday, qui démarre à 0))
les facteurs deviennent:
(((year-1-1968)/4) - ((year-1-1900)/100) + ((year-1-1600)/400))*86400;
Et on comprends alors les facteurs:
1968 est la 1e année multiple de 4 avant 1970
1900 est la 1e année multiple de 100 avant 1970
1600 est la 1e année multiple de 400 avant 1970
 
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed