home *** CD-ROM | disk | FTP | other *** search
/ World of Shareware - Software Farm 2 / wosw_2.zip / wosw_2 / CPROG / WINDATE.ZIP / DATE.C < prev    next >
C/C++ Source or Header  |  1991-02-27  |  35KB  |  828 lines

  1. /* DATE.C - Mark Jones' date/calendar routines for Windows 3.0
  2.  
  3.     Copyright (c) 1990, 1991, Mark Jones
  4.     713 Lisa Ln.
  5.     Cedar Hill, TX 75104
  6.     (214) 291-0509
  7.         Compuserve 70511,706
  8. */
  9.  
  10. #define _WINDOWS
  11. #define _WINDLL
  12.  
  13. #include <windows.h>
  14. #include "Date.h"
  15.  
  16. #include <float.h>
  17. #include <math.h>
  18.  
  19. /*****************************************************************************
  20.  
  21.                            MODIFICATION LOG
  22.  
  23. Date     Version  Who      Change
  24. ----     -------  ---      --------------------------------------------------
  25. 3 OCT 90 1.00     Mark J.  Added DATE.ICO as a resource
  26.                            Finished DateObj() and tested
  27. 22 OCT   1.01     Mark J.  Add _WINDOWS and _WINDLL and recompiled with
  28.                            warning level 3 (W3), fixing certain conversion
  29.                            warnings
  30. 2/10/91                    Changed "<win.h>" reference to <windows.h>
  31.  
  32.  
  33. *****************************************************************************/
  34. /*****************************************************************************/
  35.  
  36.                        /* MISC. DEFINITIONS */
  37.  
  38. #define  SET_ERR(ErrCode)  ( ((((LONG) DATE_ERROR)<<16)&0xFFFF0000L) + ErrCode)
  39.  
  40.  
  41. /*****************************************************************************/
  42.  
  43.                          /* GLOBAL FIELDS */
  44.  
  45.     int MoDaTbl[12] = {                   /* days-in-month table */
  46.         31, 29, 31, 30, 31, 30,
  47.         31, 31, 30, 31, 30, 31 };
  48.  
  49.  
  50.  
  51. /*****************************************************************************/
  52. /*****************************************************************************/
  53.  
  54.             /* OBJECT-ORIENTED DATE.DLL FUNCTIONS */
  55.  
  56. /*****************************************************************************/
  57. /*****************************************************************************/
  58.  
  59.     /* DateObj() : INTERFACE WITH DATE.DLL AS A SINGLE OBJECT */
  60.  
  61. /* This function allows external access to the functionality of DATE.DLL via
  62.    a generic "object" interface.  "DateObj" is the name of the object, and this
  63.    function presents "DateObj" in a way that resembles the interface to a
  64.    true object.  Such an interface may be desirable in table-driven
  65.    applications, or in systems that recognize only primitive data types,
  66.    and have no ability to inspect a data structure.  DateObj() is also
  67.    applicable to third-party software tools that cannot efficiently access
  68.    data-structure elements, such as Toolbook and Plus, or simply in situations
  69.    where a simpler, object-oriented interface is desired.
  70.  
  71.    ----------
  72.    PROTOTYPE:
  73.    ----------
  74.  
  75.     LONG FAR PASCAL DateObj(HANDLE hDateObj, WORD uMsg, WORD uID, LONG lParam)
  76.  
  77.    ------
  78.    INPUT:
  79.    ------
  80.    The following input parameters are used in the interface with "DateObj" :
  81.  
  82.    Parameter   Possible values   Meaning/Usage
  83.    ---------   ---------------   ----------------------------------------------
  84.    HANDLE      hDateObj          This is the handle to an instance of the
  85.                                  "DateObj" object.  Each object-instance will
  86.                                  be created by the CREATE message.
  87.  
  88.    WORD uMsg   CREATE            Creates a new "DateObj" object - used only
  89.                                  when the ID value is DATE
  90.                DELETE            Deletes an existing "DateObj" object - used
  91.                                  only when the ID value is DATE
  92.                SET               Used to set a property or value in DateObj,
  93.                                  or to initialize the entire DateObj to NULLs
  94.                GET               Retrieves a propery or value from DateObj
  95.                CALCULATE         Forces DateObj to recalculate itself, based
  96.                                  on either a julian or a normal date; the
  97.                                  calculation method is determined by setting
  98.                                  lParam to either JULIAN or NORMAL.  When
  99.                                  CALCULATE is used, the value of uID doesn't
  100.                                  matter.
  101.  
  102.    WORD uID    DATE              A "DateObj" object - used only with CREATE, 
  103.                                   DELETE, and SET messages
  104.                MONTH             Month number, from 1 to 12
  105.                DAY               Day of month, from 1 to 31
  106.                YEAR              Year, from 1901 to 2100
  107.                JULIANDATE        Julian date value, from 1 to 72743
  108.                LEAPYEAR          Does this date fall within a leap year?
  109.                DAYOFWEEK         What is the day of the week (0 to 6)?
  110.                DAYOFYEAR         What is the day of the year (1 to 366)?
  111.                DAYSINMONTH       How many days are in the month (1 to 31)?
  112.  
  113.    LONG lParam  *                The lParam parameter is used only when the
  114.                                  uMsg has a value of SET.  lParam should 
  115.                                  contain the numeric value associated with 
  116.                                  the uID during a SET operation.  The following 
  117.                                  values are valid lParam values:
  118.  
  119.                                  uID         valid lParam value(s)
  120.                                  ---         ----------------------
  121.                                  DATE        0
  122.                                  MONTH       1 to 12
  123.                                  DAY         1 to 31
  124.                                  YEAR        1 to 2100                 
  125.                                  JULIANDATE  1 to 72743
  126.                                  LEAPYEAR    N/A (does not use SET)
  127.                                  DAYOFWEEK   N/A (does not use SET)
  128.                                  DAYOFYEAR   N/A (does not use SET)
  129.  
  130.    -------
  131.    OUTPUT:
  132.    -------
  133.    The "DateObj" function returns a LONG value which varies depending on the
  134.    input-message and the ID.  If an error occurred, the HIWORD of the
  135.    LONG return value will contain the value DATE_ERROR, and the LOWORD
  136.    will contain a mnemonic code explaining the cause of the error.  The
  137.    following table summarizes the meanings of the possible error values
  138.    in LOWORD:
  139.  
  140.       LOWORD error value   Meaning
  141.       ------------------   ----------------------------------------------
  142.       GENERAL_FAILURE      The operation cannot be performed because of
  143.                            an underlying system failure, such as a lack
  144.                            of memory, etc.
  145.       BAD_INPUT            A bad lParam value was passed to DateObj().
  146.       NULL_HANDLE          The hDateObj value had a NULL (0) value.
  147.       BAD_HANDLE           The hDateObj value was non-NULL, but it was not
  148.                            usable by DateObj().
  149.       BAD_ID               The uID value was not valid.  See the "INPUT"
  150.                            section above for a list of valid uID values.
  151.       WRONG_ID             The uID value was valid, but was not usable
  152.                            in the context of the call.  For example, since
  153.                            a CREATE message applies only to a DATE object,
  154.                            the following would produce an error:
  155.  
  156.                               lResult =
  157.                                  DateObj(hDateObj, CREATE, MONTH, lParam);
  158.  
  159.                            but the following would be valid:
  160.  
  161.                               lResult =
  162.                                  DateObj(hDateObj, CREATE, DATE, lParam);
  163.  
  164.       BAD_MESSAGE          An invalid value was passed on the uMsg parameter.
  165.                            Consult the "INPUT" section above for a list of
  166.                            valid uMsg values.
  167.       FATAL_ERROR          An unexplainable error occurred during a CALCULATE
  168.                            operation, and DateObj couldn't complete its
  169.                            processing.  This error points to a bug in DATE.DLL!
  170.  
  171.    More specific information is provided below explaining the possible error-
  172.    return values.
  173.  
  174.    If the HIWORD of the LONG return value is not equal to DATE_ERROR, the
  175.    LOWORD value represents the result of a normal procedure.
  176.    
  177.    The way to test for success after returning from a DateObj() call is with
  178.    statements similar to:
  179.  
  180.       lResult = DateObj(hDateObj, uMsg, uId, lParam);
  181.       if (HIWORD(lResult) != DATE_ERROR)
  182.          nResultNormal = LOWORD(lResult);
  183.       else
  184.          myErrorHandler(LOWORD(lResult));
  185.  
  186.    The following table details the possible DateObj() return values.
  187.  
  188.                      -----------------------------
  189.                      DateObj() return-values table
  190.                      -----------------------------
  191.  
  192.    uMsg     DateObj LONG return value
  193.    ----     ----------------------------------------------------------------
  194.    CREATE   HIWORD = DATE_ERROR: A Date object could not be created
  195.             LOWORD = GENERAL_FAILURE:
  196.                                  Memory could not be alloc'd
  197.                      WRONG_ID:   A uID != DATE was passed
  198.              -*-
  199.             HIWORD = 0: The instance of DateObj was created
  200.             LOWORD = A valid handle to the DateObj object
  201.  
  202.    DELETE   HIWORD = DATE_ERROR: The Date object could not be
  203.                                  deleted
  204.             LOWORD = WRONG_ID:   A uID != DATE was passed, or
  205.                                  the hDateObj parameter was NULL
  206.                      NULL_HANDLE:A value of NULL (0) was passed in hDateObj
  207.                      GENERAL_FAILURE:
  208.                                  DateObj memory cannot be freed!  The
  209.                                  hDateObj value might be garbage.
  210.              -*-
  211.             HIWORD = 0: The instance of DateObj was destroyed.
  212.             LOWORD = hDateObj (this handle is now meaningless)
  213.  
  214.    SET      HIWORD = DATE_ERROR: The element could not be set
  215.             LOWORD = BAD_INPUT:  A bad value was passed, which fell outside
  216.                                  the allowed range.
  217.                      NULL_HANDLE:The hDateObj handle was NULL (0).
  218.                      BAD_HANDLE: The hDateObj handle parameter was garbage.
  219.              -*-
  220.             HIWORD = 0: The item was initialized.  If uID == DATE, then
  221.                         the DateObj was set to all NULLs (0s).
  222.             LOWORD = hDateObj
  223.  
  224.    GET      HIWORD = DATE_ERROR: The element could not be retrieved
  225.             LOWORD = NULL_HANDLE:The hDateObj handle was NULL (0).
  226.                      GENERAL_FAILURE:
  227.                                  The DateObj could not be found.  The
  228.                                  hDateObj handle might be garbage!
  229.                      BAD_ID:     The uID value was not recognized.  See
  230.                                  the "INPUT" section above for a list
  231.                                  of the valid values for uID.
  232.              -*-
  233.             HIWORD = 0: The element's value was successfully retrieved.
  234.             LOWORD = Element value (except with YEAR, in which it is likely
  235.                      that a portion of the HIWORD could be used; in any case,
  236.                      the HIWORD will never == DATE_ERROR on a normal return)
  237.  
  238.    CALCULATE
  239.             HIWORD = DATE_ERROR: DateObj could not calculate itself
  240.             LOWORD = NULL_HANDLE:The hDateObj handle was NULL (0).
  241.                      GENERAL_FAILURE:
  242.                                  The DateObj could not be found.  The
  243.                                  hDateObj handle might be garbage!
  244.                      BAD_INPUT:  The values in DateObj from which the
  245.                                  calculations are made are bad -- you
  246.                                  screwed up by passing a wrong value
  247.                                  during a SET message.
  248.                      FATAL_ERROR:A bug exists in DATE.DLL!  Scream
  249.                                  vigorously at Mark Jones.
  250.  
  251.    <other>  HIWORD = DATE_ERROR: An invalid uMsg value was used
  252.             LOWORD = BAD_MESSAGE:            "
  253. */
  254.  
  255. /*****************************************************************************/
  256.  
  257. LONG FAR PASCAL DateObj(HANDLE hDateObj, WORD uMsg, WORD uID, LONG lParam)
  258. {
  259.  LONG lReturn;                  // return() value
  260.  Date FAR *fpDate;              // &DateObj
  261.  
  262.  // INITIALIZE RETURN VALUE TO NO-ERROR
  263.  lReturn = 0;
  264.  
  265.  // MAIN OBJECT-MESSAGE (uMsg) DISPATCH switch:
  266.  switch(uMsg)
  267.    {
  268.     //******************************************************************
  269.     case CREATE:                                // CREATE A NEW DateObj
  270.        if (uID != DATE)
  271.          {
  272.          lReturn = SET_ERR(WRONG_ID);
  273.          break;
  274.          }
  275.        lReturn = (LONG) GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
  276.                 sizeof(Date));
  277.        if (!lReturn)
  278.          {
  279.          lReturn = SET_ERR(GENERAL_FAILURE);
  280.          break;
  281.          }
  282.        break;                                   // end of uMsg case CREATE
  283.  
  284.     //******************************************************************
  285.     case DELETE:                                // DELETE AN EXISTING DateObj
  286.        if (uID != DATE)                      // If ID doesn't belong here,
  287.          {
  288.          lReturn = SET_ERR(WRONG_ID);        //  return a WRONG_ID
  289.          break;
  290.          }
  291.        if (!hDateObj)                        // If NULL hDateObj
  292.          {
  293.          lReturn = SET_ERR(NULL_HANDLE);     //  return a BAD_INPUT
  294.          break;
  295.          }
  296.        lReturn = hDateObj;                   // Set lReturn to != DATE_ERROR
  297.        if (GlobalFree(hDateObj))             // If block cannot be freed,
  298.          lReturn = SET_ERR(GENERAL_FAILURE); //  return a GENERAL_FAILURE
  299.  
  300.        break;                                   // end of uMsg case DELETE
  301.  
  302.     //******************************************************************
  303.     case SET:                                   // SET A PROPERTY OR VALUE
  304.  
  305.        if (!hDateObj)                        // If bad hDateObj handle,
  306.         {
  307.         lReturn = SET_ERR(NULL_HANDLE);      //  return a DATE_ERROR
  308.         break;
  309.         }
  310.                                              // Get a far pointer to DateObj
  311.        lReturn = (LONG) GlobalLock(hDateObj);
  312.        if (!lReturn)                         // If unsuccessful,
  313.         {
  314.         lReturn = SET_ERR(BAD_HANDLE);       // return a BAD_HANDLE
  315.         break;
  316.         }
  317.        fpDate = (Date FAR *) lReturn;        // store &DateObj in fpDate
  318.        lReturn = hDateObj;                   // successful so far
  319.  
  320.        switch(uID)                           // Route message
  321.         {
  322.         //----------------------------------------------------------
  323.         case DATE:                        // SETting a DATE
  324.          fpDate->nYear =                  //  set DateObj to all 0's
  325.          fpDate->nMonth =
  326.          fpDate->nDay =
  327.          fpDate->nLeapYear =
  328.          fpDate->nError =
  329.          fpDate->nYearDays =
  330.          fpDate->nDayOfWeek = 0;
  331.          fpDate->lJulianDate = 0L;
  332.          break;
  333.  
  334.         //----------------------------------------------------------
  335.         case MONTH:                       // SETting a MONTH
  336.          if (lParam < 1 || lParam > 12)   // If outside valid range,
  337.             lReturn = SET_ERR(BAD_INPUT); //  return BAD_INPUT
  338.           else                            // else,
  339.                                           //  set nMonth to lParam
  340.             fpDate->nMonth = LOWORD(lParam);
  341.          break;
  342.  
  343.         //----------------------------------------------------------
  344.         case DAY:                         // SETting a DAY
  345.          if (lParam < 1 || lParam > 31)   // If outside valid range,
  346.             lReturn = SET_ERR(BAD_INPUT); //  return BAD_INPUT
  347.           else                            // else,
  348.             fpDate->nDay = LOWORD(lParam);//  set nDay to lParam
  349.            break;
  350.  
  351.         //----------------------------------------------------------
  352.         case YEAR:                        // SETting a YEAR
  353.                                           // If outside valid range,
  354.          if (lParam < BASE_YEAR || lParam > CEIL_YEAR)
  355.             lReturn = SET_ERR(BAD_INPUT); //  return BAD_INPUT
  356.           else                            // else,
  357.                                           //  set nYear to lParam
  358.             fpDate->nYear = LOWORD(lParam);
  359.            break;
  360.  
  361.         //----------------------------------------------------------
  362.         case JULIANDATE:                  // SETting a JULIANDATE
  363.                                           // If outside valid range,
  364.          if (lParam < 1 || lParam > MAX_JULIAN)
  365.             lReturn = SET_ERR(BAD_INPUT); //  return BAD_INPUT
  366.           else                            // else,
  367.             fpDate->lJulianDate = lParam; //  set lJulianDate to lParam
  368.            break;
  369.  
  370.         //----------------------------------------------------------
  371.         default:                          // bad uID!
  372.            lReturn = SET_ERR(BAD_ID);
  373.         }           // end of switch(uID)
  374.  
  375.        GlobalUnlock(hDateObj);               // Release fpDate
  376.  
  377.        break;                                // end of uMsg case SET
  378.  
  379.     //******************************************************************
  380.     case GET:                                   // GET A PROPERTY OR VALUE
  381.  
  382.       if (!hDateObj)                         // If bad hDateObj handle,
  383.        {
  384.        lReturn = SET_ERR(NULL_HANDLE);       //  return an DATE_ERROR
  385.        break;
  386.        }
  387.                                              // Get a far pointer to DateObj
  388.       lReturn = (LONG) GlobalLock(hDateObj);
  389.       if (!lReturn)                          // If unsuccessful,
  390.        {
  391.        lReturn = SET_ERR(GENERAL_FAILURE);   //  return a GENERAL_FAILURE
  392.        break;
  393.        }
  394.  
  395.       fpDate = (Date FAR *) lReturn;         // store &DateObj in fpDate
  396.       lReturn = hDateObj;                    // successful so far
  397.  
  398.       switch(uID)                            // Route message
  399.        {
  400.        //----------------------------------------------------------
  401.        case MONTH:                        // GETting a MONTH
  402.          lReturn = fpDate->nMonth;
  403.          break;
  404.  
  405.        //----------------------------------------------------------
  406.        case DAY:                          // GETting a DAY
  407.          lReturn = fpDate->nDay;
  408.          break;
  409.  
  410.        //----------------------------------------------------------
  411.        case YEAR:                         // GETting a YEAR
  412.          lReturn = fpDate->nYear;
  413.          break;
  414.  
  415.        //----------------------------------------------------------
  416.        case JULIANDATE:                   // GETting a JULIANDATE
  417.          lReturn = fpDate->lJulianDate;
  418.          break;
  419.  
  420.        //----------------------------------------------------------
  421.        case LEAPYEAR:                     // GETting a LEAPYEAR
  422.          lReturn = fpDate->nLeapYear;
  423.          break;
  424.  
  425.        //----------------------------------------------------------
  426.        case DAYOFWEEK:                    // GETting a DAYOFWEEK
  427.          lReturn = fpDate->nDayOfWeek;
  428.          break;
  429.  
  430.        //----------------------------------------------------------
  431.        case DAYOFYEAR:                    // GETting a DAYOFYEAR
  432.          lReturn = fpDate->nYearDays;
  433.          break;
  434.  
  435.        //----------------------------------------------------------
  436.        case DAYSINMONTH:                  // GETting a DAYSINMONTH
  437.          lReturn = MoDaTbl[fpDate->nMonth-1];
  438.          if (fpDate->nMonth==2 && !fpDate->nLeapYear)
  439.             lReturn--;
  440.          break;
  441.  
  442.        //----------------------------------------------------------
  443.        default:                           // bad uID!
  444.           lReturn = SET_ERR(BAD_ID);      // return a BAD_ID
  445.  
  446.        }                                    // end of switch(uID)
  447.  
  448.       GlobalUnlock(hDateObj);               // Release fpDate
  449.  
  450.       break;                                // end of uMsg case GET
  451.  
  452.     //******************************************************************
  453.     case CALCULATE:                             // FORCE DateObj to recalc
  454.  
  455.       if (!hDateObj)                         // If bad hDateObj handle,
  456.        {
  457.        lReturn = SET_ERR(NULL_HANDLE);       //  return an DATE_ERROR
  458.        break;
  459.        }
  460.                                              // Get a far pointer to DateObj
  461.       lReturn = (LONG) GlobalLock(hDateObj);
  462.       if (!lReturn)                          // If unsuccessful,
  463.        {
  464.        lReturn = SET_ERR(GENERAL_FAILURE);   //  return a GENERAL_FAILURE
  465.        break;
  466.        }
  467.  
  468.       fpDate = (Date FAR *) lReturn;         // store &DateObj in fpDate
  469.       lReturn = hDateObj;                    // successful so far
  470.  
  471.       switch(lParam)                         // Route message
  472.        {
  473.        //----------------------------------------------------------
  474.        case JULIAN:                       // Calc based on julian date
  475.          if (fpDate->lJulianDate < 1 || fpDate->lJulianDate > MAX_JULIAN)
  476.             {                          // If original julian date was bad,
  477.                                        //  return a BAD_INPUT
  478.             lReturn = SET_ERR(BAD_INPUT);
  479.             break;
  480.             }
  481.          CalcNormalDate(fpDate);       // Calculate a normal date
  482.          if (fpDate->nError)           // If an error occurred,
  483.             {                          //  report a bug in DATE.DLL and exit!
  484.             lReturn = SET_ERR(FATAL_ERROR);
  485.             break;
  486.             }
  487.          break;
  488.  
  489.        //----------------------------------------------------------
  490.        case NORMAL:                       // Calc based on normal date
  491.          CheckValidDate(fpDate);       // Check for a valid MMDDYYYY date
  492.          if (fpDate->nError)           // If an error occurred,
  493.             {                          //  return a BAD_INPUT
  494.             lReturn = SET_ERR(BAD_INPUT);
  495.             break;
  496.             }
  497.          CalcDayOfWeek(fpDate);        // Calculate all items
  498.          if (fpDate->nError)           // If an error occurred,
  499.             {                          //  report a bug in DATE.DLL and exit!
  500.             lReturn = SET_ERR(FATAL_ERROR);
  501.             break;
  502.             }
  503.          break;
  504.  
  505.        //----------------------------------------------------------
  506.        default:                           // bad uID!
  507.           lReturn = SET_ERR(BAD_ID);      // return a BAD_ID
  508.  
  509.        }                                    // end of switch(uID)
  510.  
  511.       GlobalUnlock(hDateObj);               // Release fpDate
  512.  
  513.       break;                                // end of uMsg case CALCULATE
  514.  
  515.     //******************************************************************
  516.     default:                                    // BAD uMsg value!
  517.        lReturn = SET_ERR(BAD_MESSAGE);
  518.    }                                            // end of switch(uMsg)
  519.  
  520.  return(lReturn);               // return LONG value
  521. }
  522.  
  523.  
  524. /*****************************************************************************/
  525. /*****************************************************************************/
  526.  
  527.              /* PROCEDURE-ORIENTED DATE.DLL FUNCTIONS */
  528.  
  529. /*****************************************************************************/
  530. /*****************************************************************************/
  531.  
  532.              /* CALCULATE THE DATE FROM A JULIAN VALUE */
  533.  
  534. /* This procedure will determine the date from any valid Julian number.
  535.    A valid Julian number will range from 1 to 72743.
  536.  
  537.    If the Julian value is valid, this procedure will fill in the fields
  538.    DateStruct->nYear, DateStruct->nMonth, DateStruct->nDay,
  539.    DateStruct->nLeapYear, DateStruct->nYearDays, and
  540.    DateStruct->nDayOfWeek.
  541.  
  542.    If the Julian value is bad, this procedure will make DateStruct->nError =
  543.    ON, and will return with no further action.
  544. */
  545.  
  546. VOID FAR PASCAL CalcNormalDate(Date far *DateStruct)
  547. {
  548.  int ctr;                                       /* loop counter */
  549.  int accum;                                     /* accumulator */
  550.  long JulianSave = DateStruct->lJulianDate;   /* julian-date save */
  551.  
  552.  DateStruct->nError = OFF;                     /* no error yet */
  553.  
  554.                         /* if bad Julian value, */
  555.  if (DateStruct->lJulianDate<1 || DateStruct->lJulianDate>MAX_JULIAN)
  556.   {
  557.     DateStruct->nError = ON;                   /* flag as date error */
  558.     return;                                     /* return from function */
  559.   }
  560.  
  561.  /* CALCULATE DateStruct->nYear */
  562.                         /* determine year number */
  563.  DateStruct->nYear = (int) ((float) DateStruct->lJulianDate / 365.25) +
  564.               BASE_YEAR;
  565.  if (!DateStruct->lJulianDate % 1461L) DateStruct->nYear--;
  566.  
  567.  /* CALCULATE DateStruct->nYearDays */
  568.                         /* determine day # in year */
  569.  DateStruct->nYearDays = (int)
  570.           ( DateStruct->lJulianDate -
  571.                (long) ( ((float) (DateStruct->nYear - BASE_YEAR)) * 365.25 )
  572.           );
  573.  
  574.  /* CALCULATE DateStruct->nLeapYear */
  575.  if (DateStruct->nYear % 4)                    /* determine leap year ? */
  576.     DateStruct->nLeapYear = OFF;               /* if remainder, OFFT leap yr */
  577.  else                                           /* else, */
  578.     DateStruct->nLeapYear = ON;        /* is a leap year */
  579.  
  580.  /* CALCULATE DateStruct->nMonth */
  581.  accum = 0;                                     /* zero out day accumulator */
  582.  for (ctr = 0; ctr < 12; ctr++)                 /* month-day accumulate loop */
  583.   {
  584.    accum += MoDaTbl[ctr];                     /* add # days in month */
  585.    if (accum >= DateStruct->nYearDays)         /* if finished accumulating */
  586.     {
  587.       DateStruct->nMonth = ctr + 1;            /* month is identified */
  588.       break;                                    /* exit loop */
  589.     }
  590.    if (ctr==1 && !DateStruct->nLeapYear)       /* if Feb of non-leap year */
  591.       accum--;                                  /* decrement accum by 1 */
  592.   }
  593.  
  594.  /* CALCULATE DateStruct->nDay */
  595.                         /* determine day in month */
  596.  DateStruct->nDay = MoDaTbl[ctr] - (accum - DateStruct->nYearDays);
  597.  
  598.  
  599.  /* CALCULATE DateStruct->nDayOfWeek
  600.  
  601.     The new DateStruct->nYear, DateStruct->nMonth, and DateStruct->nDay
  602.     is used to recalculate the julian date, using the CalcDayOfWeek()
  603.     function.
  604.  
  605.     If the calculation fails, this function immediately returns.  Otherwise,
  606.     the new DateStruct->lJulianDate value is compared to the previous one.
  607.     If the new value is not equal to the old one, the DateStruct->nError flag
  608.     is set, and this function returns.
  609.  */
  610.  CalcDayOfWeek(DateStruct);                  /* determine day-of-week */
  611.  if (DateStruct->nError) return;               /* exit if error */
  612.  if (DateStruct->lJulianDate != JulianSave)   /* if DateStruct->
  613.                             JulianDate changed, */
  614.     DateStruct->nError = ON;                   /* there is a bug */
  615. }
  616.  
  617.  
  618. /*****************************************************************************/
  619.  
  620.             /* CALCULATE THE DAY OF THE WEEK */
  621.  
  622. /* This procedure calculates the value for the DateStruct->nDayOfWeek,
  623.    ranging from 0 (Sunday) to 6 (Saturday).  The fields DateStruct->nYear,
  624.    DateStruct->nMonth, and DateStruct->nDay should be initialized with valid
  625.    date values before this function is called.
  626.  
  627.    This procedure will exit with no action if any of these three date
  628.    fields (DateStruct->nYear, DateStruct->nMonth, and DateStruct->nDay) has a
  629.    bad value, and the DateStruct->nError field will be set to ON (bad date
  630.    data).
  631. */
  632.  
  633. VOID FAR PASCAL CalcDayOfWeek(Date far *DateStruct)
  634. {
  635.  DateStruct->nDayOfWeek = 0;                   /* initial value of 0 */
  636.  
  637.  CalcJulianDate(DateStruct);                  /* calculate julian value */
  638.  if (!DateStruct->nError) {                    /* if a valid date, */
  639.                         /* determine day of week */
  640.     DateStruct->nDayOfWeek = (int) (DateStruct->lJulianDate % 7) + 1;
  641.     if (DateStruct->nDayOfWeek > 6)
  642.        DateStruct->nDayOfWeek = 0;
  643.  }
  644. }
  645.  
  646.  
  647. /*****************************************************************************/
  648.  
  649.         /* CALCULATE THE JULIAN VALUE OF A DATE */
  650.  
  651. /* This procedure calculates the value for the field DateStruct->lJulianDate,
  652.    which is the consecutive day number, starting at 1, from January 1,
  653.    1901 (Julian date value of 1), and is accurate to February 28, 2100
  654.    (Julian date value of 72743).
  655.  
  656.    This procedure will exit with no action if any of the three date
  657.    fields (DateStruct->nYear, DateStruct->nMonth, and DateStruct->nDay) has a
  658.    bad value, and the DateStruct->nError field will be set to ON (bad date
  659.    data).
  660. */
  661.  
  662. VOID FAR PASCAL CalcJulianDate(Date far *DateStruct)
  663. {
  664.  DateStruct->lJulianDate = 0;                  /* no julian date yet */
  665.  
  666.  CalcYearDays(DateStruct);                    /* calc # days into year */
  667.  if (!DateStruct->nError)                      /* if a valid date, */
  668.   {                                             /* calculate julian date */
  669.     DateStruct->lJulianDate = (
  670.            ((long) (DateStruct->nYear - BASE_YEAR)) * 365
  671.           ) +
  672.           (
  673.            (DateStruct->nYear - BASE_YEAR) / 4
  674.           ) +
  675.           DateStruct->nYearDays;
  676.   }
  677. }
  678.  
  679.  
  680. /*****************************************************************************/
  681.  
  682.         /* CALCULATE THE NUMBER OF DAYS INTO THE YEAR */
  683.  
  684. /* This procedure calculates the value for the field DateStruct->nYearDays,
  685.    which is the "day number" in the year for the date designated by
  686.    the three date fields (DateStruct->nYear, DateStruct->nMonth, and
  687.    DateStruct->nDay).  The value of the DateStruct->nYearDays field may range
  688.    from 1 to 366.
  689.  
  690.    This procedure will exit with no action if any of the three date
  691.    fields (DateStruct->nYear, DateStruct->nMonth, and DateStruct->nDay) has a
  692.    bad value, and the DateStruct->nError field will be set to ON (bad date
  693.    data).
  694. */
  695.  
  696. VOID FAR PASCAL CalcYearDays(Date far *DateStruct)
  697. {
  698.  int ctr;                                       /* loop counter */
  699.  
  700.  DateStruct->nYearDays = 0;                    /* no year days yet */
  701.  
  702.  IsLeapYear(DateStruct);                      /* determine if leap year */
  703.  if (!DateStruct->nError) {                    /* if a valid date, */
  704.     if (DateStruct->nMonth > 1) {              /* if past January, */
  705.                         /* accumulate month-days */
  706.        for (ctr=0; ctr < DateStruct->nMonth-1; ctr++)
  707.     {
  708.       DateStruct->nYearDays += MoDaTbl[ctr];
  709.     }
  710.     }
  711.     DateStruct->nYearDays += DateStruct->nDay; /* add day-in-month */
  712.                         /* if past Feb, non-leap yr, */
  713.     if (DateStruct->nMonth > 2 && !DateStruct->nLeapYear)
  714.        DateStruct->nYearDays -= 1;             /* take one day away */
  715.  }
  716. }
  717.  
  718.  
  719. /*****************************************************************************/
  720.  
  721.             /* IS THIS A LEAP YEAR ? */
  722.  
  723. /* This procedure determines whether the date designated by the
  724.    three date fields (DateStruct->nYear, DateStruct->nMonth, and
  725.    DateStruct->nDay) falls within a leap year.  If it does, then the field
  726.    DateStruct->nLeapYear will be set to ON; otherwise, it will be set to 0.
  727.  
  728.    This procedure will exit with no action if any of the three date
  729.    fields (DateStruct->nYear, DateStruct->nMonth, and DateStruct->nDay) has a
  730.    bad value, and the DateStruct->nError field will be set to ON (bad date
  731.    data).
  732. */
  733.  
  734. VOID FAR PASCAL IsLeapYear(Date far *DateStruct)
  735. {
  736.  CheckValidDate(DateStruct);                  /* is this a valid date ? */
  737.  if (!DateStruct->nError) {                    /* if a valid date, */
  738.     if (DateStruct->nYear % 4)                 /* determine if leap year */
  739.        DateStruct->nLeapYear = OFF;            /* if remainder, OFFT leap yr */
  740.     else                                        /* else, */
  741.        DateStruct->nLeapYear = ON;             /* is a leap year */
  742.  }
  743. }
  744.  
  745.  
  746. /*****************************************************************************/
  747.  
  748.         /* VERIFY THAT DATE FIELDS CONTAIN VALID VALUES */
  749.  
  750. /* This procedure checks to see whether the date designated by the
  751.    three date fields (DateStruct->nYear, DateStruct->nMonth, and
  752.    DateStruct->nDay) is a valid date, but does not check for leap years.
  753.  
  754.    If the date field data is valid, the field DateStruct->nError will contain
  755.    an OFF upon exit.  Otherwise, this field will contain an ON.
  756. */
  757.  
  758. VOID FAR PASCAL CheckValidDate(Date far *DateStruct)
  759. {
  760.  DateStruct->nError = OFF;                     /* no date error yet */
  761.  if                                             /* test date values */
  762.   (
  763.     DateStruct->nYear < BASE_YEAR ||
  764.     DateStruct->nYear > CEIL_YEAR ||
  765.     DateStruct->nMonth < 1 ||
  766.     DateStruct->nMonth > 12 ||
  767.     DateStruct->nDay < 1 ||
  768.     DateStruct->nDay > 31 ||
  769.     (DateStruct->nYear==CEIL_YEAR && DateStruct->nMonth>=2 &&
  770.      DateStruct->nDay>28)
  771.   )
  772.     DateStruct->nError = ON;                   /* signify date error */
  773.  if (!DateStruct->nError)
  774.     if (DateStruct->nDay > MoDaTbl[DateStruct->nMonth-1])
  775.        DateStruct->nError = ON;
  776. }
  777.  
  778.  
  779. /*****************************************************************************/
  780.  
  781.            /* ONE-TIME GLOBAL DLL-INIT FUNCTION */
  782.  
  783. int FAR PASCAL LibMain (HANDLE hInstance,
  784.             WORD wDataSeg,
  785.             WORD cbHeapSize,
  786.             LPSTR lpszCmdLine)
  787. {
  788.     /* Perform DLL initialization
  789.     .
  790.     .
  791.     .
  792.     */
  793.  
  794. if (cbHeapSize != 0)                    /* If DLL data seg is MOVEABLE */
  795.     UnlockData(0);                      /*  unlock the data seg */
  796.  
  797. return(1);                              /* Initialization successful,
  798.                        otherwise, return(0); */
  799. }
  800.  
  801.  
  802. /*****************************************************************************/
  803.  
  804.         /* ONE-TIME GLOBAL DLL-SHUTDOWN FUNCTION */
  805.  
  806. WORD FAR PASCAL WEP(int nParameter)
  807. {
  808.     if (nParameter == WEP_SYSTEM_EXIT)
  809.     {
  810.     /* System shutdown in progress.  Respond accordingly. */
  811.     return(1);
  812.     }
  813.     else
  814.     {
  815.     if (nParameter == WEP_FREE_DLL)
  816.     {
  817.         /* DLL-use count is zero.  Every application that had loaded the
  818.         DLL has freed it. */
  819.         return(1);
  820.     }
  821.     else
  822.     {
  823.         /* Undefined value.  Ignore. */
  824.         return(1);
  825.     }
  826.     }
  827. }
  828.