/*

  Source: date.c
  Author: Linda Murphy, murphy@dccs.upenn.edu
  Date: December 1991

  4/29/92 wade@mit.edu
  Added print_wday() to create a date string in
  the form "Wed Apr 1 1992".
  
  */

#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <ctype.h>
#include "date.h"

#define ASECOND  1
#define AMINUTE  60
#define ANHOUR   3600
#define ADAY     86400     /*  24 x ANHOUR */
#define AMONTH   2419200   /*  28 x ADAY   */
#define AYEAR    31536000  /* 365 x ADAY   */

#define NUMMONTHS 12
void print_wday(short year, short month, short day, char *result);

char *monthnames[] = {
  "",   /* there is no 0th month */
  "Jan",
  "Feb",
  "Mar",
  "Apr",
  "May",
  "Jun",
  "Jul",
  "Aug",
  "Sep",
  "Oct",
  "Nov",
  "Dec"
  };

int monthlen[] = {
  0,   /* there is no 0th month */
  31,  /* jan */
  29,  /* feb */
  31,  /* mar */
  30,  /* apr */
  31,  /* may */
  30,  /* jun */
  31,  /* jul */ 
  31,  /* aug */
  30,  /* sep */
  31,  /* oct */
  30,  /* nov */
  31   /* dec */
  };

/*****************************************************************************
 *  amon to imon - convert ascii string to integer month (0 to 11)           *
 *****************************************************************************/
 
int atoi_mon (name)
     char *name;
{
  int i, imon;

  if (isdigit(*name))
    return(atoi(name));

  for (imon = -1, i=1; i < 13 && imon<0 ; i++) {
    if ( strcasecmp(name, monthnames[i]) == 0)
      imon = i; 
  }
  return(imon); 
}

/*
  The delta returned is not precisely correct, it is
  only an approximate value of the # of seconds between date1 & date2 */
int datecmp (struct daterec date1, struct daterec date2)
{
  int delta;

  if ( (delta = date1.year - date2.year) != 0)
    return (delta * AYEAR);
  if ((delta = date1.month - date2.month) != 0)
    return (delta * AMONTH);
  if ((delta = date1.day - date2.day) != 0)
    return (delta * ADAY);
  if ((delta = date1.hour - date2.hour) != 0)
    return (delta * ANHOUR);
  if ((delta = date1.minute - date2.minute) != 0)
    return (delta * AMINUTE);
  if ((delta = date1.sec - date2.sec) != 0)
    return (delta * ASECOND);

  /* if we got this far, the dates are equal */
  return 0;
}

/* parse_logdate:
   expecting " Jan  1 00:00:00 1990"  (Month Day Hour:Min:Sec Year)
   accepts a number instead of 3 letter monthname.
   year is optional (current year is default).
   any # of spaces greater than 0 is accepted (where whitespace is indicated)
   time_opt parameter is true, then "month day year" is acceptable
   */
int parse_logdate (char *datestr, struct daterec *dt, int opt_time)
{
  char *ptr, *ptr2;
  time_t now;

  dt->year = dt->month = dt->day = dt->hour = dt->minute = dt->sec = 0;
  for (ptr = datestr; *ptr == ' '; ptr++);  /* skip blanks */
  for (ptr2=ptr; *ptr2 && *ptr2 != ' '; ptr2++);     /* find end of month str*/
  if (*ptr2 == ' ') {
    *ptr2 = '\0';
    ptr2++;
  }
  if ((dt->month = atoi_mon(ptr)) < 1)
    return 0;
  ptr = ptr2;  /* move past month */

  for (; *ptr == ' '; ptr++);  /* skip blanks */
  if ((dt->day = atoi (ptr)) <= 0)
    return 0;

  for (; *ptr && *ptr != ' '; ptr++);  /* move past day */
  for (; *ptr == ' '; ptr++);  /* skip blanks */

  for (ptr2 = ptr; *ptr2 && *ptr2 != ':'; ptr2++); /*if there's :, move to it*/
  if (*ptr2 != ':' && !opt_time)     /* time is not optional */
    return 0;

  if (*ptr2 == ':') {
    *ptr2 = '\0';
    dt->hour = atoi(ptr);
    ptr = ptr2+1;

    dt->minute = atoi(ptr);
    for (; *ptr && *ptr != ':' && *ptr != ' '; ptr++);  /* move past minute */

    if (*ptr != ':' && !opt_time)   /* seconds are not optional */
      return 0;
    
    if (*ptr == ':') {   /* seconds are optional, so look for colon */
      ptr++;
      dt->sec = atoi(ptr);
      for (; *ptr && *ptr != ' '; ptr++);  /* move past second */
    }
  }

  for (; *ptr == ' '; ptr++);  /* skip blanks */

  if (!isdigit(*ptr)) {
    now = time(0);
    dt->year = atoi(ctime(&now)+20);  /* default year is current one */
    return 1;
  }

  if ((dt->year = atoi(ptr)) < 1)
    return 0;

  return 1;
}

sprintdate(struct daterec date, char *datestr, int fmt)
{
  switch(fmt) {
  case 1:
    sprintf(datestr, "%02d/%02d/%d %02d:%02d:%02d", 
	    date.month, date.day, date.year,
	    date.hour, date.minute, date.sec);
    break;
    
  case 2:
    sprintf(datestr, "%d/%02d/%02d %02d:%02d:%02d", 
	    date.year, date.month, date.day,
	    date.hour, date.minute, date.sec);
    break;

  case 3:
    sprintf(datestr, "%s %2d %d", 
	    monthnames[date.month], date.day, date.year);
    break;

  case 4:
    print_wday(date.year, date.month, date.day, datestr);
    break;

  case 0:
  default:
    sprintf(datestr, "%s %2d %02d:%02d:%02d %d", 
	    monthnames[date.month], date.day,
	    date.hour, date.minute, date.sec, date.year);
    break;
  }
}



