/***************************************************************************
 *                                                                         *
 *   DNTAPI.C                                                              *
 *                                                                         *
 *   Copyright (C) 1988-1997 Galacticomm, Inc.      All Rights Reserved.   *
 *                                                                         *
 *   Gregorian date and time utilities.                                    *
 *                                                                         *
 *   Dates are represented in eight ways:                                  *
 *        Discrete components:  the year, month, and day are each separate *
 *                              integers (range 1-9999)                    *
 *        Packed 16-bit format:  YYYYYYYMMMMDDDDD (range 1980-2107)        *
 *        Count of days since 1/1/1980 (range 1980-2099)                   *
 *        ASCII:  "MM/DD/YY" as in "01/09/89" (range 1980-2079)            *
 *        ASCII long:  "MM/DD/YYYY" as in "01/09/1989" (range 1980-2107)   *
 *        ASCII European:  "DD-MMM-YY" as in "09-JAN-89" (range 1980-2107) *
 *        ASCII European long:  "DD-MMM-YYYY" as in "09-JAN-1989"          *
 *                              (range 1980-2107)                          *
 *        Sort format: "YYYYMMDD" as in "19890109" (range 15821015-9999)   *
 *                                                                         *
 *        today() returns current day (from DOS) in Packed form            *
 *        daytoday() returns current day of the week (0=Sun, 1=Mon, etc.)  *
 *        ncdate() converts Packed to ASCII                                *
 *        ncdatel() converts Packed to ASCII long                          *
 *        ncedat() converts Packed to ASCII European                       *
 *        ncedatl() converts Packed to ASCII European long                 *
 *        dcdate() converts ASCII or ASCII long to Packed                  *
 *                          (year defaults to this year)                   *
 *        cofdat() converts Packed to Count                                *
 *        datofc() converts Count to Packed                                *
 *        ddyear() converts Packed to discrete year                        *
 *        ddmon() converts Packed to discrete month                        *
 *        ddday() converts Packed to discrete day                          *
 *        dddate() converts discrete components to Packed                  *
 *        sDateEncode() converts discrete components to Sort               *
 *        sDateEncodeDOS() converts Packed to Sort                         *
 *        sDateDecode() converts Sort to discrete components               *
 *        sDateDecodeDOS() converts Sort to Packed                         *
 *                                                                         *
 *   Time-of-day is represented in four ways:                              *
 *        Discrete components:  hour, minute, and second are each separate *
 *                              integers                                   *
 *        Packed 16-bit format:  HHHHHMMMMMMSSSSS[S] (resolution 2 sec)    *
 *        ASCII:  "HH:MM:SS" as in "23:59:58" (2 seconds before midnite)   *
 *        Sort:  "HHMMSS" as in "235957" (3 seconds before midnite)        *
 *                                                                         *
 *        now() returns the current time (from DOS) in Packed form         *
 *        nctime() converts Packed to ASCII                                *
 *        dctime() converts ASCII to Packed (seconds default to 00)        *
 *        dthour() converts Packed to discrete hour                        *
 *        dtmin() converts Packed to discrete minute                       *
 *        dtsec() converts Packed to discrete seconds                      *
 *        dttime() converts discrete components to Packed                  *
 *        sTimeEncode() converts discrete components to Sort               *
 *        sTimeEncodeDOS() converts Packed to Sort                         *
 *        sTimeDecode() converts Sort to discrete components               *
 *        sTimeDecodeDOS() converts Sort to Packed                         *
 *                                                                         *
 *                                                                         *
 *                            - RNStein  B03F  2952  01/31/88  31 JAN 88   *
 *   Combined into one file   - Craig Yap 01/16/96                         *
 *   Sort format, Y2K fixes   - J. Alvrus 19971007                         *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#if defined(GCWINNT) && !defined(GCMVC)
#include "windows.h"
#endif // GCWINNT && !GCMVC

/* don't know why, but VC++ gives redefinition warning on FILREV */
#undef FILREV
#define FILREV "$Revision: 9 $"

