## root/clib/datime.c

/* [<][>][^][v][top][bottom][index][help] */

## DEFINITIONS

This source file includes following definitions.

1. datime_month_name_2_num
2. datime_month_num_2_name
3. get_ftime
4. get_time
5. datime_hour12_2_hour24
6. datime_get_ampm
7. run_year
8. days_in_month
9. isleap
10. juldate
11. datdif

    /* Date/time functions
       Rick Smereka, Copyright (C) 2002-2004.

       This program is free software; you can redistribute it and/or modify
       it under the terms of the GNU General Public License as published by
       the Free Software Foundation; either version 2 of the License, or
       (at your option) any later version.

       This program is distributed in the hope that it will be useful,
       but WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       GNU General Public License for more details.

       You should have received a copy of the GNU General Public License
       along with this program; if not, get a copy via the Internet at
       http://gnu.org/copyleft/gpl.html or write to the Free Software
       Foundation, Inc., 59 Temple Place, Suite 330, Boston,
       MA 02111-1307 USA

       You can contact the author via email at rsmereka@future-lab.com

       Original Linux version. Moved function 'get_time' from 'parse.c'
       to this module. May/2002, Rick Smereka

       Ported to 32-bit Windows. Jun/2002, Rick Smereka

       Ported to DOS and QNX 4.x. Jul/2002, Rick Smereka

       Changed function 'get_time' to return a compact date/time
       string instead of using 'asctime'. Sep/2002, Rick Smereka

       Ported to Debian Linux. Oct/2002, Rick Smereka

       Added functions 'run_year' and 'get_ftime'.
       Ported to Debian Linux. Nov/2002, Rick Smereka

       Added functions 'days_in_month' and 'isleap'.
       Jan/2004, Rick Smereka

       Added functions 'juldate' and 'datdif'. Dec/2004,
       Rick Smereka */

    #include "stdhead.h"

    // julian base year

    #define DATIME_JUL_BASE 1954

    /* month short names */

    char *smonth[] = { "", "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
                       "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" };

    /* private function */

    static int datime_get_ampm(char *, char *);

    int datime_month_name_2_num(char *mname)
    {
       /* Convert a month name to a month number. Only the first three
          characters of the month name are compared so you may use the
          long or short month names. January is considered month one.
          Function returns the month number upon success, zero otherwise. */

       int len, i;

       if (mname == (char *)NULL)
          return(FALSE);

       len = strlen(mname);

       /* must have a minimum of three characters */

       if (len < 3)
          return(FALSE);

       for(i = 1; i <= 12; i++)
          if (!strnicmp(smonth[i], mname, 3))
             return(i);

       return(FALSE);
    }

    int datime_month_num_2_name(int mnum, char *mname)
    {
       /* Translate a month number (1=Jan) to the short month name.
          Function returns 'TRUE' upon success with the month name
          loaded into 'mname' (which must already be allocated to
          sufficient size by the caller), 'FALSE' otherwise. */

       if (mnum <= 0 || mnum > 12)
          return(FALSE);

       if (mname == (char *)NULL)
          return(FALSE);

       strcpy(mname, smonth[mnum]);
       return(TRUE);
    }

    void get_ftime(char *tbuf)
    {
       /* Format and return the current time string. Returned format is:

             yyyy/mm/dd hh:mm:ss

          Buffer 'tbuf' must already be allocated by the caller
          to a minimum size of 20 bytes. */

       char raw_time[15], year[5], month[3], day[3], hour[3];
       char minute[3], second[3];

       get_time(raw_time);
       strncpy(year, raw_time, 4);
       year[4] = EOS;
       strncpy(month, &raw_time[4], 2);
       month[2] = EOS;
       strncpy(day, &raw_time[6], 2);
       day[2] = EOS;
       strncpy(hour, &raw_time[8], 2);
       hour[2] = EOS;
       strncpy(minute, &raw_time[10], 2);
       minute[2] = EOS;
       strncpy(second, &raw_time[12], 2);
       second[2] = EOS;
       sprintf(tbuf, "%s/%s/%s %s:%s:%s", year, month, day, hour, minute, second);
    }

    void get_time(char *tbuf)
    {
       /* Format and return the current time string. Format of the time string
          is:

             yyyymmddhhmmss

          Buffer 'tbuf' must be allocated to at least 15 bytes by the caller. */

       time_t tod;
       struct tm *tmbuf;

       if (tbuf == (char *)NULL)
          return;

       tod = time(NULL);
       tmbuf = localtime(&tod);
       sprintf(tbuf, "%d%02d%02d%02d%02d%02d", tmbuf->tm_year + 1900,
               tmbuf->tm_mon + 1, tmbuf->tm_mday, tmbuf->tm_hour,
               tmbuf->tm_min, tmbuf->tm_sec);
    }

    int datime_hour12_2_hour24(char *intime, char *outime, int sep_flag)
    {
       /* Convert a time string expressed in 12 hour format
          to a time string expressed in 24 format. The input
          time string ('intime') may be in any of the following
          formats:

             hh[:mm][:ss]p, hh[:mm][:ss] p, hh[:mm][:ss]pm, hh[:mm][:ss] pm
             hh[:mm][:ss]a, hh[:mm][:ss] a, hh[:mm][:ss]am, hh[:mm][:ss] am
             hh[:mm][:ss]m, hh[:mm][:ss] m, hh[:mm][:ss]n, hh[:mm][:ss] n

          Where 'p' and 'pm' are the same, 'a' and 'am' are also the
          same. A trailing 'm' indicates midnight and a trailing
          'n' indicates noon. When a trailing 'm' or 'n' is used,
          the time is assumed to be '12:00' regardless of any
          other hour/minute value. Spaces may or may not be present
          after the time and before the 'am' or 'pm' designator. The
          colon separator is required in the input time string. The flag
          'sep_flag' is used to indicate whether the output time
          string should have a colon separator. The minute and seconds fields
          are completly optional. The output time string always contains
          two digits each for the hour, minute and optional seconds.
          The hour and minute are always present in the output string.
          Seconds are only present in the output string if present in
          the input string. Function returns 'TRUE' with the converted
          time loaded into 'outime' upon success. Function returns
          'FALSE' otherwise. */

       char *new_inp, wrd[10], ap;
       int hr, min, sec, is_sec, nwords, len, i;

       if (intime == (char *)NULL || !strlen(intime))
          return(FALSE);

       if (outime == (char *)NULL)
          return(FALSE);

       outime[0] = EOS;
       len = strlen(intime);
       hr = min = sec = is_sec = 0;
       ap = EOS;

       if ((new_inp = (char *)malloc(len + 1)) == (char *)NULL)
          return(FALSE);

       /* remove all spaces from the input time string */

       strip_space(intime, new_inp);
       nwords = ll_words(new_inp, ':');

       if (nwords > 3)
          {
          free(new_inp);
          return(FALSE);
          }

       /* take apart input time string */

       for(i = 1; i <= nwords; i++)
          {
          if (!ll_word(new_inp, wrd, i, ':'))
             {
             free(new_inp);
             return(FALSE);
             }

          /* on the last part of the input time string, get
             the 'am'/'pm' designator */

          if (i == nwords)
             if (!datime_get_ampm(wrd, &ap))
                {
                free(new_inp);
                return(FALSE);
                }

          switch(i)
             {
             case 1:
                /* hour */

                if (!qatoi(wrd, &hr))
                   {
                   free(new_inp);
                   return(FALSE);
                   }

                break;

             case 2:
                /* minute */

                if (!qatoi(wrd, &min))
                   {
                   free(new_inp);
                   return(FALSE);
                   }

                break;

             case 3:
                /* second */

                if (!qatoi(wrd, &sec))
                   {
                   free(new_inp);
                   return(FALSE);
                   }

                is_sec = TRUE;
                break;
             };
          }

       // printf("hr=%d,min=%d,sec=%d,is_sec=%d,ap=%c\n", hr, min, sec, is_sec, ap);
       free(new_inp);

       /* build 24hr time */
       /* first, adjust time */

       switch(ap)
          {
          case 'A':
             if (hr == 12)
                hr = 0;

             break;

          case 'P':
             /* adjust hour if necessary */

             if (hr != 12)
                hr += 12;

             break;

          case 'M':
             /* midnight is always hour zero, minute zero */

             hr = min = 0;
             break;

          case 'N':
             /* noon is always hour 12, minute zero */

             hr = 12;
             min = 0;
             break;
          };

       if (sep_flag)
          if (is_sec)
             sprintf(outime, "%02d:%02d:%02d", hr, min, sec);
          else
             sprintf(outime, "%02d:%02d", hr, min);
       else
          if (is_sec)
             sprintf(outime, "%02d%02d%02d", hr, min, sec);
          else
             sprintf(outime, "%02d%02d", hr, min);

       return(TRUE);
    }

    static int datime_get_ampm(char *wrd, char *ap)
    {
       /* Get the 'am', 'pm' designator in the time string 'wrd'.
          If found, the designator will be removed from 'wrd' and
          a single character will be loaded into 'ap'. Acceptable
          designators are 'a', 'am', 'p', 'pm', 'n' and 'm'.
          Function returns 'TRUE' upon success, 'FALSE' otherwise. */

       char ch;
       int len, i, pos;

       if (wrd == (char *)NULL || !strlen(wrd))
          return(FALSE);

       if (ap == (char *)NULL)
          return(FALSE);

       len = strlen(wrd);
       *ap = EOS;

       /* loop to identify the designator */

       for(i = 0, pos = 0; i < len; i++)
          {
          ch = toupper(wrd[i]);

          if (ch >= '0' && ch <= '9')
             continue;

          /* set position of first non-digit */

          if (!pos)
             pos = i;

          /* if it's a space, ignore */

          if (ch == ' ')
             continue;

          /* load first character of designator */

          *ap = ch;
          break;
          }

       /* error on no designator */

       if (*ap == EOS)
          return(FALSE);

       /* designator must be on of 'a', 'p', 'm' or 'n' */

       if (*ap != 'A' && *ap != 'P' && *ap != 'M' && *ap != 'N')
          {
          *ap = EOS;
          return(FALSE);
          }

       /* elimiate designator */

       wrd[pos] = EOS;
       return(TRUE);
    }

    int run_year(void)
    {
       /* Get the year of program run via 'get_time' function.
          Function returns year as a number upon success,
          zero otherwise. */

       char tbuf[20], year_char[5];
       int year;

       get_time(tbuf);
       strncpy(year_char, tbuf, 4);
       year_char[4] = EOS;

       if (!qatoi(year_char, &year))
          return(0);

       return(year);
    }

    int days_in_month(int year, int month)
    {
       /* Determine the number of days in a month taking into account
          leap years. Function returns the number of days in 'month'
          upon success, zero otherwise. */

       static int numdays[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
       int leap;

       if (month < 1 || month > 12)
          return(0);

       leap = isleap(year);

       if (month == 2 && leap)
          return(29);

       return(numdays[month]);
    }

    int isleap(int year)
    {
       /* Determine whether the 'year' is a leap year. Function returns
          'TRUE' if the 'year' is a leap year, 'FALSE' otherwise. */

       return((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
    }

    int juldate(char *dte, int *julout)
    {
       /* Convert a date in the form 'yyyymmdd' to a Julian
          value using 'DATIME_JUL_BASE' as the base year. Function
          returns 'TRUE' upon success with the julian date
          value loaded into 'julout' upon success. Function
          returns 'FALSE' otherwise. */

       char tmp[5];
       int d_year, d_mo, d_day, ret, i;

       if (dte == (char *)NULL || strlen(dte) != 8)
          return(FALSE);

       if (julout == (int *)NULL)
          return(FALSE);

       *julout = 0;

       strncpy(tmp, dte, 4);
       tmp[4] = EOS;

       if (!qatoi(tmp, &d_year))
          return(FALSE);

       strncpy(tmp, &dte[4], 2);
       tmp[2] = EOS;

       if (!qatoi(tmp, &d_mo))
          return(FALSE);

       strncpy(tmp, &dte[6], 2);
       tmp[2] = EOS;

       if (!qatoi(tmp, &d_day))
          return(FALSE);

       ret = d_day;

       // for every month preceding the current, add days

       for(i = 1; i < d_mo; i++)
          ret += days_in_month(d_year, i);

       // add days from preceding years

       for(i = DATIME_JUL_BASE; i < d_year; i++)
          if (isleap(i))
             ret += 366;
          else
             ret += 365;

       *julout = ret;
       return(TRUE);
    }

    int datdif(char *d1, char *d2, int *dif)
    {
       /* Find the number of days difference between two dates.
          'd1' is assumed to be the later of the two dates.
          Dates are expected in the form 'yyyymmdd'.
          Function returns 'TRUE' upon success with the number
          of days difference loaded into 'dif' upon success.
          Function returns 'FALSE' otherwise. */

       int jul1, jul2;

       if (d1 == (char *)NULL || d2 == (char *)NULL || dif == (int *)NULL)
          return(FALSE);

       if (!juldate(d1, &jul1))
          return(FALSE);

       if (!juldate(d2, &jul2))
          return(FALSE);

       *dif = jul1 - jul2;
       return(TRUE);
    }

/* [<][>][^][v][top][bottom][index][help] */