int
leapyear (y)
     int y;
{
  if ( (y % 400 == 0) || ( (y % 4 == 0) && (y % 100 != 0) ) ) {
    return(1); 
  }
  else
    return(0);
}

dateincre (struct daterec *dat, char incrementch)
{
  int daysinmonth;

  switch (incrementch) {
  case 'H':
    if (dat->hour < 23) {
      (dat->hour)++;
      break;                        /* break out of switch */
    }
    dat->hour = 0;  /* if hour was 23:, next hour is 00: */
    /* continue to next case to incre day */

  case 'W':
  case 'D':
    dat->day += (incrementch == 'W') ? 7 : 1;
    
    if (dat->month == 2)
      daysinmonth = leapyear(dat->year) ? 29 : 28;
    else
      daysinmonth = monthlen[dat->month];
    
    if (dat->day <= daysinmonth)
      break;                       /* break out of switch */

    /* past the end of the month: fix the day, and incr month */
    dat->day -= daysinmonth;
    /* contine to next case to increment month */

  case 'M':
    if (dat->month < 12) {
      (dat->month)++;
      break;                        /* break out of switch */
    }

    /* if month is December, next month is January, and year increases */
    dat->month = 1;
    /* continue to next case to incre year */

  case 'Y':
    (dat->year)++;
    break;

  default:
    fprintf(stderr,"Increment character must be one of HDWMY\n");
    exit (-1);
    break;
  }
}

datedecr (struct daterec *dat, char decrementch)
{
  int daysinmonth;

  switch (decrementch) {
  case 'H':
    if (dat->hour > 0) {
      (dat->hour)--;
      break;                        /* break out of switch */
    }
    dat->hour = 23;  /* if hour was 00:, prev hour is 23: */
    /* continue to next case to decre day */

  case 'W':
  case 'D':
    dat->day -= (decrementch == 'W') ? 7 : 1;

    if (dat->day > 0)
      break;                                 /* break out of switch */

    /* day is <= 0, so we need to fix day, and decre month */

    /* how many days are in the previous month? */
    if (dat->month == 3)                  /* previous month was Feb */
      daysinmonth = leapyear(dat->year) ? 29 : 28;
    else if (dat->month <= 1)             /* previous month was Dec */
      daysinmonth = monthlen[12];
    else
      daysinmonth = monthlen[(dat->month - 1)];

    /* since day is 0 or less, adding is really subtracting */
    dat->day += daysinmonth;    

    /* contine to next case to decre month */

  case 'M':
    if (dat->month > 1) {
      (dat->month)--;
      break;                        /* break out of switch */
    }
    
    /* if month is January, prev month is December, and year decreases */
    dat->month = 12;
    /* continue to next case to decre year */
    
  case 'Y':
    (dat->year)--;
    break;
    
  default:
    fprintf(stderr,"Decrement character must be one of HDWMY\n");
    exit (-1);
    break;
  }
}

setdaterec (struct daterec *dt, time_t tim)
{
  char *timestr;
  time_t thetime;

  thetime = tim;
  timestr = ctime (&thetime);

  dt->year = atoi(timestr+20);
  *(timestr+7) = '\0';
  dt->month = atoi_mon (timestr+4);
  dt->day = atoi(timestr+8);
  dt->hour = atoi(timestr+11);
  dt->minute = atoi(timestr+14);
  dt->sec = atoi(timestr+17);
}

datecpy (struct daterec *d1, struct daterec d2)  /* copy d2 into d1 */
{
  d1->year = d2.year;
  d1->month = d2.month;
  d1->day = d2.day;
  d1->hour = d2.hour;
  d1->minute = d2.minute;
  d1->sec = d2.sec;
}

void
print_wday(short year, short month, short day, char *datestr)
{
  struct tm tm;
  short     jan01;
  short     ctr;
  short     days;
  char     *helpptr;
  short     known_yr;
  char     *astr;

  /* complete tm struct */
  tm.tm_sec  = 0;
  tm.tm_min  = 0;
  tm.tm_hour = 0;
  tm.tm_mday = day;
  tm.tm_mon  = month - 1;
  tm.tm_year = year - 1900;
  tm.tm_yday = 0;

  /* determine day the year starts on - watch out for leap years */
  for (known_yr=90,jan01=1; known_yr < tm.tm_year; known_yr++) {
    if (leapyear(known_yr))
      jan01 = (jan01 + (366 % 7)) % 7;
    else
      jan01 = (jan01 + (365 % 7)) % 7;
  }

  /* correct Febuary days if it is a leap year*/
  (leapyear(year)) ? (monthlen[2] = 29) : (monthlen[2] = 28);

  /* get day of the week 0:6 based on year offset */
  for (ctr = 1,days = 0; ctr <= tm.tm_mon; ctr++)
    days = days + monthlen[ctr];
  tm.tm_wday = ((((days + tm.tm_mday) % 7) + jan01 - 1) % 7);

  /* rip out eol terminator and time */
  astr = asctime(&tm);
  astr[strlen(astr)-1] = '\0';
  helpptr = (char *)index(astr,':');
  *(helpptr-2) = '\0';
  helpptr = helpptr + 7;
  strcat(astr,helpptr);

  strcpy(datestr,astr);
}