CHAR moname[16][4]={"000","JAN","FEB","MAR",
                          "APR","MAY","JUN",
                          "JUL","AUG","SEP",
                          "OCT","NOV","DEC","XXX","XXX","XXX"};
CHAR strMonths[12][10]={
     "January","February","March","April","May","June",
     "July","August","September","October","November","December"
};
CHAR strDays[7][10]={
     "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"
};

USHORT                             /*   returns current day in             */
today(VOID)                        /*   YYYYYYYMMMMDDDDD format            */
{
#if defined(GCWINNT) && !defined(GCMVC)
     SYSTEMTIME st;
     INT ecode;

     ecode=WSAGetLastError();
     GetLocalTime(&st);
     WSASetLastError(ecode);
     return((USHORT)(dddate(st.wMonth,st.wDay,st.wYear)));
#else
     struct tm *tim;
     time_t t;

     t=time(NULL);
     tim=localtime(&t);
     return((USHORT)(dddate((tim->tm_mon+1),tim->tm_mday,(tim->tm_year+1900))));
#endif // GCWINNT && !GCMVC
}

USHORT                             /*   returns current day of week. 0=Sun */
daytoday(VOID)                     /* gets current day of the week         */
{
#if defined(GCWINNT) && !defined(GCMVC)
     SYSTEMTIME st;
     INT ecode;

     ecode=WSAGetLastError();
     GetLocalTime(&st);
     WSASetLastError(ecode);
     return((USHORT)st.wDayOfWeek);
#else
     struct tm *tme;
     time_t t;

     t=time(NULL);
     tme=localtime(&t);
     return((USHORT)tme->tm_wday);
#endif // GCWINNT && !GCMVC
}

USHORT                             /*   returns current time in            */
now(VOID)                          /* HHHHHMMMMMMSSSSS[S] format           */
{
#if defined(GCWINNT) && !defined(GCMVC)
     SYSTEMTIME st;
     INT ecode;

     ecode=WSAGetLastError();
     GetLocalTime(&st);
     WSASetLastError(ecode);
     return((USHORT)(dttime(st.wHour,st.wMinute,st.wSecond)));
#else
     struct tm *tme;
     time_t t;
     SHORT hour,min,sec;

     t=time(NULL);
     tme=localtime(&t);
     hour=tme->tm_hour;
     min=tme->tm_min;
     sec=tme->tm_sec;
     return((USHORT)(dttime(hour,min,sec)));
#endif // GCWINNT && !GCMVC
}

const CHAR *                       /*   returns ascii date string MM/DD/YY */
ncdate(                            /* encodes date into "MM/DD/YY"         */
USHORT date)                       /*   packed date                        */
{
     static CHAR buff[sizeof("MM/DD/YY")];

     return(prnDate(ddyear(date),ddmon(date),ddday(date),
                    buff,sizeof(buff),PRND_MDY,'/'));
}

const CHAR *                       /*   returns ASCII long date string     */
ncdatel(                           /* encodes date into "MM/DD/YYYY"       */
USHORT date)                       /*   packed date                        */
{
     static CHAR buff[sizeof("MM/DD/YYYY")];

     return(prnDate(ddyear(date),ddmon(date),ddday(date),
                    buff,sizeof(buff),PRND_MDYY,'/'));
}

const CHAR *                       /*   returns ascii time string HH:MM:SS */
nctime(                            /* encodes time into "HH:MM:SS"         */
USHORT time)                       /*   packed time                        */
{
     static CHAR buff[sizeof("HH:MM:SS")];

     return(prnTime(dthour(time),dtmin(time),dtsec(time),
                    buff,sizeof(buff),PRNT_HMS+PRNT_PAD));
}

const CHAR *                       /*   rets date string in european format*/
ncedat(                            /* encodes date to DD-MMM-YY            */
USHORT date)                       /*   packed date                        */
{
     static CHAR buff[sizeof("DD-MMM-YY")];

     return(prnDate(ddyear(date),ddmon(date),ddday(date),
                    buff,sizeof(buff),PRND_DMMCY,'-'));
}

