/* ---------------------------------------------------------------------
   (c) ED 2000-2004
   Projet       : CLIB
   Fonction     : Gestion des dates
   Module       : DATE
   Fichier      : DATE.C
   Creation     : 10-01-2000
   Modification : 16-01-2004
   --------------------------------------------------------------------- */

/* ---------------------------------------------------------------------
   Journal

   0.0 du 10-01-2000 Creation
   1.0 du 11-01-2000 Version operationelle
   1.1 du 26-03-2003 Ajout de la structure sDATE et des fonctions de
   .                 conversion 'struct tm' <-> 'sDATE'
   2.0 du 23-04-2003 Les anciennes fonctions 'tm' deviennent DATE_tm_*()
   .                 Les fonctions DATE_*() utilisent sDATE
   2.1 du 28-04-2003 Dans DATE2tm(), ajout de 'tm_isdst = 0'
   2.2 du 29-04-2003 Ajout des formats standard pour printf DATE_FTM_xxx
   2.3 du 06-05-2003 Ajout de DATE_print() (si DATE_PRINT est defini)
   2.4 du 16-01-2004 Mise au point DATE_is_valid()
   --------------------------------------------------------------------- */
#ifdef __cplusplus
#error This source file is not C++ but rather C. Please use a C-compiler
#endif

#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "ed/inc/date.h"
#include "ed/inc/str.h"
#include "ed/inc/sys.h"
#include "ed/inc/sysalloc.h"

#ifdef DATE_PRINT
#include <stdio.h>
#endif

/* macros ============================================================== */

#define MODULE "DATE"
#define VER "2.4"
#define ID "DATE Module \"C\" (c) ED 2000-2004"

/* constants =========================================================== */

#ifdef DATE_TM

#if DATETIME2tm

#define SEP_D " "
#define SEP_H ":"

/* juste pour avoir la taille */
static char const szDATE[] = __DATE__;
static char const szTIME[] = __TIME__;

#endif /* DATETIME2tm */

#endif /* DATE_TM */

/* MONth TO Number Of Days */
static int const Mon2nod[] =
{
   0
   ,31                          /* Jan */
   ,28                          /* Feb */
   ,31                          /* Mar */
   ,30                          /* Apr */
   ,31                          /* May */
   ,30                          /* Jun */
   ,31                          /* Jul */
   ,31                          /* Aug */
   ,30                          /* Sep */
   ,31                          /* Oct */
   ,30                          /* Nov */
   ,31                          /* Dec */
};

/* types =============================================================== */
/* structures ========================================================== */
/* private data ======================================================== */
/* private functions =================================================== */

/* ---------------------------------------------------------------------
   is_leap()
   ---------------------------------------------------------------------
   Role :
   ---------------------------------------------------------------------
   E : year (YYYY)
   S : 0=err 1=ok
   --------------------------------------------------------------------- */
static int is_leap (uint const year)
{
   return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}

#ifdef DATE_TM
/* ---------------------------------------------------------------------
   tm_is_valid()
   ---------------------------------------------------------------------
   OBSOLETE
   ---------------------------------------------------------------------
   E :
   S : 0=OK -1=KO
   --------------------------------------------------------------------- */
static int tm_is_valid (struct tm const *const p_tm, int *const day_max)
{
   int cr = -1;
   int month = p_tm->tm_mon;

   if (month <= 12
       && month >= 1)
   {
      int nod = Mon2nod[month];
      int year = p_tm->tm_year;

      if (p_tm->tm_mon == 2)    /* Fevrier */
      {

         if (is_leap (year))
         {
            nod = 29;
         }
      }

      if (p_tm->tm_mday > 0
          && p_tm->tm_mday <= nod)
      {
         if (day_max)
         {
            *day_max = nod;
         }

         cr = 0;
      }
   }
   return cr;
}
#endif /* DATE_TM */

/* ---------------------------------------------------------------------
   is_valid()
   ---------------------------------------------------------------------
   Role :
   ---------------------------------------------------------------------
   E :
   S : 1=OK 0=KO
   --------------------------------------------------------------------- */
