/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** 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 3 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, see . */ /****************************************************************************\ xtime Neal Kettler Improved version of the Wtime library (V->W->X...) Handles signed times better and dates long long long after you'll be dead. \****************************************************************************/ #include #include #ifndef _WINDOWS #include #endif #include "xtime.h" static char *DAYS[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; static char *FULLDAYS[]={"Sunday","Monday","Tuesday","Wednesday","Thursday", "Friday","Saturday"}; static char *MONTHS[]={"Jan","Feb","Mar","Apr","May","Jun","Jul", "Aug","Sep","Oct","Nov","Dec"}; static char *FULLMONTHS[]={"January","February","March","April","May","June", "July","August","September","October","November","December"}; #define IS_LEAP(y) ((y) % 4) == 0 && (! ((y) % 100) == 0 || ((y) % 400) == 0) #define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) /////////////// Utility functions /////////////////// // // Return the daycount since year 0 for the specified date. // month = 1-12, day = 1-31 year = 0... // static sint32 Get_Day(int month, int day, int year) { time_t days; static int DaysAtMonth[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; /* Add up number of straight days + number of leap days + days */ /* up to the month + the day of month. */ days = (year * 365) + (year / 4) - (year / 100) + (year / 400) + DaysAtMonth[month-1] + day; /* If we haven't hit Feb 29, and this is a leap year, we need to */ /* subtract out the leap day that was added above for this year */ if (month < 3 && IS_LEAP(year)) --days; return(days); } // // Get the year from a daycount since year 0 // Also get the daycount since the start of the year // // Ayecarumba what a pain in the ass! // static bit8 Get_Date_From_Day(sint32 days, OUT sint32 &year, OUT sint32 &yday) { //register long int rem; register long int y; //register const unsigned short int *ip; if (days <= 365) { year=0; yday=days+1; // 1 based return(TRUE); } y = 1; days-=365; days--; // zero based // // As far as I can tell there's no non-iteritive way to // do this... // while (days < 0 || days >= (IS_LEAP (y) ? 366 : 365)) { /* Guess a corrected year, assuming 365 days per year. */ long int yg = y + days / 365 - (days % 365 < 0); /* Adjust DAYS and Y to match the guessed year. */ days -= ((yg - y) * 365 + LEAPS_THRU_END_OF (yg - 1) - LEAPS_THRU_END_OF (y - 1)); y = yg; } year=y; yday=days+1; // 1 based return(TRUE); } // // Get the max day of a given month in a given year // int Max_Day(int month, int year) { static char dayTable[2][13] = { {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; bit8 isleap=IS_LEAP(year); return(dayTable[isleap][month]); } /********************************************************** int main(int argc, char *argv[]) { Xtime wtime; int month,mday,year,hour,minute,second,day; int i; int dayoffset; int yr; int yday; int ydaycount; wtime.set(719528,0); *********************************************** for (year=0; year<10000; year++) { ydaycount=1; for (month=1; month<=12; month++) { for (day=1; day<=Max_Day(month,year); day++) { dayoffset=Get_Day(month,day,year); assert (GetDateFromDay(dayoffset,yr,yday)==TRUE); //printf("Yday=%d YdayCount=%d\n",yday,ydaycount); if (yr!=year) { printf("MO=%d DAY=%d YEAR=%d YR=%d\n",month,day,year,yr); assert(0); } if (yday != ydaycount) { printf("MO=%d DAY=%d YEAR=%d YR=%d\n",month,day,year,yr); printf("Yday=%d YdayCount=%d\n",yday,ydaycount); assert(0); } ydaycount++; } } printf("(%d) ",year); } *************************************** ///////wtime.addSeconds((60*60*24)-(60*60*8)); ////////wtime.addSeconds(-(60*60*8)); // timezone delta dayoffset=Get_Day(1,1,1970); printf("DAYOFFSET = %d\n",dayoffset); wtime.getTime(month, mday, year, hour, minute, second); printf("\n%s %d %d %d:%02d:%02d\n\n", MONTHS[month-1],mday,year,hour,minute,second); struct timeval unixtime; struct timezone unixtzone; time_t ttime; tm tmtime; memset(&tmtime,0,sizeof(tmtime)); ttime=0; unixtime.tv_sec=0; unixtime.tv_usec=0; //gettimeofday(&unixtime,&unixtzone); //ttime=time(NULL); tmtime=*gmtime(&ttime); printf("TIME->CTIME = %s\n",ctime(&ttime)); printf("GTOD->CTIE = %s\n",ctime(&(unixtime.tv_sec))); printf("TIME->GMT->ASCTIME = %s\n",asctime(&tmtime)); } ***************************************************************/ // // Construct with current clock time // Xtime::Xtime(void) { update(); } // // Copy constructor // Xtime::Xtime( Xtime &other ) { day_=other.day_; msec_=other.msec_; } // // Set to a time_t (1970-2038) // Xtime::Xtime( time_t other ) { day_=719528; // days from year 0 to Jan1, 1970 // Add seconds from time_t addSeconds(other); msec_=0; } Xtime::~Xtime() { } // // Add some number of seconds to the time (seconds can be negative) // void Xtime::addSeconds(sint32 seconds) { // Add to day counter first day_+=(seconds/86400); msec_+=(seconds % 86400)*1000; // Now normalize in case msec is > 1 days worth normalize(); } // // If msec is > 1 days worth, adjust the day count // & decrement the msec counter until OK. // void Xtime::normalize(void) { day_+=(msec_/86400000); msec_%=86400000; while (msec_ < 0) { day_--; msec_+=86400000; } } // // Update time to hold the current clock time // (breaks in 2038 :-) // void Xtime::update(void) { day_=719528; // day_s from year 0 to Jan1, 1970 msec_=0; #ifdef _WINDOWS struct _timeb wintime; _ftime(&wintime); addSeconds(wintime.time); msec_+=wintime.millitm; #endif #ifndef _WINDOWS struct timeval unixtime; struct timezone unixtzone; gettimeofday(&unixtime,&unixtzone); addSeconds(unixtime.tv_sec); msec_+=(unixtime.tv_usec/1000); #endif // Now normalize in case msec is > 1 days worth normalize(); } // This takes the standard Microsoft time formatting string // make sure the out string is big enough // An example format would be "mm/dd/yy hh:mm:ss" // CHANGE: Joe Howes 06/30/99 // To specify 12-hour format, use "aa" instead of "hh". // The hours will be 12 hour and the string will be // appended with " AM" or " PM". bit8 Xtime::FormatTime(char *out, char *format) { int lastWasH=0; int ampmflag = 0; out[0]=0; char *ptr=format; if (*ptr=='"') ptr++; // skip past open quote if exists while (*ptr!=0) { if (lastWasH>0) lastWasH--; if (isspace(*ptr)) { if (lastWasH==1) lastWasH=2; sprintf(out+strlen(out),"%c",*ptr); ptr+=1; } else if (strncmp(ptr,"\"",1)==0) { break; } else if (strncmp(ptr,":",1)==0) { if (lastWasH==1) lastWasH=2; sprintf(out+strlen(out),":"); ptr+=1; } else if (strncmp(ptr,"/",1)==0) { sprintf(out+strlen(out),"/"); ptr+=1; } else if (strncmp(ptr,"c",1)==0) { sprintf(out+strlen(out),"%ld/%ld/%02ld %ld:%02ld:%02ld",getMonth(), getMDay(),getYear()%100,getHour(),getMinute(),getSecond()); ptr+=1; } else if (strncmp(ptr,"dddddd",6)==0) { sprintf(out+strlen(out),"%s %02ld, %ld",FULLMONTHS[getMonth()-1], getMDay(),getYear()); ptr+=6; } else if (strncmp(ptr,"ddddd",5)==0) { sprintf(out+strlen(out),"%ld/%ld/%02ld",getMonth(),getMDay(), getYear()%100); ptr+=5; } /*else if (strncmp(ptr,"dddd",4)==0) { sprintf(out+strlen(out),"%s",FULLDAYS[getWDay()-1]); ptr+=4; } else if (strncmp(ptr,"ddd",3)==0) { sprintf(out+strlen(out),"%s",DAYS[getWDay()-1]); ptr+=3; }*/ else if (strncmp(ptr,"dd",2)==0) { sprintf(out+strlen(out),"%02ld",getMDay()); ptr+=2; } else if (strncmp(ptr,"d",1)==0) { sprintf(out+strlen(out),"%ld",getMDay()); ptr+=1; } /*else if (strncmp(ptr,"ww",2)==0) { sprintf(out+strlen(out),"%02ld",getYWeek()); ptr+=2; }*/ /*else if (strncmp(ptr,"w",1)==0) { sprintf(out+strlen(out),"%ld",getWDay()); ptr+=1; }*/ else if (strncmp(ptr,"mmmm",4)==0) { sprintf(out+strlen(out),"%s",FULLMONTHS[getMonth()-1]); ptr+=4; } else if (strncmp(ptr,"mmm",3)==0) { sprintf(out+strlen(out),"%s",MONTHS[getMonth()-1]); ptr+=3; } else if (strncmp(ptr,"mm",2)==0) { if (lastWasH==1) sprintf(out+strlen(out),"%02ld",getMinute()); else sprintf(out+strlen(out),"%02ld",getMonth()); ptr+=2; } else if (strncmp(ptr,"m",1)==0) { if (lastWasH==1) sprintf(out+strlen(out),"%ld",getMinute()); else sprintf(out+strlen(out),"%ld",getMonth()); ptr+=1; } else if (strncmp(ptr,"q",1)==0) { sprintf(out+strlen(out),"%ld",((getMonth()-1)/4)+1); // GetQuarter ptr+=1; } else if (strncmp(ptr,"yyyy",4)==0) { sprintf(out+strlen(out),"%ld",getYear()); ptr+=4; } else if (strncmp(ptr,"yy",2)==0) { sprintf(out+strlen(out),"%02ld",getYear()%100); ptr+=2; } /*else if (strncmp(ptr,"y",1)==0) { sprintf(out+strlen(out),"%ld",getYDay()); ptr+=1; }*/ else if (strncmp(ptr,"hh",2)==0) { sprintf(out+strlen(out),"%02ld",getHour()); lastWasH=2; // needs to be 1 after top of loop decs it ptr+=2; } else if (strncmp(ptr,"h",1)==0) { sprintf(out+strlen(out),"%ld",getHour()); lastWasH=2; // needs to be 1 after top of loop decs it ptr+=1; } else if (strncmp(ptr,"nn",2)==0) { sprintf(out+strlen(out),"%02ld",getMinute()); ptr+=2; } else if (strncmp(ptr,"n",1)==0) { sprintf(out+strlen(out),"%ld",getMinute()); ptr+=1; } else if (strncmp(ptr,"ss",2)==0) { sprintf(out+strlen(out),"%02ld",getSecond()); ptr+=2; } else if (strncmp(ptr,"s",1)==0) { sprintf(out+strlen(out),"%ld",getSecond()); ptr+=1; } else if (strncmp(ptr,"ttttt",5)==0) { sprintf(out+strlen(out),"%ld:%02ld:%02ld",getHour(),getMinute(), getSecond()); ptr+=5; } else if (strncmp(ptr,"aa",2)==0) { uint32 tmp = (getHour() <= 12) ? getHour() : getHour() - 12; sprintf(out+strlen(out),"%02ld", tmp); lastWasH=2; // needs to be 1 after top of loop decs it ptr+=2; ampmflag = 1; } else // an unknown char, move to next ptr++; } if(ampmflag) { char ampm[4]; if( getHour() < 12 ) strcpy(ampm, " AM"); else strcpy(ampm, " PM"); sprintf(out+strlen(out), "%s", ampm); } return(TRUE); } /************************************** REPLACE THIS CRAP // Parses a date string that's in modified RFC 1123 format // Can have a +minutes after the normal time // eg: Thu, 20 Jun 1996 17:33:49 +100 // Returns true if successfully parsed, false otherwise bit8 Xtime::ParseDate(char *in) { int i; uint32 minOffset; struct tm t; char *ptr=in; while ((!isgraph(*ptr))&&(*ptr!=0)) ptr++; // skip to start of string if (*ptr==0) return(FALSE); t.tm_wday_=-1; for (i=0; i<7; i++) // parse day_ of week if (strncmp(ptr,DAYS[i],strlen(DAYS[i]))==0) t.tm_wday_=i; if (t.tm_wday_==-1) return(FALSE); while ((!isdigit(*ptr))&&(*ptr!=0)) ptr++; // skip to day_ of month if (*ptr==0) return(FALSE); t.tm_mday_=atoi(ptr); while ((!isalpha(*ptr))&&(*ptr!=0)) ptr++; // skip to month if (*ptr==0) return(FALSE); t.tm_mon=-1; for (i=0; i<12; i++) // match month if (strncmp(ptr,MONTHS[i],strlen(MONTHS[i]))==0) t.tm_mon=i; if (t.tm_mon==-1) return(FALSE); while ((!isdigit(*ptr))&&(*ptr!=0)) ptr++; if (*ptr==0) return(FALSE); t.tm_year=atoi(ptr); if (t.tm_year<70) // if they specify a 2 digit year, we'll be nice t.tm_year+=2000; else if (t.tm_year<100) t.tm_year+=1900; if (t.tm_year>2200) // I doubt my code will be around for another 203 years return(FALSE); while ((isdigit(*ptr))&&(*ptr!=0)) ptr++; // skip to end of year if (*ptr==0) return(FALSE); while ((!isgraph(*ptr))&&(*ptr!=0)) ptr++; // skip to start of time if (*ptr==0) return(FALSE); t.tm_hour=atoi(ptr); while ((*ptr!=':')&&(*ptr!=0)) ptr++; ptr++; // skip past colon if (*ptr==0) return(FALSE); t.tm_min=atoi(ptr); while ((*ptr!=':')&&(*ptr!=0)) ptr++; ptr++; // skip past colon if (*ptr==0) return(FALSE); t.tm_sec=atoi(ptr); t.tm_year%=100; // 1996 is stored as 96, not 1996 t.tm_isdst=-1; // day_light savings info isn't available sec=(uint32)(mktime(&t)); if ((sint32)sec==-1) return(FALSE); // The next part of the time is OPTIONAL (+minutes) // first skip past the seconds while ((isdigit(*ptr))&&(*ptr!=0)) ptr++; if (*ptr==0) return(TRUE); // skip past any spaces while ((isspace(*ptr))&&(*ptr!=0)) ptr++; if (*ptr!='+') { //printf("\nNOPE ptr was '%s'\n",ptr); return(TRUE); } ptr++; if (*ptr==0) { //printf("\nPTR WAS 0\n"); return(TRUE); } minOffset=atol(ptr); //printf("\n\nAdding %d minutes!\n\n",minOffset); sec+=minOffset*60; // add the minutes as seconds return(TRUE); } // In addition to PrintTime & PrintDate there is the 'Print' function // which prints both in RFC 1123 format void Xtime::PrintTime(FILE *out) const { char string[80]; PrintTime(string); fprintf(out,"%s",string); } void Xtime::PrintTime(char *out) const { sprintf(out," %02lu:%02lu:%02lu",GetHour(),GetMinute(),GetSecond()); } void Xtime::PrintDate(FILE *out) const { char string[80]; PrintDate(string); fprintf(out,"%s",string); } void Xtime::PrintDate(char *out) const { sprintf(out,"%s, %lu %s %lu",DAYS[GetWDay()-1],GetMDay(),MONTHS[GetMonth()-1], GetYear()); } ********************************************/ // Get day_s since year 0 sint32 Xtime::getDay(void) const { return(day_); } // Get msecs since start of day sint32 Xtime::getMsec(void) const { return(msec_); } // Set days since year 0 void Xtime::setDay(sint32 newday) { day_=newday; } // Set msec since start of this day void Xtime::setMsec(sint32 newmsec) { msec_=newmsec; } // Set both void Xtime::set(sint32 newday, sint32 newmsec) { day_=newday; msec_=newmsec; } // // Get a timeval ptr from a Xtime class // May fail if timeval can't hold a year this big or small // bit8 Xtime::getTimeval(struct timeval &tv) { // A timeval can only hold dates from 1970-2038 if ((day_ < 719528) || (day_ >= 719528+24855)) return(FALSE); // Compute seconds since Jan 1, 1970 uint32 seconds=day_-719528; seconds*=(60*60*24); seconds+=(msec_/1000); tv.tv_sec=seconds; tv.tv_usec=(msec_%1000)*1000; return(TRUE); } // // Set the time // bit8 Xtime::setTime(int month, int mday, int year, int hour, int minute, int second) { day_=Get_Day(month,mday,year); msec_=(hour*1000*60*60)+(minute*1000*60)+(second*1000); return(TRUE); } int Xtime::getYDay(void) const // Day of Year (1-366) (366 = leap yr) { int year; sint32 dayofyear; if (Get_Date_From_Day(day_,year,dayofyear)==FALSE) return(-1); return dayofyear; } // // Get all the components of the time in the usual normalized format. // // Most of the uglyness is in Get_Date_From_Day() // bit8 Xtime::getTime(int &month, int &mday, int &year, int &hour, int &minute, int &second) const { int i; sint32 dayofyear; if (Get_Date_From_Day(day_,year,dayofyear)==FALSE) return(FALSE); static int DaysAtMonth[2][12] = { {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, // normal {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335} // leap year }; month=0; bit8 isleap=IS_LEAP(year); for (i=0; i<12; i++) { if (DaysAtMonth[isleap][i] >= dayofyear) break; month=i; } month++; // 1 based mday=dayofyear-DaysAtMonth[isleap][month-1]; // Whew! Now all we have to do is figure out H/M/S from the msec! hour=(msec_/3600000)%24; // 1000*60*60 minute=(msec_/60000)%60; // 1000*60 second=(msec_/ 1000)%60; // 1000 return(TRUE); } // // These are for getting components of the time in the // standard ranges. Like Day 1-31, Second 0-59, etc... // int Xtime::getSecond(void) const { int month,mday,year,hour,minute,second; getTime(month, mday, year, hour, minute, second); return(second); } int Xtime::getMinute(void) const { int month,mday,year,hour,minute,second; getTime(month, mday, year, hour, minute, second); return(minute); } int Xtime::getHour(void) const { int month,mday,year,hour,minute,second; getTime(month, mday, year, hour, minute, second); return(hour); } int Xtime::getMDay(void) const { int month,mday,year,hour,minute,second; getTime(month, mday, year, hour, minute, second); return(mday); } int Xtime::getMonth(void) const { int month,mday,year,hour,minute,second; getTime(month, mday, year, hour, minute, second); return(month); } // based at year 0 (real 0, not 1970) int Xtime::getYear(void) const { int month,mday,year,hour,minute,second; getTime(month, mday, year, hour, minute, second); return(year); } // // Set the seconds value (0-59) // bit8 Xtime::setSecond(sint32 sec) { sint32 second=(msec_/ 1000)%60; msec_-=(second*1000); msec_+=(sec*1000); return(TRUE); } // // Set the minutes value (0-59) // bit8 Xtime::setMinute(sint32 min) { sint32 minute=(msec_/60000)%60; // 1000*60 msec_-=(minute*60000); msec_+=(min*60000); return(TRUE); } // // Set the minutes value (0-23) // bit8 Xtime::setHour(sint32 hour) { hour=(msec_/3600000)%24; // 1000*60*60 msec_-=(hour*3600000); msec_+=(hour*3600000); return(TRUE); } // // Set the year value // Results are undefined if you're moving from Feb 29, to a non leap year // bit8 Xtime::setYear(sint32 _year) { // extract the date int month,mday,year,hour,min,sec; getTime(month,mday,year,hour,min,sec); // modify & rebuild year=_year; day_=Get_Day(month,mday,year); return(TRUE); } // // Modify the month // bit8 Xtime::setMonth(sint32 _month) { // extract the date int month,mday,year,hour,min,sec; getTime(month,mday,year,hour,min,sec); // modify & rebuild month=_month; day_=Get_Day(month,mday,year); return(TRUE); } // // Modify the day of the month // bit8 Xtime::setMDay(sint32 _mday) { // extract the date int month,mday,year,hour,min,sec; getTime(month,mday,year,hour,min,sec); // modify & rebuild mday=_mday; day_=Get_Day(month,mday,year); return(TRUE); } // // Compare two times. The time better be normalized // which it would be unless you've put bad stuff in it! // // 1 = *this > other //-1 = *this < other // 0 = *this == other int Xtime::compare(const Xtime &other) const { if ((day_==other.day_)&&(msec_==other.msec_)) return(0); // equal else if (day_>other.day_) return(1); else if (day_other.msec_) return(1); else return(-1); } bit8 Xtime::operator == ( const Xtime &other ) const { bit8 retval=compare(other); if (retval==0) return(TRUE); else return(FALSE); } bit8 Xtime::operator != ( const Xtime &other ) const { bit8 retval=compare(other); if (retval==0) return(FALSE); else return(TRUE); } bit8 Xtime::operator < ( const Xtime &other ) const { int retval=compare(other); if (retval==-1) return(TRUE); else return(FALSE); } bit8 Xtime::operator > ( const Xtime &other ) const { int retval=compare(other); if (retval==1) return(TRUE); else return(FALSE); } bit8 Xtime::operator <= ( const Xtime &other ) const { int retval=compare(other); if ((retval==-1)||(retval==0)) return(TRUE); else return(FALSE); } bit8 Xtime::operator >= ( const Xtime &other ) const { int retval=compare(other); if ((retval==1)||(retval==0)) return(TRUE); else return(FALSE); } Xtime &Xtime::operator += (const Xtime &other) { day_+=other.day_; msec_+=other.msec_; normalize(); return *this; } Xtime &Xtime::operator -= (const Xtime &other) { day_-=other.day_; msec_-=other.msec_; normalize(); return *this; } Xtime Xtime::operator - (Xtime &other) { Xtime temp(*this); temp-=other; return(temp); } Xtime Xtime::operator + (Xtime &other) { Xtime temp(*this); temp+=other; return(temp); } Xtime &Xtime::operator = (const Xtime &other) { day_=other.day_; msec_=other.msec_; return *this; } Xtime &Xtime::operator += (const time_t other) { addSeconds(other); return *this; } Xtime &Xtime::operator -= (const time_t other) { addSeconds(-((sint32)other)); return *this; } Xtime Xtime::operator - (time_t other) { Xtime temp(*this); temp-=other; return(temp); } Xtime Xtime::operator + (time_t other) { Xtime temp(*this); temp+=other; return(temp); } Xtime &Xtime::operator = (const time_t other) { msec_=0; day_=719528; // Jan 1, 1970 addSeconds(other); return *this; }