const CHAR *                       /*   date string in European long format*/
ncedatl(                           /* encodes date to DD-MMM-YYYY          */
USHORT date)                       /*   packed date                        */
{
     static CHAR buff[sizeof("DD-MMM-YYYY")];

     return(prnDate(ddyear(date),ddmon(date),ddday(date),
                    buff,sizeof(buff),PRND_DMMCYY,'-'));
}

const CHAR *                       /*   returns  MMM DD HH:MM              */
ncudnt(                            /* encodes Unix style date/time         */
USHORT date,                       /*   packed date                        */
USHORT time)                       /*   packed time                        */
{
     static CHAR buff[13];
     USHORT y;

     strcpy(buff,moname[ddmon(date)]);
     strlwr(buff+1);
     sprintf(buff+3," %2d %-5.5s",ddday(date),nctime(time));
     if ((y=ddyear(date)) != ddyear(today())) {
          sprintf(buff+7," %d",y);
     }
     return(buff);
}

#define AABBCC 3                   /* sscanf() return for MM/DD/YYYY       */
#define AABB   2                   /* sscanf() return for MM/DD            */

VOID
dateDecode(                        /* decode date from "MM/DD[/YY[YY]]"    */
const CHAR *src,                   /*   date string                        */
INT *pYear,                        /*   buffer for year (-1 if bad)        */
INT *pMonth,                       /*   buffer for month (-1 if bad)       */
INT *pDay)                         /*   buffer for day (-1 if bad)         */
{
     INT n,y,m,d;

     y=m=d=-1;
     if ((n=sscanf(src,"%d/%d/%d",&m,&d,&y)) == AABBCC
      || (n=sscanf(src,"%d/%d",&m,&d)) == AABB) {
          if (n == AABB) {
               y=ddyear(today());
          }
          else if (y < 1 || 9999 < y) {
               y=-1;
          }
          else if (y < 100) {
               y+=(y < 80 ? 2000 : 1900);
          }
          if (m < 1 || 12 < m) {
               m=-1;
          }
          if (y < 0 || m < 0 || d < 1 || lastDayOfMonth(m,y) < d) {
               d=-1;
          }
     }
     *pYear=y;
     *pMonth=m;
     *pDay=d;
}

VOID
timeDecode(                        /* decode time from  "HH:MM[:SS]"       */
const CHAR *src,                   /*   encoded date string                */
INT *pHour,                        /*   buffer for hour (-1 if bad)        */
INT *pMinute,                      /*   buffer for minute (-1 if bad)      */
INT *pSecond)                      /*   buffer for second (-1 if bad)      */
{
     INT n,h,m,s;

     h=m=s=-1;
     if ((n=sscanf(src,"%d:%d:%d",&h,&m,&s)) == AABBCC
      || (n=sscanf(src,"%d:%d",&h,&m)) == AABB) {
          if (h < 0 || 23 < h) {
               h=-1;
          }
          if (m < 0 || 59 < m) {
               m=-1;
          }
          if (n == AABB) {
               s=0;
          }
          else if (s < 0 || 59 < s) {
               s=-1;
          }
     }
     *pHour=h;
     *pMinute=m;
     *pSecond=s;
}

USHORT                             /*   returns GCINVALIDDOT if invalid    */
dcdate(                            /* decode DOS date from "MM/DD[/YY[YY]]"*/
const CHAR *datstr)                /*   date string to convert             */
{
     INT y,m,d;

     dateDecode(datstr,&y,&m,&d);
     if (validDate(y,m,d)) {
          return((USHORT)(dddate(m,d,y)));
     }
     return(GCINVALIDDOT);
}

USHORT                             /*   returns GCINVALIDDOT if invalid    */
dctime(                            /* decode DOS time from "HH:MM[:SS]"    */
const CHAR *timstr)                /*   time string to convert             */
{
     INT h,m,s;

     timeDecode(timstr,&h,&m,&s);
     if (validTime(h,m,s)) {
          return((USHORT)(dttime(h,m,s)));
     }
     return(GCINVALIDDOT);
}

