/****************************************************************************
*
*                            Open Watcom Project
*
*    Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
*  ========================================================================
*
*    This file contains Original Code and/or Modifications of Original
*    Code as defined in and that are subject to the Sybase Open Watcom
*    Public License version 1.0 (the 'License'). You may not use this file
*    except in compliance with the License. BY USING THIS FILE YOU AGREE TO
*    ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
*    provided with the Original Code and Modifications, and is also
*    available at www.sybase.com/developer/opensource.
*
*    The Original Code and all software distributed under the License are
*    distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
*    EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
*    ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
*    NON-INFRINGEMENT. Please see the License for the specific language
*    governing rights and limitations under the License.
*
*  ========================================================================
*
* Description:  mktime() without timezone and struct tm fields adjustment.
*               used by mktime() and DOS clock()
*
****************************************************************************/

#include "variety.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <limits.h>
#include "rtdata.h"
#include "timedata.h"

#define MONTH_YR        ( 12 )
#define DAY_YR          ( 365 )
#define HOUR_YR         ( DAY_YR * 24 )
#define MINUTE_YR       ( HOUR_YR * 60 )
#define SECOND_YR       ( MINUTE_YR * 60 )
#define __MONTHS        ( INT_MIN / MONTH_YR )
#define __DAYS          ( INT_MIN / DAY_YR )

// these ones can underflow in 16bit environments,
// so check the relative values first
#if ( HOUR_YR ) < ( INT_MAX / 60 )
 #define __MINUTES      ( INT_MIN / MINUTE_YR )
 #if ( MINUTE_YR ) < ( INT_MAX / 60 )
  #define __SECONDS     ( INT_MIN / SECOND_YR )
 #else
  #define __SECONDS     ( 0 )
 #endif
#else
 #define __MINUTES      ( 0 )
 #define __SECONDS      ( 0 )
#endif

#define SMALLEST_YEAR_VALUE ( __MONTHS + __DAYS + __MINUTES + __SECONDS )

time_t __local_mktime( const struct tm *t, long *pdays, long *pseconds )
{
    int         month;
    int         year;
    long        days;
    long        seconds;
    short const *month_start;

    month_start = __diyr;
    month = t->tm_mon % 12; /* put tm_mon into range */
    year = t->tm_year;
    if( year < SMALLEST_YEAR_VALUE )
        return( ( time_t ) -1 );
    year += t->tm_mon / 12;
    while( month < 0 )
        --year, month += 12;
    if( year < 0 )
        return( ( time_t ) -1 );
    if( __leapyear( ( unsigned ) ( year + 1900 ) ) )
        month_start = __dilyr;
    days = year * 365L                   /* # of days in the years */
        + ( ( year + 3 ) / 4 )           /* add # of leap years before year */
        - ( ( year + 99 ) / 100 )        /* sub # of leap centuries */
        + ( ( year + 399 - 100 ) / 400 ) /* add # of leap 4 centuries */
                                         /* adjust for 1900 offset */
                                         /* note: -100 == 300 (mod 400) */
        + month_start[month]             /* # of days to 1st of month*/
        + t->tm_mday - 1;                /* day of the month */
    seconds = ( ( ( long ) ( t->tm_hour ) ) *60L + ( long ) ( t->tm_min ) ) *60L + t->tm_sec;
                                         /* seconds needs to be positive for __brktime */
    while( seconds < 0 )
        days -= 1, seconds += ( long ) SECONDS_PER_DAY;
    while( seconds >= ( long ) SECONDS_PER_DAY )
        days += 1, seconds -= ( long ) SECONDS_PER_DAY;
    if( days < ( DAYS_FROM_1900_TO_1970 - 1 ) )
        return( ( time_t ) -1 );
    if ( pdays ) *pdays = days;
    if ( pseconds ) *pseconds = seconds;
    return( seconds + ( days - DAYS_FROM_1900_TO_1970 ) * ( long ) SECONDS_PER_DAY );
}