/****************************************************************************/ /* */ /* NewTime.h */ /* */ /****************************************************************************/ /****************************************************************************/ /* This file defines three new data types to express time and routines for */ /* converting time between these three different formats: */ /* 1. Time expressed as type 'Time_sec' (unsigned long). This is time */ /* in seconds since Jan 1,1900, 00:00. In 32 bit systems we can */ /* uniquely specify each second till 2036 Feb 6, 06:28:15. */ /* 2. Time expressed as structure 'Time_struct'. Components of this */ /* structure are: */ /* int tm_sec seconds after minute (0,61) */ /* int tm_min minutes after the hour (0,59) */ /* int tm_hour hours since midnight (0,23) */ /* int tm_mday day of the month (1,31) */ /* int tm_mon months since January (0,11) ! */ /* int tm_year years since 1900 */ /* 3. Time expressed as a string 'YYMMDDHHMMSS' or 'YYYYMMDDHHMMSS'. */ /* So the year may be expressed either with two or four digits. */ /* The string may be delimited with a '\0' or otherwise the number */ /* of characters must be specified. If some fields are not included */ /* then minimum values are assumed for missing fields. ('941007' is */ /* 1994 Oct 7th at 00:00:00). */ /* If the first two characters in the time string are '19' or '20' */ /* then it is assumed that these denote centuries and the year is */ /* is given with four characters. If the first two characters are */ /* not '19' or '20' then it is assumed that the year is given with */ /* two characters. This definition will be unique until year 2019. */ /* In years 2019 and 2020 the year number must be written with four */ /* digits. */ /* When writing the year with two characters it is not clear */ /* whether '280101' means 1928 Jan 1st or 2028 Jan 1st. Since we */ /* want to use these routines also in the 21st century we define */ /* the delimiting year as 1930. Thus 300101 is Jan 1st, 1930 but */ /* 291231 is 2029 Dec 31st. */ /* */ /* The definitions for Time_sec and Time_struct resemble those defined in */ /* the time.h file (time_t and struct tm). However, definition of time_t */ /* is system dependent (some define it as 'unsigned long' others 'long'). */ /* The file time.h also defines routines for converting time from one */ /* format to another. However, I have had a lot of troubles using these */ /* routines (especially with the daytime saving convention). Therefore I */ /* decided to define new data types for expressing time and write brand new */ /* routines for converting time between these formats. */ /* */ /* This file defines the following format manipulation routines: */ /* 1 -> 2 : void SecsToTm(Time_sec Secs,Time_struct *tp) */ /* 2 -> 1 : Time_sec TmToSecs(Time_struct *tp) */ /* 3 -> 1 : Time_sec StrToSecs(char *TimeStr, int Count) */ /* 1 -> 3 : void SecsToStr(Time_sec Secs, char *TimeStr) */ /* 2 -> 3 : void TmToStr(Time_struct *tp,char *TimeStr) */ /* 3 -> 2 : StrToTm(char *TimeStr, int Count, Time_struct *tp) */ /* In these routines Count is a parameter defining how many characters are */ /* included in the string TimeStr. If Count = 0, then all characters */ /* before '\0' are included. */ /****************************************************************************/ /****************************************************************************/ /* Lasse Hakkinen */ /* Finnish Meteorological Institute */ /* Department of Geophysics */ /* P.O.Box 503 */ /* FIN-00101, Helsinki, Finland */ /* e-mail: Lasse.Hakkinen@fmi.fi */ /* phone : (+358)-9-19294634 */ /* fax : (+358)-9-19294603 */ /* */ /* version 1.04 01.01.2019 */ /****************************************************************************/ /****************************************************************************/ /* Version history: */ /* */ /* 1.04 01.01.2019 Fixed the 19-century bug. */ /* 1.03 16.09.1999 Redefined the definitions for MAX_TIME and YEAR2000 so */ /* that gnu C compiler doesn't anymore give warnings about */ /* unsigned integers. */ /* 1.02 09.09.1999 Redefined the definition for writing the date in string */ /* format (see definition 3. above). The new definition */ /* allows to use either 2 or 4 characters for the year so */ /* that the centuries may be included. */ /* 1.01 02.07.1999 Fixed a milennium bug in TmToStr-routine which gave an */ /* incorrect result if year was >= 2000. (And I thought */ /* my routines were Y2K clean !!!). */ /* 1.0 13.11.1995 First official release */ /****************************************************************************/ #include /*--------------------------------------------------------------------------*/ /* Define time variable types */ /*--------------------------------------------------------------------------*/ typedef unsigned long Time_sec; typedef struct Tm { int tm_sec; /* seconds after minute (0,61) */ int tm_min; /* minutes after the hour (0,59) */ int tm_hour; /* hours since midnight (0,23) */ int tm_mday; /* day of the month (1,31) */ int tm_mon; /* months since January (0,11) ! */ int tm_year; /* years since 1900 */ } Time_struct; /*--------------------------------------------------------------------------*/ /* Define some constants */ /* Note: MIN_TIME = 1900 Jan 01, 00:00:00 */ /* MAX_TIME = 2036 Feb 07, 06:28:14 */ /*--------------------------------------------------------------------------*/ #define LimitYear 30 #define MIN_TIME (Time_sec) 0 #define MAX_TIME (Time_sec) 4294967295UL #define YEAR2000 (Time_sec) 3155673600UL /*--------------------------------------------------------------------------*/ /* Function prototypes */ /*--------------------------------------------------------------------------*/ void SecsToTm(Time_sec Secs,Time_struct *tp); Time_sec TmToSecs(Time_struct *tp); Time_sec StrToSecs(char *TimeStr,int Count); Time_sec StrToSecsC(char *TimeStr,int Count); Time_sec StrToSecsYY(char *TimeStr,int Count); void SecsToStr(Time_sec Secs, char *TimeStr); void SecsToStrC(Time_sec Secs, char *TimeStr); void TmToStr(Time_struct *tp,char *TimeStr); void TmToStrC(Time_struct *tp,char *TimeStr); void StrToTm(char *TimeStr, int Count, Time_struct *tp); static int TwoCharNumber(char *p); void DayNbrToTm(long Year, long DayNbr, long Hour, long Minute, long Second, Time_struct *tp); long GetDayNbr(Time_struct *tp); long GetWeekdayTm(Time_struct *tp); long GetWeekdayStr(char *DateStr); /*--------------------------------------------------------------------------*/ /* Get the integer corresponding to two ascii characters. Spaces are */ /* interpreted as '0'. */ /*--------------------------------------------------------------------------*/ static int TwoCharNumber(char *p) { register int a,b; a = (*p != ' ') ? *p-48 : 0; b = (*(p+1) != ' ') ? *(p+1)-48 : 0; return (10*a+b); } /*--------------------------------------------------------------------------*/ /* This routine converts a date given in seconds into years, months, days, */ /* hours, minutes and seconds which are fields in Time_struct. The zero */ /* point of the date is set to 1900 Jan 1, at 00:00. This routine will work */ /* only between the years 1900 to 2040. The algorithm is from the book: */ /* Jean Meeus, Astronomical Algorithms, p 59-66. */ /*--------------------------------------------------------------------------*/ void SecsToTm(Time_sec Secs, Time_struct *tp) { long Seconds; long alfa,B,C,D,E; long DayNbr; Seconds = ((Time_sec) Secs) % ((Time_sec) 86400); DayNbr = ((Time_sec) Secs) / ((Time_sec) 86400); tp->tm_hour = (int) (Seconds/3600); tp->tm_min = (int) ((Seconds % 3600)/60); tp->tm_sec = (int) (Seconds % 60); DayNbr += 2415021; /* 1900, Jan 1, 00:00 is Julian day 2415020.5 */ alfa = (long)((DayNbr-1867216.25)/36524.25); B = DayNbr+alfa-alfa/4+1525; C = (long)((B-122.1)/365.25); D = (long)(365.25*C); E = (long)((B-D)/30.6001); tp->tm_mday = B-D-(long)(30.6001*E); tp->tm_mon = (E<14) ? E-2 : E-14; tp->tm_year = (tp->tm_mon > 1) ? C-6616 : C-6615; } /*--------------------------------------------------------------------------*/ /* This routine converts a date given as Time_struct into seconds. The zero */ /* point of the date is set to 1900 Jan 1, at 00:00. This routine will work */ /* only between the years 1900 to 2040. The algorithm is from the book: */ /* Jean Meeus, Astronomical Algorithms, p 59-66. */ /*--------------------------------------------------------------------------*/ Time_sec TmToSecs(Time_struct *tp) { int Year = tp->tm_year+1900; int Month = tp->tm_mon+1; long A,B; Time_sec DayNbr; Time_sec Seconds; if (Month < 3) { Year -= 1; Month += 12; } A = Year/100; B = 2-A+A/4; DayNbr = (Time_sec)(365.25*(Year+4716)); DayNbr += (Time_sec)(30.6001*(Month+1)); DayNbr += (Time_sec)(tp->tm_mday) + B - (Time_sec) 2416545; Seconds = ((Time_sec) tp->tm_hour)*((Time_sec) 3600); Seconds += ((Time_sec) tp->tm_min)*((Time_sec) 60); Seconds += (Time_sec) tp->tm_sec; return (Seconds+((Time_sec) 86400)*DayNbr); } /*--------------------------------------------------------------------------*/ /* Convert a time string into seconds. The string's format is either */ /* YYMMDDHHMMSS or YYYYMMDDHHMMSS. So the year number may or may not include*/ /* century. The Count parameter specifies how many characters are included */ /* from the time string (centuries (=two first characters) are neglected if */ /* they are given. If Count = 0 all characters before '\0' are taken. The */ /* routine does not check the correctness of the TimeStr or the value of */ /* Count. If the year is given with only two characters (YY) we define */ /* 300101 to be Jan 1st, 1930 but 291231 is Dec 31st, 2029. */ /* */ /* This function is not used anymore anywere. The code which wants to do the*/ /* must know whether YYYY or YY is used and then call StrToSecsC or */ /* StrToSecsYY accordingly. */ /*--------------------------------------------------------------------------*/ Time_sec StrToSecs(char *TimeStr,int Count) { Time_struct MyTime = {0,0,0,1,0,0}; /* {secs,mins,...,years} */ char *YYStr = TimeStr; /* Pointer to two last chars of the year string */ long YY; long MM; long Centuries = 0; if (Count == 0) Count = strlen(TimeStr); /* Check for the centuries */ YY = TwoCharNumber(TimeStr); MM = TwoCharNumber(TimeStr+2); if ((YY == 19) && (MM > 30)) { /* Centuries included */ Centuries = 1; } if (YY == 20) { if (MM > 12) { Centuries = 1; } // Does 20050101 mean 2005-01-01 or 2020-05-01 // else if (Count >= 8) { // This is not good ! Only temporary fix to get GetIMAGEdata function correctly // Centuries = 1; // } } if (Centuries == 1) { YYStr = TimeStr+2; Count -= 2; } switch (Count) { /* No breaks here ! */ case 12 : MyTime.tm_sec = TwoCharNumber(YYStr+10); case 10 : MyTime.tm_min = TwoCharNumber(YYStr+8); case 8 : MyTime.tm_hour = TwoCharNumber(YYStr+6); case 6 : MyTime.tm_mday = TwoCharNumber(YYStr+4); case 4 : MyTime.tm_mon = TwoCharNumber(YYStr+2); case 2 : MyTime.tm_year = TwoCharNumber(YYStr); } if (MyTime.tm_year < LimitYear) MyTime.tm_year += 100; MyTime.tm_mon--; /* Months start from 0 */ return TmToSecs(&MyTime); } /*--------------------------------------------------------------------------*/ /* Convert a time string into seconds. The string format is YYYYMMDDHHMMSS. */ /* So the year number includes century. The Count parameter specifies how */ /* many characters are included from the time string. If Count = 0 all */ /* characters before '\0' are taken. The routine does not check the */ /* correctness of the TimeStr or the value of Count. */ /*--------------------------------------------------------------------------*/ Time_sec StrToSecsC(char *TimeStr,int Count) { Time_struct MyTime = {0,0,0,1,0,0}; /* {secs,mins,...,years} */ long Century; if (Count == 0) Count = strlen(TimeStr); Century = TwoCharNumber(TimeStr); if ((Century < 19)) Century = 19; switch (Count) { /* No breaks here ! */ case 14 : MyTime.tm_sec = TwoCharNumber(TimeStr+12); case 12 : MyTime.tm_min = TwoCharNumber(TimeStr+10); case 10 : MyTime.tm_hour = TwoCharNumber(TimeStr+8); case 8 : MyTime.tm_mday = TwoCharNumber(TimeStr+6); case 6 : MyTime.tm_mon = TwoCharNumber(TimeStr+4); case 4 : MyTime.tm_year = TwoCharNumber(TimeStr+2); } if (Century >= 20) MyTime.tm_year += 100; MyTime.tm_mon--; /* Months start from 0 */ return TmToSecs(&MyTime); } /*--------------------------------------------------------------------------*/ /* Convert a time string into seconds. The string format is YYMMDDHHMMSS. */ /* So the year number does not include century. The Count parameter */ /* specifies how many characters are included from the time string. If */ /* Count = 0 all characters before '\0' are taken. The routine does not */ /* check the correctness of the TimeStr or the value of Count. */ /*--------------------------------------------------------------------------*/ Time_sec StrToSecsYY(char *TimeStr,int Count) { Time_struct MyTime = {0,0,0,1,0,0}; /* {secs,mins,...,years} */ long Year; if (Count == 0) Count = strlen(TimeStr); Year = TwoCharNumber(TimeStr); switch (Count) { /* No breaks here ! */ case 14 : MyTime.tm_sec = TwoCharNumber(TimeStr+10); case 12 : MyTime.tm_min = TwoCharNumber(TimeStr+8); case 10 : MyTime.tm_hour = TwoCharNumber(TimeStr+6); case 8 : MyTime.tm_mday = TwoCharNumber(TimeStr+4); case 6 : MyTime.tm_mon = TwoCharNumber(TimeStr+2); case 4 : MyTime.tm_year = TwoCharNumber(TimeStr); } if (Year < 70) MyTime.tm_year += 100; MyTime.tm_mon--; /* Months start from 0 */ return TmToSecs(&MyTime); } /*--------------------------------------------------------------------------*/ /* Convert Time_struct into time string. String's format is YYMMDDHHMMSS. */ /*--------------------------------------------------------------------------*/ void TmToStr(Time_struct *tp,char *TimeStr) { if (tp->tm_year >= 100) sprintf(TimeStr,"%02d",tp->tm_year-100); else sprintf(TimeStr,"%02d",tp->tm_year); sprintf(TimeStr+2 ,"%02d",tp->tm_mon+1); sprintf(TimeStr+4 ,"%02d",tp->tm_mday); sprintf(TimeStr+6 ,"%02d",tp->tm_hour); sprintf(TimeStr+8 ,"%02d",tp->tm_min); sprintf(TimeStr+10,"%02d",tp->tm_sec); } /*--------------------------------------------------------------------------*/ /* Convert Time_struct into time string. String's format is YYYYMMDDHHMMSS. */ /* Centuries are included. */ /*--------------------------------------------------------------------------*/ void TmToStrC(Time_struct *tp,char *TimeStr) { sprintf(TimeStr,"%04d",tp->tm_year+1900); sprintf(TimeStr+4 ,"%02d",tp->tm_mon+1); sprintf(TimeStr+6 ,"%02d",tp->tm_mday); sprintf(TimeStr+8 ,"%02d",tp->tm_hour); sprintf(TimeStr+10 ,"%02d",tp->tm_min); sprintf(TimeStr+12,"%02d",tp->tm_sec); } /*--------------------------------------------------------------------------*/ /* Convert seconds into a time string. The string's format is YYMMDDHHMMSS. */ /*--------------------------------------------------------------------------*/ void SecsToStr(Time_sec Secs, char *TimeStr) { Time_struct MyTime; SecsToTm(Secs,&MyTime); TmToStr(&MyTime,TimeStr); } /*----------------------------------------------------------------------------*/ /* Convert seconds into a time string. The string's format is YYYYMMDDHHMMSS. */ /*----------------------------------------------------------------------------*/ void SecsToStrC(Time_sec Secs, char *TimeStr) { Time_struct MyTime; SecsToTm(Secs,&MyTime); TmToStrC(&MyTime,TimeStr); } /*--------------------------------------------------------------------------*/ /* Convert a time string into Time_struct. The string's format is */ /* YYYYMMDDHHMMSS. So the year number includes century. */ /* The Count parameter specifies how many characters are included */ /* from the time string (centuries (=two first characters) are neglected if */ /* they are given. If Count = 0 all characters before '\0' are taken. The */ /* routine does not check the correctness of the TimeStr or the value of */ /* Count. If the year is given with only two characters (YY) we define */ /* 300101 to be Jan 1st, 1930 but 291231 is Dec 31st, 2029. */ /*--------------------------------------------------------------------------*/ void StrToTm(char *TimeStr, int Count, Time_struct *tp) { SecsToTm(StrToSecsC(TimeStr,Count),tp); } /*--------------------------------------------------------------------------*/ /* This routine converts a date given as year, day of year, hour, minute, */ /* second into a Time_struct format. */ /*--------------------------------------------------------------------------*/ void DayNbrToTm(long Year, long DayNbr, long Hour, long Minute, long Second, Time_struct *tp) { Time_sec Secs; tp->tm_year = Year-1900; tp->tm_mon = 0; tp->tm_mday = DayNbr; tp->tm_hour = Hour; tp->tm_min = Minute; tp->tm_sec = Second; Secs = TmToSecs(tp); SecsToTm(Secs,tp); } /*--------------------------------------------------------------------------*/ /* This routine computes the day of year from given Time_struct variable. */ /* This routine will work only between the years 1900 to 2040. The */ /* algorithm is from the book: Jean Meeus, Astronomical Algorithms, p 65. */ /*--------------------------------------------------------------------------*/ long GetDayNbr(Time_struct *tp) { long K,M; if ((tp->tm_year % 4) == 0) K = 1; else K = 2; M = tp->tm_mon + 1; return((275*M)/9 - K*((M+9)/12) + tp->tm_mday - 30); } /*--------------------------------------------------------------------------*/ /* This routine returns the weekday index of given Time_struct variable. */ /* Indices are: sunday = 0, ... , saturday = 6. */ /*--------------------------------------------------------------------------*/ long GetWeekdayTm(Time_struct *tp) { return ((TmToSecs(tp)/86400 + 1) % 7); } long GetWeekdayStr(char *DateStr) { return ((StrToSecs(DateStr,0)/86400 + 1) % 7); }