static INT
numPeriod(                         /* get # of periods in a range          */
INT begRange,                      /*   beginning of range                 */
INT endRange,                      /*   end of range                       */
INT period)                        /*   size of period                     */
{                                  /* (e.g. 1981-92=2, 1984 & 1988, !1992) */
     INT first,last;

     ASSERT(begRange <= endRange);
     first=((begRange+(period-1))/period)*period;
     last=((endRange-1)/period)*period;
     if (first <= last) {
          return(((last-first)/period)+1);
     }
     return(0);
}

LONG                               /*   < 0 if end year before beg year    */
difYear(                           /* get # days between 1JAN of two years */
INT begYear,                       /*   beginning year                     */
INT endYear)                       /*   ending year                        */
{
     LONG nDays;
     INT dif,tmp,nQuadYears,nCenturies,nQuadCents;
     GBOOL negFlag;

     ASSERT(0 <= begYear && begYear <= 9999);
     ASSERT(0 <= endYear && endYear <= 9999);
     if ((dif=endYear-begYear) == 0) {
          return(0L);
     }
     negFlag=(dif < 0);
     if (negFlag) {
          dif=-dif;
          tmp=begYear;
          begYear=endYear;
          endYear=tmp;
     }
     nQuadYears=numPeriod(begYear,endYear,4);
     nCenturies=numPeriod(begYear,endYear,100);
     nQuadCents=numPeriod(begYear,endYear,400);
     nDays=((((LONG)dif*365L)+nQuadYears)-nCenturies)+nQuadCents;
     return(negFlag ? -nDays : nDays);
}

LONG                               /*   < 0 if end date before beg date    */
difDate(                           /* compute # days between dates         */
INT begYear,                       /*   year component of first date       */
INT begMonth,                      /*   month component of first date      */
INT begDay,                        /*   day component of first date        */
INT endYear,                       /*   year component of second date      */
INT endMonth,                      /*   month component of second date     */
INT endDay)                        /*   day component of second date       */
{
     LONG nDays;
     INT tmp;
     GBOOL negFlag;
     static INT daymon[]={0,31,59,90,120,151,181,212,243,273,304,334};

     ASSERT(0 <= begYear && begYear <= 9999);
     ASSERT(1 <= begMonth && begMonth <= 12);
     ASSERT(1 <= begDay && begDay <= lastDayOfMonth(begMonth,begYear));
     ASSERT(0 <= endYear && endYear <= 9999);
     ASSERT(1 <= endMonth && endMonth <= 12);
     ASSERT(1 <= endDay && endDay <= lastDayOfMonth(endMonth,endYear));
     negFlag=(begYear > endYear
           || (begYear == endYear
            && (begMonth > endMonth
             || (begMonth == endMonth && begDay > endDay))));
     if (negFlag) {
          tmp=endYear;
          endYear=begYear;
          begYear=tmp;
          tmp=endMonth;
          endMonth=begMonth;
          begMonth=tmp;
          tmp=endDay;
          endDay=begDay;
          begDay=tmp;
     }
     nDays=difYear(begYear,endYear);
     nDays+=daymon[endMonth-1];
     if (isLeapYear(endYear) && endMonth > 2) {
          ++nDays;
     }
     nDays+=endDay-1;
     nDays-=daymon[begMonth-1];
     if (isLeapYear(begYear) && begMonth > 2) {
          --nDays;
     }
     nDays-=begDay-1;
     return(negFlag ? -nDays : nDays);
}