static int is_valid (sDATE const *const p_date, int *const p_last_day)
{
   int ok = 0;
   uint month = p_date->month;

   if (month <= 12
       && month >= 1)
   {
      uint nod = Mon2nod[month];
      uint year = p_date->year;

      if (month == 2)           /* Fevrier */
      {
         if (is_leap (year))
         {
            nod = 29;
         }
      }

      if (p_date->day > 0
          && p_date->day <= nod)
      {
         if (p_last_day)
         {
            *p_last_day = nod;
         }

         if (p_date->hour < 24)
         {
            if (p_date->minute < 60)
            {
               if (p_date->second < 60)
               {
                  ok = 1;
               }
            }
         }
      }
   }
   return ok;
}

/* internal public data ================================================ */
/* internal public functions =========================================== */
/* entry points ======================================================== */

/* ---------------------------------------------------------------------
   DATE_sver()
   ---------------------------------------------------------------------
   Role : Retourne une chaine "Version"
   ---------------------------------------------------------------------
   E :
   S : Pointeur de chaine ASCIIZ
   --------------------------------------------------------------------- */
const char *DATE_sver (void)
{
   return VER;
}

/* ---------------------------------------------------------------------
   DATE_sid()
   ---------------------------------------------------------------------
   Role : Retourne une chaine "Identification"
   ---------------------------------------------------------------------
   E :
   S : Pointeur de chaine ASCIIZ
   --------------------------------------------------------------------- */
const char *DATE_sid (void)
{
   return ID;
}

/* ---------------------------------------------------------------------
   DATE_annee4()
   ---------------------------------------------------------------------
   Role : Conversion d'une date modulo 100 en date 4 digit
   ---------------------------------------------------------------------
   E : date charniere
   E : date sur 2 chiffres (0-99)
   S : annee sur 4 chiffres
   --------------------------------------------------------------------- */
int DATE_annee4 (int const yy, int const y2)
{
   int y4;

   ENUM_CHECK ();

   if (y2 < (yy % 100))
   {
      y4 = y2 + ((yy / 100) + 1) * 100;
   }
   else
   {
      y4 = y2 + (yy / 100) * 100;
   }
   return y4;
}

/* ---------------------------------------------------------------------
   DATE_jour_max()
   ---------------------------------------------------------------------
   Role : Retourne le jour max en fonction de la date courante
   ---------------------------------------------------------------------
   E : structure sDATE
   S : jour 28/29/30/31
   --------------------------------------------------------------------- */
int DATE_jour_max (sDATE * pDate)
{
   int nod = 0;

   ENUM_CHECK ();

   is_valid (pDate, &nod);

   return nod;
}

/* ---------------------------------------------------------------------
   tm2DATE()
   ---------------------------------------------------------------------
   Role : Conversion d'une date standard C en sDATE
   ---------------------------------------------------------------------
   E : adresse de sortie (sDATE)
   E : adresse de la date d'enytreee (struct tm)
   S : 0=ok 1=err
   --------------------------------------------------------------------- */
int tm2DATE (sDATE * const p_date, struct tm const *const p_tm)
{
   int err = 0;

   if (p_date && p_tm)
   {
      p_date->year = p_tm->tm_year + 1900;
      p_date->month = p_tm->tm_mon + 1;
      p_date->day = p_tm->tm_mday;

      p_date->hour = p_tm->tm_hour;
      p_date->minute = p_tm->tm_min;
      p_date->second = p_tm->tm_sec;

      err = !is_valid (p_date, NULL);
   }
   return err;
}

/* ---------------------------------------------------------------------
   DATE2tm()
   ---------------------------------------------------------------------
   Role : Conversion d'une date sDATE en standard C
   ---------------------------------------------------------------------
   E : adresse de sortie (struct tm)
   E : adresse de la date d'entree (sDATE)
   S : 0=ok 1=err
   --------------------------------------------------------------------- */
int DATE2tm (struct tm *const p_tm, sDATE const *const p_date)
{
   int err = 0;

   if (p_date && p_tm)
   {
      err = !is_valid (p_date, NULL);

      if (!err)
      {
         p_tm->tm_year = p_date->year - 1900;
         p_tm->tm_mon = p_date->month - 1;
         p_tm->tm_mday = p_date->day;

         p_tm->tm_hour = p_date->hour;
         p_tm->tm_min = p_date->minute;
         p_tm->tm_sec = p_date->second;

         p_tm->tm_isdst = 0;

         mktime (p_tm);
      }
   }
   return err;
}