VOID
addDaysToDate(                     /* get date by adding # days to base    */
LONG nDays,                        /*   # days to add (can be negative)    */
INT *pYear,                        /*   base year (updated w/ new year)    */
INT *pMonth,                       /*   base month (updated w/ new month)  */
INT *pDay)                         /*   base day (updated w/ new day)      */
{
     INT year,month,day,count;

     /* handle trivial case */
     if (nDays == 0) {
          return;
     }

     year=*pYear;
     month=*pMonth;
     day=*pDay;
     ASSERT(1 <= year && year <= 9999);
     ASSERT(1 <= month && month <= 12);
     ASSERT(1 <= day && day <= lastDayOfMonth(month,year));

     /* compute target date as days since 1/1/1 */
     nDays=difDate(1,1,1,year,month,day)+nDays;
     ASSERT(nDays >= 0);

     /* now convert back to year/month/day */
     year=month=1;
     /* Estimate the number of whole years represented, and subtract  *
      * the actual number of days represented by the estimated years. *
      * The estimated number of years will always be <= the actual    *
      * number, but this still saves iterations.                      */
     if (nDays >= 366) {
          year=(INT)(nDays/366);
          nDays-=difYear(1,year);
     }
     /* count off any remaining whole years */
     while (nDays >= (count=isLeapYear(year) ? 366 : 365)) {
          nDays-=count;
          ++year;
     }
     /* count off any remaining whole months */
     while (nDays >= (count=lastDayOfMonth(month,year))) {
          nDays-=count;
          ++month;
          ASSERT(month <= 12);
     }
     day=(INT)nDays+1;
     ASSERT(validDate(year,month,day));
     *pYear=year;
     *pMonth=month;
     *pDay=day;
}

USHORT                             /*   returns number of days since 1/1/80*/
cofdat(                            /* Compute number of days since 1/1/80  */
USHORT date)                       /*   date in DOS format:YYYYYYYMMMMDDDDD*/
{
     return((USHORT)difDate(1980,1,1,ddyear(date),ddmon(date),ddday(date)));
}

USHORT                             /*   returns packed date                */
datofc(                            /* Compute date form YYYYYYYMMMMDDDDD   */
USHORT count)                      /*   count of days since 1/1/80         */
{
     INT year,month,day;

     year=1980;
     month=1;
     day=1;
     addDaysToDate((LONG)count,&year,&month,&day);
     return((USHORT)dddate(month,day,year));
}

CHAR *                             /*   returns pointer to buffer          */
prnDate(                           /* form formatted date string           */
INT year,                          /*   year component (1-9999)            */
INT month,                         /*   month component (1-12)             */
INT day,                           /*   day component (1-31)               */
CHAR *buf,                         /*   buffer to receive formatted date   */
size_t bufSiz,                     /*   size of buffer                     */
INT mode,                          /*   format mode                        */
CHAR sep)                          /*   separator character                */
{
     CHAR dateBuf[sizeof("September 31, 1999")];

     if (!validDate(year,month,day)) {
          *buf='\0';
          return(buf);
     }
     if (mode <= 9) {
          year%=100;
     }
     switch (mode%12) {
     case 0:
     case 1:
          sprintf(dateBuf,"%02d%c%02d",month,sep,day);
          break;
     case 2:
     case 3:
          sprintf(dateBuf,"%02d%c%02d",month,sep,year);
          break;
     case 4:
     case 5:
          sprintf(dateBuf,"%02d%c%02d%c%02d",month,sep,day,sep,year);
          break;
     case 6:
     case 7:
          sprintf(dateBuf,"%02d%c%3.3s%c%02d",
                  day,sep,strMonths[month-1],sep,year);
          break;
     case 8:
     case 9:
          sprintf(dateBuf,"%3.3s %d, %02d",strMonths[month-1],day,year);
          break;
     case 10:
     case 11:
          sprintf(dateBuf,"%s %d, %02d",strMonths[month-1],day,year);
          break;
     }
     if (mode&1) {
          strupr(dateBuf);
     }
     return(stlcpy(buf,dateBuf,bufSiz));
}

CHAR *                             /*   returns pointer to buffer          */
prnTime(                           /* form formatted time string           */
INT hour,                          /*   hour component (0-23)              */
INT minute,                        /*   minute component (0-59)            */
INT second,                        /*   second component (0-59)            */
CHAR *buf,                         /*   buffer to receive formatted time   */
size_t bufSiz,                     /*   size of buffer                     */
INT mode)                          /*   format mode                        */
{
     GBOOL pad;
     CHAR timeBuf[sizeof("HH:MM:SS PM")],tmpval[sizeof(" PM")];

     if (!validTime(hour,minute,second)) {
          *buf='\0';
          return(buf);
     }
     tmpval[0]='\0';
     pad=(mode >= PRNT_PAD);
     mode%=PRNT_PAD;
     if (mode%5 != 0) {
          if (mode%5 >= 3) {
               strcpy(tmpval," ");
          }
          strcat(tmpval,((hour < 12) ? "a" : "p"));
          if (mode%5 == 2 || mode%5 == 4) {
               strcat(tmpval,"m");
          }
          if (hour != 0) {
               hour%=12;
          }
          if (hour == 0) {
               hour=12;
          }
     }
     if (mode >= 10) {
          sprintf(timeBuf,pad ? "%02d:%02d:%02d%s" : "%d:%02d:%02d%s",
                  hour,minute,second,tmpval);
     }
     else {
          sprintf(timeBuf,pad ? "%02d:%02d%s" : "%d:%02d%s",hour,minute,tmpval);
     }
     if (mode%10 >= 6) {
          strupr(timeBuf);
     }
     return(stlcpy(buf,timeBuf,bufSiz));
}

INT                                /*   day of week (0 = Sun, <0 = error)  */
dayFromDate(                       /* get day of week given date           */
INT year,                          /*   year component (1-9999)            */
INT month,                         /*   month component (1-12)             */
INT day)                           /*   day component (1-31)               */
{
     LONG dif;

     if (!validDate(year,month,day)) {
          return(-1);
     }
     dif=difDate(1980,1,6,year,month,day);
     dif%=7;
     if (dif < 0) {
          dif+=7;
     }
     return((INT)dif);
}

const CHAR *                       /*   returns string in requested format */
prntim(                            /* print <time> in format <mode>        */
INT mode,                          /*   format mode                        */
USHORT time)                       /*   time to format                     */
{
     static CHAR timret[sizeof("HH:MM:SS PM")];

     return(prnTime(dthour(time),dtmin(time),dtsec(time),
                    timret,sizeof(timret),mode));
}

const CHAR *                       /*   returns string in requested format */
prndat(                            /* print <date> in format <mode>        */
INT mode,                          /*   format mode                        */
USHORT date,                       /*   date to format                     */
CHAR sep)                          /*   separator character                */
{
     static CHAR datret[sizeof("September 31, 1999")];

     return(prnDate(ddyear(date),ddmon(date),ddday(date),
                    datret,sizeof(datret),mode,sep));
}

const CHAR *                       /*   returns string in day format       */
prnday(                            /* print <date> in day format           */
USHORT date,                       /*   date to format                     */
INT size)                          /*   size of buffer                     */
{
     INT daynum;
     static CHAR dayret[10];

     if ((daynum=dayFromDate(ddyear(date),ddmon(date),ddday(date))) < 0) {
          return("<INVALID DATE>");
     }
     return(stlcpy(dayret,strDays[daynum],(size+1)%10));
}

GBOOL
validDate(                         /* check for valid date                 */
INT year,                          /*   year component (1-9999)            */
INT month,                         /*   month component (1-12)             */
INT day)                           /*   day component (1-31)               */
{
     return((1 <= year && year <= 9999)
         && (1 <= month && month <= 12)
         && (1 <= day && day <= lastDayOfMonth(month,year)));
}

GBOOL
validTime(                         /* check for valid time                 */
INT hour,                          /*   hour component (0-23)              */
INT minute,                        /*   minute component (0-59)            */
INT second)                        /*   second component (0-59)            */
{
     return((0 <= hour && hour <= 23)
         && (0 <= minute && minute <= 59)
         && (0 <= second && second <= 59));
}