/* ---------------------------------------------------------------------
   DATE_is_valid()
   ---------------------------------------------------------------------
   Role : tester la validite d'une date
   ---------------------------------------------------------------------
   E : structure date (sDATE)
   S : 1=ok 0=err
   --------------------------------------------------------------------- */
int DATE_is_valid (sDATE const *const pDate)
{
   int ok;

   ENUM_CHECK ();

   if (pDate != NULL)
   {
      ok = is_valid (pDate, NULL);
   }
   else
   {
      ok = 0;
   }

   return ok;
}

/* ---------------------------------------------------------------------
   DATE_get()
   ---------------------------------------------------------------------
   Lecture de la date courante dans une sDATE.
   L'epoch standard (time_t) est retournee
   ---------------------------------------------------------------------
   E : adresse de la structure sDATE
   S : valeur de l'epoch (time_t)
   --------------------------------------------------------------------- */
time_t DATE_get (sDATE * p_date)
{
   time_t now = time (NULL);
   struct tm tm_now = *localtime (&now);

   tm2DATE (p_date, &tm_now);

   return now;
}

#ifdef DATE_PRINT
/* ----------------------------------------------------------------------
   DATE_print()
   ----------------------------------------------------------------------

   ----------------------------------------------------------------------
   E: structure interne de date
   S:
   ---------------------------------------------------------------------- */
void DATE_print (sDATE const *const p_date)
{
   static char const *const as_day[] =
   {
      "Sun",
      "Mon",
      "Tue",
      "Wed",
      "Thu",
      "Fri",
      "Sat",
   };
   struct tm tm_date;

   int ok = !DATE2tm (&tm_date, p_date);

   printf ("%s " DATE_FMT_FULL EOL
           ,ok ? as_day[tm_date.tm_wday] : "###"
           ,p_date->year
           ,p_date->month
           ,p_date->day
           ,p_date->hour
           ,p_date->minute
           ,p_date->second
      );
}
#endif

#ifdef DATE_TM
/* ---------------------------------------------------------------------
   DATE_tm_limites()
   ---------------------------------------------------------------------
   OBSOLETE
   Role : tester le limites d'une date
   ---------------------------------------------------------------------
   E : structure standard date (tm)
   S : 0=OK -1=KO
   --------------------------------------------------------------------- */
int DATE_tm_limites (struct tm const *const p_tm)
{
   int cr = -1;

   ENUM_CHECK ();

   if ((p_tm->tm_sec >= 0)
       && (p_tm->tm_sec < 60)
      )
   {
      if ((p_tm->tm_min >= 0)
          && (p_tm->tm_min < 60)
         )
      {
         if ((p_tm->tm_hour >= 0)
             && (p_tm->tm_hour < 24)
            )
         {
            if ((p_tm->tm_mday > 0)
                && (p_tm->tm_mday <= 31)
               )
            {
               if ((p_tm->tm_mon > 0)
                   && (p_tm->tm_mon <= 12)
                  )
               {
                  if ((p_tm->tm_year >= 0)
                      && (p_tm->tm_year < 100)
                     )
                  {
                     cr = 0;
                  }
               }
            }
         }
      }
   }

   return cr;
}

/* ---------------------------------------------------------------------
   DATE_tm_valide()
   ---------------------------------------------------------------------
   OBSOLETE
   Role : tester la validite d'une date
   ---------------------------------------------------------------------
   E : structure standard date (tm)
   S : 0=OK -1=KO
   --------------------------------------------------------------------- */
int DATE_tm_valide (struct tm const *const p_tm)
{
   int cr = -1;

   ENUM_CHECK ();

   if (p_tm != NULL)
   {
      cr = tm_is_valid (p_tm, NULL);
   }

   return cr;
}

/* ---------------------------------------------------------------------
   DATE_tm_jour_max()
   ---------------------------------------------------------------------
   OBSOLETE
   Role : Retourne le jour max en fonction de la date courante
   ---------------------------------------------------------------------
   E : structure standard date (tm)
   S : jour 28/29/30/31
   --------------------------------------------------------------------- */
int DATE_tm_jour_max (struct tm const *const p_tm)
{
   int nod = 0;

   ENUM_CHECK ();

   tm_is_valid (p_tm, &nod);

   return nod;
}