GBOOL
validDateDOS(                      /* check for valid DOS date             */
USHORT dosDate)                    /*   DOS packed date                    */
{
     return(validDate(ddyear(dosDate),ddmon(dosDate),ddday(dosDate)));
}

GBOOL
validTimeDOS(                      /* check for valid DOS time             */
USHORT dosTime)                    /*   DOS packed time                    */
{
     return(validTime(dthour(dosTime),dtmin(dosTime),dtsec(dosTime)));
}

INT                                /*   number of last day (e.g. Jan = 31) */
lastDayOfMonth(                    /* get highest day in given month/year  */
INT month,                         /*   number of month (1-12)             */
INT year)                          /*   year in question (1-9999)          */
{
     INT n;
     static INT dayPerMon[12]={31,28,31,30,31,30,31,31,30,31,30,31};

     ASSERT(1 <= year && year <= 9999);
     ASSERT(1 <= month && month <= 12);
     n=dayPerMon[month-1];
     if (month == 2 && isLeapYear(year)) {
          ++n;
     }
     return(n);
}

CHAR *                             /*   returns pointer to buffer          */
sDateEncode(                       /* encode date to "YYYYMMDD"            */
INT year,                          /*   year component (1-9999)            */
INT month,                         /*   month component (1-12)             */
INT day,                           /*   day component (1-31)               */
CHAR *buf,                         /*   buffer to receive encoded date     */
size_t bufSiz)                     /*   size of buffer                     */
{
     CHAR tmpBuf[sizeof("YYYYMMDD")];

     ASSERT(buf != NULL);
     ASSERT(1 <= year && year <= 9999);
     ASSERT(1 <= month && month <= 12);
     ASSERT(1 <= day && day <= 31);
     sprintf(tmpBuf,"%04.4d%02.2d%02.2d",year,month,day);
     return(stlcpy(buf,tmpBuf,bufSiz));
}

CHAR *                             /*   returns pointer to buffer          */
sTimeEncode(                       /* encode time to "HHMMSS"              */
INT hour,                          /*   hour component (0-23)              */
INT minute,                        /*   minute component (0-59)            */
INT second,                        /*   second component (0-59)            */
CHAR *buf,                         /*   buffer to receive encoded time     */
size_t bufSiz)                     /*   size of buffer                     */
{
     CHAR tmpBuf[sizeof("HHMMSS")];

     ASSERT(buf != NULL);
     ASSERT(0 <= hour && hour < 24);
     ASSERT(0 <= minute && minute < 60);
     ASSERT(0 <= second && second < 60);
     sprintf(tmpBuf,"%02.2d%02.2d%02.2d",hour,minute,second);
     return(stlcpy(buf,tmpBuf,bufSiz));
}

CHAR *                             /*   returns pointer to buffer          */
sDateEncodeDOS(                    /* encode DOS date to "YYYYMMDD"        */
USHORT dosDate,                    /*   DOS packed date                    */
CHAR *buf,                         /*   buffer to receive encoded date     */
size_t bufSiz)                     /*   size of buffer                     */
{
     return(sDateEncode((INT)ddyear(dosDate),(INT)ddmon(dosDate),
                        (INT)ddday(dosDate),buf,bufSiz));
}

CHAR *                             /*   returns pointer to buffer          */
sTimeEncodeDOS(                    /* encode DOS time to "HHMMSS"          */
USHORT dosTime,                    /*   DOS packed time                    */
CHAR *buf,                         /*   buffer to receive encoded time     */
size_t bufSiz)                     /*   size of buffer                     */
{
     return(sTimeEncode((INT)dthour(dosTime),(INT)dtmin(dosTime),
                        (INT)dtsec(dosTime),buf,bufSiz));
}