#if DATETIME2tm
/* ---------------------------------------------------------------------
   DATE_Date2tm()
   ---------------------------------------------------------------------
   OBSOLETE (tant que cette fonction utilise des DATE_tm*())

   Role : Convertir une date au format __DATE__ au format tm
   (les autres champs ne sont pas modifies)
   ---------------------------------------------------------------------
   E : chaine sur __DATE__
   E : structure standard date (tm)
   S : 0=OK -1=KO
   --------------------------------------------------------------------- */
int DATE_Date2tm (char const *s_date, struct tm *const p_tm)
{
   int cr = -1;
   struct tm date =
   {0};
   static char const *const as_months[] =
   {
      "Jan",
      "Feb",
      "Mar",
      "Apr",
      "May",
      "Jun",
      "Jul",
      "Aug",
      "Sep",
      "Oct",
      "Nov",
      "Dec",
   };

   ENUM_CHECK ();

   if (s_date
       && (strlen (s_date) == sizeof szDATE - 1)
      )
   {
      char *const sdup = STR_dup (s_date);
      char *sbeg = sdup;
      char *send = strstr (sbeg, SEP_D);

      if (send)
      {
         *send = 0;
         {
            int n = STR_FindSz (as_months, NELEM (as_months), sbeg);

            if (n != -1)
            {
               date.tm_mon = n + 1;
               sbeg = send + strlen (SEP_D);

               send = strstr (sbeg, SEP_D);

               if (send)
               {
                  *send = 0;

                  if (strlen (sbeg))
                  {
                     long l = strtol (sbeg, &send, 10);

                     if ((send != NULL)
                         && (*send == 0)
                        )
                     {
                        date.tm_mday = (int) l;
                        sbeg = send + strlen (SEP_D);

                        l = strtol (sbeg, &send, 10);

                        if ((send != NULL)
                            && (*send == 0)
                           )
                        {
                           date.tm_year = (int) l % 100;

                           cr = DATE_tm_limites (&date);

                           if (cr == 0)
                           {
                              date.tm_year = (int) l;

                              cr = tm_is_valid (&date, NULL);
                           }
                        }

                     }
                  }
               }
            }
         }
      }
      free (sdup);
   }

   if ((cr == 0)
       && (p_tm))
   {
      p_tm->tm_mday = date.tm_mday;
      p_tm->tm_mon = date.tm_mon;
      p_tm->tm_year = date.tm_year;
   }

   return cr;
}

/* ---------------------------------------------------------------------
   DATE_Time2tm()
   ---------------------------------------------------------------------
   OBSOLETE (tant que cette fonction utilise des DATE_tm*())

   Role : Convertir une heure au format __TIME__ au format tm
   (les autres champs ne sont pas modifies)
   ---------------------------------------------------------------------
   E : chaine sur __TIME__
   E : structure standard date (tm)
   S : 0=OK -1=KO
   --------------------------------------------------------------------- */
int DATE_Time2tm (char const *s_time, struct tm *const p_tm)
{
   int cr = -1;
   struct tm date =
   {0};

   ENUM_CHECK ();

   if ((s_time)
       && (strlen (s_time) == sizeof szTIME - 1)
      )
   {
      char *const sdup = STR_dup (s_time);
      char *sbeg = sdup;
      char *send = strstr (sbeg, SEP_H);

      if (send)
      {
         *send = 0;
         {
            long l = strtol (sbeg, &send, 10);

            if ((send != NULL)
                && (*send == 0)
               )
            {
               date.tm_hour = (int) l;
               sbeg = send + strlen (SEP_H);

               send = strstr (sbeg, SEP_H);

               if (send)
               {
                  *send = 0;
                  {
                     long l = strtol (sbeg, &send, 10);

                     if ((send != NULL)
                         && (*send == 0)
                        )
                     {
                        date.tm_min = (int) l;
                        sbeg = send + strlen (SEP_H);

                        {
                           long l = strtol (sbeg, &send, 10);

                           if ((send != NULL)
                               && (*send == 0)
                              )
                           {
                              date.tm_sec = (int) l;

                           }
                        }
                     }
                  }
               }
            }
         }
      }

      free (sdup);
   }

   if (p_tm)
   {
      p_tm->tm_hour = date.tm_hour;
      p_tm->tm_min = date.tm_min;
      p_tm->tm_sec = date.tm_sec;
   }

   return cr;
}
#endif /* DATETIME2tm */

#endif /* DATE_TM */

/* public data ========================================================= */

/* File generated by 'NEW.EXE' Ver 1.20 (c) ED 1998-99 */