VOID
sDateDecode(                       /* decode "YYYYMMDD"                    */
const CHAR *src,                   /*   encoded date string                */
INT *pYear,                        /*   buffer for year (-1 if bad)        */
INT *pMonth,                       /*   buffer for month (-1 if bad)       */
INT *pDay)                         /*   buffer for day (-1 if bad)         */
{
     INT n,year,month,day;

     year=month=day=-1;
     n=sscanf(src,"%4d%2d%2d",&year,&month,&day);
     if (n >= 2 && (month < 1 || 12 < month)) {
          month=-1;
     }
     if (n >= 3
      && (year < 0 || month < 0
       || day < 1 || lastDayOfMonth(month,year) < day)) {
          day=-1;
     }
     *pYear=year;
     *pMonth=month;
     *pDay=day;
}

VOID
sTimeDecode(                       /* decode "HHMMSS"                      */
const CHAR *src,                   /*   encoded date string                */
INT *pHour,                        /*   buffer for hour (-1 if bad)        */
INT *pMinute,                      /*   buffer for minute (-1 if bad)      */
INT *pSecond)                      /*   buffer for second (-1 if bad)      */
{
     INT n,hour,minute,second;

     hour=minute=second=-1;
     n=sscanf(src,"%2d%2d%2d",&hour,&minute,&second);
     if (n >= 1 && (hour < 0 || 23 < hour)) {
          hour=-1;
     }
     if (n >= 1 && (minute < 0 || 59 < minute)) {
          minute=-1;
     }
     if (n >= 1 && (second < 0 || 59 < second)) {
          second=-1;
     }
     *pHour=hour;
     *pMinute=minute;
     *pSecond=second;
}

USHORT                             /*   DOS date or GCINVALIDDOT           */
sDateDecodeDOS(                    /* decode "YYYYMMDD" to DOS format      */
const CHAR *src)                   /*   encoded date string                */
{
     INT year,month,day;

     sDateDecode(src,&year,&month,&day);
     if (year < 1980 || month < 0 || day < 0) {
          return(GCINVALIDDOT);
     }
     return(dddate(month,day,year));
}

USHORT                             /*   DOS time or GCINVALIDDOT           */
sTimeDecodeDOS(                    /* decode "HHMMSS" to DOS format        */
const CHAR *src)                   /*   encoded time string                */
{
     INT hour,minute,second;

     sTimeDecode(src,&hour,&minute,&second);
     if (hour < 0 || minute < 0 || second < 0) {
          return(GCINVALIDDOT);
     }
     return(dttime(hour,minute,second));
}

VOID
sDecodeDT(                         /* decode "YYYYMMDD[ ]HHMMSS"           */
const CHAR *src,                   /*   encoded date string                */
INT *pYear,                        /*   buffer for year (-1 if bad)        */
INT *pMonth,                       /*   buffer for month (-1 if bad)       */
INT *pDay,                         /*   buffer for day (-1 if bad)         */
INT *pHour,                        /*   buffer for hour (-1 if bad)        */
INT *pMinute,                      /*   buffer for minute (-1 if bad)      */
INT *pSecond)                      /*   buffer for second (-1 if bad)      */
{
     const CHAR *cp;
     INT i;

     sDateDecode(src,pYear,pMonth,pDay);
     for (i=0,cp=skptwht(src) ; i < CSTRLEN("YYYYMMDD") && isdigit(*cp)
        ; ++i,++cp) {
     }
     cp=skptwht(cp);
     sTimeDecode(cp,pHour,pMinute,pSecond);
}

VOID
sDecodeDTDOS(                      /* decode "YYYYMMDD[ ]HHMMSS" to DOS fmt*/
const CHAR *src,                   /*   encoded date string                */
USHORT *pDate,                     /*   buf for date (GCINVALIDDOT if bad) */
USHORT *pTime)                     /*   buf for time (GCINVALIDDOT if bad) */
{
     INT year,month,day,hour,minute,second;

     sDecodeDT(src,&year,&month,&day,&hour,&minute,&second);
     if (year < 1980 || 2107 < year || month < 0 || day < 0) {
          *pDate=GCINVALIDDOT;
     }
     else {
          *pDate=dddate(month,day,year);
     }
     if (hour < 0 || minute < 0 || second < 0) {
          *pTime=GCINVALIDDOT;
     }
     else {
          *pTime=dttime(hour,minute,second);
     }
}
