1 //
2 // HtDateTime.cc
3 //
4 // HtDateTime: Parse, split, compare and format dates and times.
5 //  	       Uses locale.
6 //
7 // Part of the ht://Dig package   <http://www.htdig.org/>
8 // Copyright (c) 1999-2004 The ht://Dig Group
9 // For copyright details, see the file COPYING in your distribution
10 // or the GNU Library General Public License (LGPL) version 2 or later
11 // <http://www.gnu.org/copyleft/lgpl.html>
12 //
13 // $Id: HtDateTime.cc,v 1.20 2004/05/28 13:15:20 lha Exp $
14 //
15 
16 #ifdef HAVE_CONFIG_H
17 #include "htconfig.h"
18 #endif /* HAVE_CONFIG_H */
19 
20 #include "HtDateTime.h"
21 
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ctype.h>
25 
26 #ifdef HAVE_STD
27 #include <iostream>
28 #ifdef HAVE_NAMESPACES
29 using namespace std;
30 #endif
31 #else
32 #include <iostream.h>
33 #endif /* HAVE_STD */
34 
35 #ifndef HAVE_STRPTIME
36 // mystrptime() declared in lib.h, defined in htlib/strptime.cc
37 #define strptime(s,f,t)	mystrptime(s,f,t)
38 #else /* HAVE_STRPTIME */
39 #ifndef HAVE_STRPTIME_DECL
40 extern "C" {
41 extern char *strptime(const char *__s, const char *__fmt, struct tm *__tp);
42 }
43 #endif /* HAVE_STRPTIME_DECL */
44 #endif /* HAVE_STRPTIME */
45 
46 ///////
47    //    Static local variable : Visible only here !!!
48 ///////
49 
50 #define MAXSTRTIME 256  // Max length of my_strtime
51 
52 static struct tm Ht_tm;
53 static char my_strtime[MAXSTRTIME];
54 
55 
56 ///////
57   //     Recognized Date Formats
58 ///////
59 
60 //     RFC1123: Sun, 06 Nov 1994 08:49:37 GMT
61 #define RFC1123_FORMAT "%a, %d %b %Y %H:%M:%S %Z"
62 #define LOOSE_RFC1123_FORMAT "%d %b %Y %H:%M:%S"
63 
64 //     RFC850 : Sunday, 06-Nov-94 08:49:37 GMT
65 #define RFC850_FORMAT  "%A, %d-%b-%y %H:%M:%S %Z"
66 #define LOOSE_RFC850_FORMAT  "%d-%b-%y %H:%M:%S"
67 
68 //     ANSI C's asctime() format : Sun Nov  6 08:49:37 1994
69 #define ASCTIME_FORMAT  "%a %b %e %H:%M:%S %Y"
70 #define LOOSE_ASCTIME_FORMAT  "%b %e %H:%M:%S %Y"
71 
72 // 	  ISO8601 : 1994-11-06 08:49:37 GMT
73 #define ISO8601_FORMAT "%Y-%m-%d %H:%M:%S %Z"
74 
75 // 	  ISO8601 (short version): 1994-11-06
76 #define ISO8601_SHORT_FORMAT "%Y-%m-%d"
77 
78 // 	  Timestamp : 19941106084937
79 #define TIMESTAMP_FORMAT "%Y%m%d%H%M%S"
80 
81 
82 
83 ///////
84    //   Initialization
85 ///////
86 
87 const int HtDateTime::days[] = {	31, 28, 31, 30, 31, 30,
88 				31, 31, 30, 31, 30, 31};
89 
90 
91 ///////   //   Input Formats   //   ///////
92 
93 ///////
94    //   Generalized date/time parser for "LOOSE" formats
95    //   - converts LOOSE RFC850 or RFC1123 date string into a time value
96    //   - converts SHORT ISO8601 date string into a time value
97    //   - autodetects which of these formats is used
98    //   - assumes midnight if time portion omitted
99    //   We've had problems using strptime() and timegm() on a few platforms
100    //   while parsing these formats, so this is an attempt to sidestep them.
101    //
102    //	Returns 0 if parsing failed, or returns number of characters parsed
103    //	in date string otherwise, and sets Ht_t field to time_t value.
104 ///////
105 #define EPOCH	1970
106 
Parse(const char * date)107 int HtDateTime::Parse(const char *date)
108 {
109     register const char *s;
110     register const char *t;
111     int		day, month, year, hour, minute, second;
112 
113     //
114     // Three possible time designations:
115     //      Tuesday, 01-Jul-97 16:48:02 GMT     (RFC850)
116     // or
117     //      Thu, 01 May 1997 00:40:42 GMT       (RFC1123)
118     // or
119     //      1997-05-01 00:40:42 GMT             (ISO8601)
120     //
121     // We strip off the weekday because we don't need it, and
122     // because some servers send invalid weekdays!
123     // (Some don't even send a weekday, but we'll be flexible...)
124 
125     s = date;
126     while (*s && *s != ',')
127 	s++;
128     if (*s)
129 	s++;
130     else
131 	s = date;
132     while (isspace(*s))
133 	s++;
134 
135     // check for ISO8601 format
136     month = 0;
137     t = s;
138     while (isdigit(*t))
139 	t++;
140     if (t > s && *t == '-' && isdigit(t[1]))
141 	day = -1;
142     else {
143 	// not ISO8601, so try RFC850 or RFC1123
144 	// get day...
145 	if (!isdigit(*s))
146 	    return 0;
147 	day = 0;
148 	while (isdigit(*s))
149 	    day = day * 10 + (*s++ - '0');
150 	if (day > 31)
151 	    return 0;
152 	while (*s == '-' || isspace(*s))
153 	    s++;
154 
155 	// get month...
156 	// (it's ugly, but it works)
157 	switch (*s++) {
158 	    case 'J': case 'j':
159 		switch (*s++) {
160 		case 'A': case 'a':
161 		    month = 1;
162 		    s++;
163 		    break;
164 		case 'U': case 'u':
165 		    switch (*s++) {
166 		    case 'N': case 'n':
167 			month = 6;
168 			break;
169 		    case 'L': case 'l':
170 			month = 7;
171 			break;
172 		    default:
173 			return 0;
174 		    }
175 		    break;
176 		default:
177 		    return 0;
178 		}
179 		break;
180 	    case 'F': case 'f':
181 		month = 2;
182 		s += 2;
183 		break;
184 	    case 'M': case 'm':
185 		switch (*s++) {
186 		case 'A': case 'a':
187 		    switch (*s++) {
188 		    case 'R': case 'r':
189 			month = 3;
190 			break;
191 		    case 'Y': case 'y':
192 			month = 5;
193 			break;
194 		    default:
195 			return 0;
196 		    }
197 		    break;
198 		default:
199 		    return 0;
200 		}
201 		break;
202 	    case 'A': case 'a':
203 		switch (*s++) {
204 		case 'P': case 'p':
205 		    month = 4;
206 		    s++;
207 		    break;
208 		case 'U': case 'u':
209 		    month = 8;
210 		    s++;
211 		    break;
212 		default:
213 		    return 0;
214 		}
215 		break;
216 	    case 'S': case 's':
217 		month = 9;
218 		s += 2;
219 		break;
220 	    case 'O': case 'o':
221 		month = 10;
222 		s += 2;
223 		break;
224 	    case 'N': case 'n':
225 		month = 11;
226 		s += 2;
227 		break;
228 	    case 'D': case 'd':
229 		month = 12;
230 		s += 2;
231 		break;
232 	    default:
233 		return 0;
234 	}
235 	while (*s == '-' || isspace(*s))
236 	    s++;
237     }
238 
239     // get year...
240     if (!isdigit(*s))
241 	return 0;
242     year = 0;
243     while (isdigit(*s))
244 	year = year * 10 + (*s++ - '0');
245     if (year < 69)
246 	year += 2000;
247     else if (year < 1900)
248 	year += 1900;
249     else if (year >= 19100)	// seen some programs do it, why not check?
250 	year -= (19100-2000);
251     while (*s == '-' || isspace(*s))
252 	s++;
253 
254     if (day < 0) {		// still don't have day, so it's ISO8601 format
255 	// get month...
256 	if (!isdigit(*s))
257 	    return 0;
258 	month = 0;
259 	while (isdigit(*s))
260 	    month = month * 10 + (*s++ - '0');
261 	if (month < 1 || month > 12)
262 	    return 0;
263 	while (*s == '-' || isspace(*s))
264 	    s++;
265 
266 	// get day...
267 	if (!isdigit(*s))
268 	    return 0;
269 	day = 0;
270 	while (isdigit(*s))
271 	    day = day * 10 + (*s++ - '0');
272 	if (day < 1 || day > 31)
273 	    return 0;
274 	while (*s == '-' || isspace(*s))
275 	    s++;
276     }
277 
278     // optionally get hour...
279     hour = 0;
280     while (isdigit(*s))
281 	hour = hour * 10 + (*s++ - '0');
282     if (hour > 23)
283 	return 0;
284     while (*s == ':' || isspace(*s))
285 	s++;
286 
287     // optionally get minute...
288     minute = 0;
289     while (isdigit(*s))
290 	minute = minute * 10 + (*s++ - '0');
291     if (minute > 59)
292 	return 0;
293     while (*s == ':' || isspace(*s))
294 	s++;
295 
296     // optionally get second...
297     second = 0;
298     while (isdigit(*s))
299 	second = second * 10 + (*s++ - '0');
300     if (second > 59)
301 	return 0;
302     while (*s == ':' || isspace(*s))
303 	s++;
304 
305     // Assign the new value to time_t field
306     //
307     // Calculate date as seconds since 01 Jan 1970 00:00:00 GMT
308     // This is based somewhat on the date calculation code in NetBSD's
309     // cd9660_node.c code, for which I was unable to find a reference.
310     // It works, though!
311     //
312     Ht_t = (time_t) (((((367L*year - 7L*(year+(month+9)/12)/4
313 				   - 3L*(((year)+((month)+9)/12-1)/100+1)/4
314 				   + 275L*(month)/9 + day) -
315 			(367L*EPOCH - 7L*(EPOCH+(1+9)/12)/4
316 				   - 3L*((EPOCH+(1+9)/12-1)/100+1)/4
317 				   + 275L*1/9 + 1))
318 		       * 24 + hour) * 60 + minute) * 60 + second);
319 
320     // cerr << "Date string '" << date << "' converted to time_t "
321     //      << (int)Ht_t << ", used " << (s-date) << " characters\n";
322 
323     return s-date;
324 }
325 
326 ///////
327    //   Personalized format such as C strftime function
328   //     Overloaded version 1
329    //    It ignores, for now, Time Zone values
330 ///////
331 
SetFTime(const char * buf,const char * format)332 char *HtDateTime::SetFTime(const char *buf, const char *format)
333 {
334 
335    register char *p;
336    register int r;
337 
338    ToGMTime();    // This must be set cos strptime always stores in GM
339 
340    p = (char *) buf;
341    if (*format == '%')		// skip any unexpected white space
342       while (isspace(*p))
343 	 p++;
344 
345    // Special handling for LOOSE/SHORT formats...
346    if ((strcmp((char *) format, LOOSE_RFC850_FORMAT) == 0 ||
347 	strcmp((char *) format, LOOSE_RFC1123_FORMAT) == 0 ||
348 	strcmp((char *) format, ISO8601_SHORT_FORMAT) == 0)  &&
349        (r = Parse(p)) > 0)
350 	    return p+r;
351 
352    p = (char *) strptime (p, (char *) format, & Ht_tm);
353 
354 #ifdef TEST_HTDATETIME
355 //   ViewStructTM(& Ht_tm);
356 #endif
357 
358    // Assign the new value to time_t value
359    SetDateTime(Ht_tm);
360 
361    return p;
362 
363 }
364 
365 
366 ///////
367    //   C asctime() standard format
368 ///////
369 
SetAscTime(char * s)370 void HtDateTime::SetAscTime(char *s)
371 {
372 
373    // Unfortunately, I cannot think of an easy test to
374    // see if we have a weekday *FIX*
375    SetFTime(s, ASCTIME_FORMAT);
376 
377 }
378 
379 ///////
380    //   RFC1123 standard Date format
381  //     Sun, 06 Nov 1994 08:49:37 GMT
382 ///////
383 
SetRFC1123(char * s)384 void HtDateTime::SetRFC1123(char *s)
385 {
386 
387    // abbreviated weekday name;
388    // day of the month;
389    // abbreviated month name;
390    // year as ccyy;
391    // hour ( 00 - 23);
392    // minute ( 00 - 59);
393    // seconds ( 00 - 59);
394    // time zone name;
395 
396    // First, if we have it, strip off the weekday
397    char *stripped;
398    stripped = strchr(s, ',');
399    if (stripped)
400         stripped++;
401    else
402         stripped = s;
403 
404    SetFTime(stripped, LOOSE_RFC1123_FORMAT);
405 
406 }
407 
408 
409 ///////
410    //   RFC850 standard Date format
411  //     Sunday, 06-Nov-1994 08:49:37 GMT
412 ///////
413 
SetRFC850(char * s)414 void HtDateTime::SetRFC850(char *s)
415 {
416 
417    // weekday name;
418    // day of the month;
419    // abbreviated month name;
420    // year within century;
421    // hour ( 00 - 23);
422    // minute ( 00 - 59);
423    // seconds ( 00 - 59);
424    // time zone name;
425 
426    // First, if we have it, strip off the weekday
427    char *stripped;
428    stripped = strchr(s, ',');
429    if (stripped)
430         stripped++;
431    else
432         stripped = s;
433 
434    SetFTime(stripped, LOOSE_RFC850_FORMAT);
435 
436 }
437 
438 
439 ///////
440    //   ISO8601 standard Date format
441  //     1994-11-06 08:49:37 GMT
442 ///////
443 
SetISO8601(char * s)444 void HtDateTime::SetISO8601(char *s)
445 {
446 
447    // year as ccyy;
448    // month ( 01 - 12)
449    // day of the month
450    // hour ( 00 - 23)
451    // minute ( 00 - 59)
452    // seconds ( 00 - 59);
453    // time zone name;
454 
455    SetFTime(s, ISO8601_FORMAT);
456 
457 }
458 
459 
460 ///////
461    //   Timestamp Date format (MySQL) without timezone
462  //     19941106084937
463 ///////
464 
SetTimeStamp(char * s)465 void HtDateTime::SetTimeStamp(char *s)
466 {
467 
468    // year as ccyy;
469    // month ( 01 - 12)
470    // day of the month
471    // hour ( 00 - 23)
472    // minute ( 00 - 59)
473    // seconds ( 00 - 59);
474 
475    SetFTime(s, TIMESTAMP_FORMAT);
476 
477 }
478 
479 
480 ///////
481    //   Default date and time format for the locale
482 ///////
483 
SetDateTimeDefault(char * s)484 void HtDateTime::SetDateTimeDefault(char *s)
485 {
486 
487    SetFTime(s, "%c");
488 
489 }
490 
491 
492 
493 
494 ///////   //   Output Formats   //   ///////
495 
496 
497 ///////
498    //   Personalized format such as C strftime function
499   //     Overloaded version 1
500 ///////
501 
GetFTime(char * s,size_t max,const char * format) const502 size_t HtDateTime::GetFTime(char *s, size_t max, const char *format) const
503 {
504    // Refresh static struct tm variable
505 
506    RefreshStructTM();
507 
508    return strftime(s, max, format, & Ht_tm);
509 
510 }
511 
512 ///////
513    //   Personalized format such as C strftime function
514   //     Overloaded version 2 - The best to be used outside
515    //       	   	       	  for temporary uses
516 ///////
517 
GetFTime(const char * format) const518 char *HtDateTime::GetFTime(const char *format) const
519 {
520 
521    // Invoke GetFTime overloaded method
522 
523    if(GetFTime(my_strtime, MAXSTRTIME, format))
524       return (char *)my_strtime;
525    else return 0;
526 
527 }
528 
529 ///////
530    //   RFC1123 standard Date format
531  //     Sun, 06 Nov 1994 08:49:37 GMT
532 ///////
533 
GetRFC1123() const534 char *HtDateTime::GetRFC1123() const
535 {
536 
537    // abbreviated weekday name;
538    // day of the month;
539    // abbreviated month name;
540    // year as ccyy;
541    // hour ( 00 - 23);
542    // minute ( 00 - 59);
543    // seconds ( 00 - 59);
544    // time zone name;
545 
546    GetFTime(my_strtime, MAXSTRTIME, RFC1123_FORMAT);
547 
548    return (char *)my_strtime;
549 
550 }
551 
552 ///////
553    //   RFC850 standard Date format
554  //     Sunday, 06-Nov-94 08:49:37 GMT
555 ///////
556 
GetRFC850() const557 char *HtDateTime::GetRFC850() const
558 {
559 
560    // full weekday name
561    // day of the month
562    // abbreviated month name
563    // year within century ( 00 - 99 )
564    // hour ( 00 - 23)
565    // minute ( 00 - 59)
566    // seconds ( 00 - 59);
567    // time zone name;
568 
569    GetFTime(my_strtime, MAXSTRTIME, RFC850_FORMAT);
570 
571    return (char *)my_strtime;
572 
573 }
574 
575 ///////
576    //   C asctime() standard format
577 ///////
578 
GetAscTime() const579 char *HtDateTime::GetAscTime() const
580 {
581 
582    GetFTime(my_strtime, MAXSTRTIME, ASCTIME_FORMAT);
583    return (char *)my_strtime;
584 
585 }
586 
587 ///////
588    //   ISO8601 standard Date format
589  //     1994-11-06 08:49:37 GMT
590 ///////
591 
GetISO8601() const592 char *HtDateTime::GetISO8601() const
593 {
594 
595    // year as ccyy;
596    // month ( 01 - 12)
597    // day of the month
598    // hour ( 00 - 23)
599    // minute ( 00 - 59)
600    // seconds ( 00 - 59);
601    // time zone name;
602 
603    GetFTime(my_strtime, MAXSTRTIME, ISO8601_FORMAT);
604 
605    return (char *)my_strtime;
606 
607 }
608 
609 ///////
610    //   ISO8601 standard Date format
611  //     1994-11-06 08:49:37 GMT
612 ///////
613 
GetShortISO8601() const614 char *HtDateTime::GetShortISO8601() const
615 {
616 
617    // year as ccyy;
618    // month ( 01 - 12)
619    // day of the month
620 
621    GetFTime(my_strtime, MAXSTRTIME, ISO8601_SHORT_FORMAT);
622 
623    return (char *)my_strtime;
624 
625 }
626 
627 ///////
628    //   Timestamp Date format (MySQL) without timezone
629  //     19941106084937
630 ///////
631 
GetTimeStamp() const632 char *HtDateTime::GetTimeStamp() const
633 {
634 
635    // year as ccyy;
636    // month ( 01 - 12)
637    // day of the month
638    // hour ( 00 - 23)
639    // minute ( 00 - 59)
640    // seconds ( 00 - 59);
641 
642    GetFTime(my_strtime, MAXSTRTIME, TIMESTAMP_FORMAT);
643 
644    return (char *)my_strtime;
645 
646 }
647 
648 ///////
649    //   Default date and time format for the locale
650 ///////
651 
GetDateTimeDefault() const652 char *HtDateTime::GetDateTimeDefault() const
653 {
654 
655    GetFTime(my_strtime, MAXSTRTIME, "%c");
656 
657    return (char *)my_strtime;
658 
659 }
660 
661 ///////
662    //   Default date format for the locale
663 ///////
664 
GetDateDefault() const665 char *HtDateTime::GetDateDefault() const
666 {
667 
668    GetFTime(my_strtime, MAXSTRTIME, "%x");
669 
670    return (char *)my_strtime;
671 
672 }
673 
674 ///////
675    //   Default time format for the locale
676 ///////
677 
GetTimeDefault() const678 char *HtDateTime::GetTimeDefault() const
679 {
680 
681    GetFTime(my_strtime, MAXSTRTIME, "%X");
682 
683    return (char *)my_strtime;
684 
685 }
686 
687 
688 
689 ///////
690    //   Set the static struct tm depending on localtime status
691 ///////
692 
693 
RefreshStructTM() const694 void HtDateTime::RefreshStructTM() const
695 {
696 
697    if(local_time)
698       // Setting localtime
699    	 memcpy(& Ht_tm, localtime(&Ht_t), sizeof(struct tm));
700    else
701    	 // Setting UTC or GM time
702    	 memcpy(& Ht_tm , gmtime(&Ht_t), sizeof(struct tm));
703 
704 }
705 
706 
707 // Set the date time from a struct tm pointer
708 
SetDateTime(struct tm * ptm)709 void HtDateTime::SetDateTime(struct tm *ptm)
710 {
711 
712    if(local_time)
713    	 Ht_t = mktime(ptm);  	// Invoke mktime
714    else
715    	 Ht_t = HtTimeGM(ptm);	// Invoke timegm alike function
716 
717 }
718 
719 
720 // Set time to now
721 
SettoNow()722 void HtDateTime::SettoNow()
723 {
724    Ht_t = time(0);
725 }
726 
727 
728 // Sets date by passing specific values
729 // The values are reffered to the GM date time
730 // Return false if failed
731 
SetGMDateTime(int year,int mon,int mday,int hour,int min,int sec)732 bool HtDateTime::SetGMDateTime ( int year, int mon, int mday,
733 			int hour, int min, int sec)
734 {
735    struct tm tm_tmp;
736 
737    // Year
738 
739    if ( ! isAValidYear (year) ) return false;
740 
741    if( year < 100)
742 	 year=Year_From2To4digits (year); // For further checks it's converted
743 
744    // Assigning the year
745 
746    tm_tmp.tm_year=year-1900;
747 
748 
749    // Month
750 
751    if( ! isAValidMonth(mon) ) return false;
752 
753    tm_tmp.tm_mon=mon-1; // Assigning the month to the structure
754 
755 
756    // Day
757 
758    if ( ! isAValidDay ( mday, mon, year ) ) return false;
759 
760    tm_tmp.tm_mday=mday; // Assigning the day of the month
761 
762 
763 
764    if(hour >= 0 && hour < 24) tm_tmp.tm_hour = hour;
765    	 else return false;
766 
767    if(min >= 0 && min < 60) tm_tmp.tm_min = min;
768    	 else return false;
769 
770    if(sec >= 0 && sec < 60) tm_tmp.tm_sec = sec;
771    	 else return false;
772 
773    tm_tmp.tm_yday = 0; // day of the year (to be ignored)
774    tm_tmp.tm_isdst = 0; // default for GM (to be ignored)
775 
776    // Now we are going to insert the new values as time_t value
777    // This can only be done using GM Time and so ...
778 
779    if (isLocalTime())
780    {
781    	 ToGMTime(); 	       // Change to GM Time
782    	 SetDateTime(&tm_tmp); // commit it
783 	 ToLocalTime();       // And then return to Local Time
784    }
785    else SetDateTime(&tm_tmp); // only commit it
786 
787    return true;
788 
789 }
790 
791 
792 ///////
793    //   Gets a struct tm from the value stored in the object
794   //    It's a protected method. Not visible outside the class
795 ///////
796 
GetStructTM() const797 struct tm &HtDateTime::GetStructTM() const
798 {
799 	RefreshStructTM(); // refresh it
800 
801 	return Ht_tm;
802 }
803 
804 
GetGMStructTM() const805 struct tm &HtDateTime::GetGMStructTM() const
806 {
807    GetGMStructTM (Ht_tm);
808    return Ht_tm;
809 }
810 
GetGMStructTM(struct tm & t) const811 void HtDateTime::GetGMStructTM(struct tm & t) const
812 {
813    // Directly gets gmtime value
814    memcpy(& t , gmtime(& Ht_t), sizeof(struct tm));
815 }
816 
817 
818 ///////
819    //   Is a leap year?
820 ///////
821 
LeapYear(int y)822 bool HtDateTime::LeapYear (int y)
823 {
824 
825    if(y % 400 == 0 || ( y % 100 != 0 && y % 4 == 0))
826    	 return true; 	// a leap year
827    else
828    	 return false;	// and not
829 }
830 
831 
832 ///////
833    //   Is a valid year number?
834 ///////
835 
isAValidYear(int y)836 bool HtDateTime::isAValidYear (int y)
837 {
838 
839    if(y >= 1970 && y < 2069) return true; // simple check and most likely
840 
841    if(y >= 0 && y < 100) return true;  // 2 digits year number
842 
843    return false;
844 
845 }
846 
847 
848 ///////
849    //   Is a valid month number?
850 ///////
851 
isAValidMonth(int m)852 bool HtDateTime::isAValidMonth (int m)
853 {
854 
855    if( m >= 1 && m <= 12) return true;
856    else return false;
857 
858 }
859 
860 
861 ///////
862    //   Is a valid day?
863 ///////
864 
isAValidDay(int d,int m,int y)865 bool HtDateTime::isAValidDay (int d, int m, int y)
866 {
867 
868    if ( ! isAValidYear (y) ) return false;	// Checks for the year
869 
870    if ( ! isAValidMonth (m) ) return false;	// Checks for the month
871 
872    if(m == 2)
873    {
874 
875    	 // Expands the 2 digits year number
876    	 if ( y < 100 ) y=Year_From2To4digits(y);
877 
878 	 if ( LeapYear (y) ) // Checks for the leap year
879       {
880 	    if (d >= 1 && d <= 29) return true;
881 	    else return false;
882       }
883    }
884 
885    // Acts as default
886 
887    if (d >= 1 && d <= days [m -1]) return true;
888    else return false;
889 
890 }
891 
892 
893 ///////
894    //   Comparison methods
895 ///////
896 
897 
DateTimeCompare(const HtDateTime & right) const898 int HtDateTime::DateTimeCompare (const HtDateTime & right) const
899 {
900    int result;
901 
902    // Let's compare the date
903 
904    result=DateCompare(right);
905 
906    if(result) return result;
907 
908    // Same date. Let's compare the time
909 
910    result=TimeCompare(right);
911 
912    return result; // Nothing more to check
913 
914 }
915 
916 
GMDateTimeCompare(const HtDateTime & right) const917 int HtDateTime::GMDateTimeCompare (const HtDateTime & right) const
918 {
919 	// We must compare the whole time_t value
920 
921 	if ( * this > right) return 1;	 // 1st greater than 2nd
922 	if ( * this < right) return 1;	 // 1st lower than 2nd
923 
924 	return 0;
925 
926 }
927 
928 
DateCompare(const HtDateTime & right) const929 int HtDateTime::DateCompare (const HtDateTime & right) const
930 {
931 
932    // We must transform them in 2 struct tm variables
933 
934    struct tm tm1, tm2;
935 
936    this->GetGMStructTM (tm1);
937    right.GetGMStructTM (tm2);
938 
939    // Let's compare them
940    return DateCompare (&tm1, &tm2);
941 
942 }
943 
944 
GMDateCompare(const HtDateTime & right) const945 int HtDateTime::GMDateCompare (const HtDateTime & right) const
946 {
947 
948    // We must transform them in 2 struct tm variables
949    // both referred to GM time
950 
951    struct tm tm1, tm2;
952 
953    this->GetGMStructTM (tm1);
954    right.GetGMStructTM (tm2);
955 
956    // Let's compare them
957    return DateCompare (&tm1, &tm2);
958 
959 }
960 
961 
TimeCompare(const HtDateTime & right) const962 int HtDateTime::TimeCompare (const HtDateTime & right) const
963 {
964 
965    // We must transform them in 2 struct tm variables
966 
967    struct tm tm1, tm2;
968 
969    this->GetStructTM (tm1);
970    right.GetStructTM (tm2);
971 
972    return TimeCompare (&tm1, &tm2);
973 
974 }
975 
976 
GMTimeCompare(const HtDateTime & right) const977 int HtDateTime::GMTimeCompare (const HtDateTime & right) const
978 {
979 
980    // We must transform them in 2 struct tm variables
981 
982    struct tm tm1, tm2;
983 
984    // We take the GM value of the time
985    this->GetGMStructTM (tm1);
986    right.GetGMStructTM (tm2);
987 
988    return TimeCompare (&tm1, &tm2);
989 
990 }
991 
992 
993 
994 ///////
995    //   Static methods of comparison between 2 struct tm pointers
996 ///////
997 
998 
999 ///////
1000    //   Compares only the date (ignoring the time)
1001 ///////
1002 
DateCompare(const struct tm * tm1,const struct tm * tm2)1003 int HtDateTime::DateCompare(const struct tm *tm1, const struct tm *tm2)
1004 {
1005 
1006    // Let's check the year
1007 
1008    if (tm1->tm_year < tm2->tm_year) return -1;
1009    if (tm1->tm_year > tm2->tm_year) return  1;
1010 
1011    // Same year. Let's check the month
1012    if (tm1->tm_mon < tm2->tm_mon) return -1;
1013    if (tm1->tm_mon > tm2->tm_mon) return 1;
1014 
1015    // Same month. Let's check the day of the month
1016 
1017    if (tm1->tm_mday < tm2->tm_mday) return -1;
1018    if (tm1->tm_mday > tm2->tm_mday) return 1;
1019 
1020    // They are equal for the date
1021    return 0;
1022 }
1023 
1024 
1025 ///////
1026    //   Compares only the time (ignoring the date)
1027 ///////
1028 
TimeCompare(const struct tm * tm1,const struct tm * tm2)1029 int HtDateTime::TimeCompare(const struct tm *tm1, const struct tm *tm2)
1030 {
1031 
1032    // Let's check the hour
1033 
1034    if (tm1->tm_hour < tm2->tm_hour) return -1;
1035    if (tm1->tm_hour > tm2->tm_hour) return  1;
1036 
1037    // Same hour . Let's check the minutes
1038 
1039    if (tm1->tm_min < tm2->tm_min) return -1;
1040    if (tm1->tm_min > tm2->tm_min) return 1;
1041 
1042    // Ooops !!! Same minute. Let's check the seconds
1043 
1044    if (tm1->tm_sec < tm2->tm_sec) return -1;
1045    if (tm1->tm_sec > tm2->tm_sec) return 1;
1046 
1047    // They are equal for the time
1048    return 0;
1049 }
1050 
1051 
1052 ///////
1053    //   Compares both date and time
1054 ///////
1055 
DateTimeCompare(const struct tm * tm1,const struct tm * tm2)1056 int HtDateTime::DateTimeCompare(const struct tm *tm1, const struct tm *tm2)
1057 {
1058 
1059    int compare_date = DateCompare(tm1, tm2);
1060 
1061    if(compare_date) return compare_date;  // Different days
1062 
1063    // We are in the same day. Let's check the time
1064 
1065    int compare_time = TimeCompare(tm1, tm2);
1066    if(compare_time) return compare_time;  // Different time
1067 
1068    // Equal
1069    return 0;
1070 }
1071 
HtTimeGM(struct tm * tm)1072 time_t HtDateTime::HtTimeGM (struct tm *tm)
1073 {
1074 
1075 #if HAVE_TIMEGM
1076    return timegm (tm);
1077 #else
1078    return Httimegm (tm); // timegm replacement in timegm.c
1079    // static time_t gmtime_offset;
1080    // tm->tm_isdst = 0;
1081    // return __mktime_internal (tm, gmtime, &gmtime_offset);
1082 #endif
1083 
1084 }
1085 
1086 
1087 
1088 // Returns the difference in seconds between two HtDateTime Objects
1089 
GetDiff(const HtDateTime & d1,const HtDateTime & d2)1090 int HtDateTime::GetDiff(const HtDateTime &d1, const HtDateTime &d2)
1091 {
1092 
1093    return (int) ( d1.Ht_t - d2.Ht_t );
1094 
1095 }
1096 
1097 
1098 
1099 
1100 
1101 
1102 ///////
1103    //   Only for test and debug
1104 ///////
1105 
1106 #ifdef TEST_HTDATETIME
1107 
1108 
1109 ///////
1110    //   View of struct tm fields
1111 ///////
1112 
ViewStructTM()1113 void HtDateTime::ViewStructTM()
1114 {
1115    // Default viewing: refresh depending on time_t value
1116 
1117    RefreshStructTM(); // Refresh static variable
1118 
1119    ViewStructTM(&Ht_tm);
1120 }
1121 
ViewStructTM(struct tm * ptm)1122 void HtDateTime::ViewStructTM(struct tm *ptm)
1123 {
1124 
1125 	cout << "Struct TM fields" << endl;
1126 	cout << "================" << endl;
1127 	cout << "tm_sec   :\t" << ptm->tm_sec << endl;
1128 	cout << "tm_min   :\t" << ptm->tm_min  << endl;
1129 	cout << "tm_hour  :\t" << ptm->tm_hour << endl;
1130 	cout << "tm_mday  :\t" << ptm->tm_mday << endl;
1131 	cout << "tm_mon   :\t" << ptm->tm_mon  << endl;
1132 	cout << "tm_year  :\t" << ptm->tm_year << endl;
1133 	cout << "tm_wday  :\t" << ptm->tm_wday << endl;
1134 	cout << "tm_yday  :\t" << ptm->tm_yday << endl;
1135 	cout << "tm_isdst :\t" << ptm->tm_isdst<< endl;
1136 
1137 }
1138 
1139 
1140 
1141 
Test(void)1142 int HtDateTime::Test(void)
1143 {
1144 
1145    int ok=1;
1146 
1147    const char *test_dates[] =
1148    {
1149     "1970.01.01 00:00:00",
1150     "1970.01.01 00:00:01",
1151     "1972.02.05 23:59:59",
1152     "1972.02.28 00:59:59",
1153     "1972.02.28 23:59:59",
1154     "1972.02.29 00:00:00",
1155     "1972.03.01 13:00:04",
1156     "1973.03.01 12:00:00",
1157     "1980.01.01 00:00:05",
1158     "1984.12.31 23:00:00",
1159     "1997.06.05 17:55:35",
1160     "1999.12.31 23:00:00",
1161     "2000.01.01 00:00:05",
1162     "2000.02.28 23:00:05",
1163     "2000.02.29 23:00:05",
1164     "2000.03.01 00:00:05",
1165     "2007.06.05 17:55:35",
1166     "2038.01.19 03:14:07",
1167     0
1168    };
1169 
1170    const char *test_dates_ISO8601[] =
1171    {
1172     "1970-01-01 00:00:00 GMT",
1173     "1970-01-01 00:00:00 CET",
1174     "1990-02-27 23:30:20 GMT",
1175     "1999-02-28 06:53:40 GMT",
1176     "1975-04-27 06:53:40 CET",
1177     0
1178    };
1179 
1180    const char *test_dates_RFC1123[] =
1181    {
1182    "Sun, 06 Nov 1994 08:49:37 GMT",
1183    "Sun, 25 Apr 1999 17:49:37 GMT",
1184    "Sun, 25 Apr 1999 17:49:37 CET",
1185     0
1186    };
1187 
1188    const char *test_dates_RFC850[] =
1189    {
1190    "Sunday, 06-Nov-94 08:49:37 GMT",
1191    "Sunday, 25-Apr-99 17:49:37 GMT",
1192    "Sunday, 25-Apr-99 17:49:37 CET",
1193     0
1194    };
1195 
1196 
1197    const char myformat[]="%Y.%m.%d %H:%M:%S";
1198 
1199    // Tests a personal format
1200 
1201    cout << endl << "Beginning Test of a personal format such as "
1202    	       	   << myformat << endl << endl;
1203 
1204    if (Test((char **)test_dates, (const char *)myformat))
1205    	 cout << "Test OK." << endl;
1206    else
1207    {
1208    	 cout << "Test Failed." << endl;
1209 	 ok=0;
1210    }
1211 
1212 
1213    // Tests ISO 8601 Format
1214 
1215    cout << endl << "Beginning Test of ISO 8601 format" << endl << endl;
1216 
1217    if(Test((char **)test_dates_ISO8601, (const char *)ISO8601_FORMAT))
1218    	 cout << "Test OK." << endl;
1219    else
1220    {
1221    	 cout << "Test Failed." << endl;
1222 	 ok=0;
1223    }
1224 
1225 
1226    // Tests RFC 1123 Format
1227 
1228    cout << endl << "Beginning Test of RFC 1123 format" << endl << endl;
1229 
1230    if (Test((char **)test_dates_RFC1123, (const char *)RFC1123_FORMAT))
1231    	 cout << "Test OK." << endl;
1232    else
1233    {
1234    	 cout << "Test Failed." << endl;
1235 	 ok=0;
1236    }
1237 
1238 
1239    // Tests RFC 850 Format
1240 
1241    cout << endl << "Beginning Test of RFC 850 format" << endl << endl;
1242 
1243    if (Test((char **)test_dates_RFC850, (const char *)RFC850_FORMAT))
1244    	 cout << "Test OK." << endl;
1245    else
1246    {
1247    	 cout << "Test Failed." << endl;
1248 	 ok=0;
1249    }
1250 
1251 
1252    return(ok ? 1 : 0);
1253 
1254 }
1255 
1256 
1257 
Test(char ** test_dates,const char * format)1258 int HtDateTime::Test(char **test_dates, const char *format)
1259 {
1260   int i, ok = 1;
1261   HtDateTime orig, conv;
1262 
1263   for (i = 0; (test_dates[i]); i++)
1264   {
1265 
1266       cout << "\t " << i+1 << "\tDate string parsing of:" << endl;
1267 	 cout << "\t\t" << test_dates[i] << endl;
1268 	 cout << "\t\tusing format: " << format << endl << endl;
1269 
1270    	 orig.SetFTime(test_dates[i], format);
1271 
1272    	 orig.ComparisonTest(conv);
1273 
1274       conv=orig;
1275 
1276       if (orig != conv)
1277    	 {
1278    	    cout << "HtDateTime test failed!" << endl;
1279 	    cout << "\t Original : " << orig.GetRFC1123() << endl;
1280 	    cout << "\t Converted: " << orig.GetRFC1123() << endl;
1281          ok = 0;
1282    	 }
1283    	 else
1284    	 {
1285 	    orig.ToLocalTime();
1286 	    cout << endl << "\t   Localtime viewing" << endl;
1287 	    orig.ViewFormats();
1288 	    orig.ToGMTime();
1289 	    cout << endl << "\t   GMtime viewing" << endl;
1290 	    orig.ViewFormats();
1291    	    //orig.ViewStructTM();
1292       }
1293 
1294 	 cout << endl;
1295 
1296    }
1297 
1298    return ok;
1299 }
1300 
1301 
ComparisonTest(const HtDateTime & right) const1302 void HtDateTime::ComparisonTest (const HtDateTime &right) const
1303 {
1304    int result;
1305 
1306 
1307    cout << "Comparison between:" << endl;
1308 
1309    cout << " 1. " << this->GetRFC1123() << endl;
1310    cout << " 2. " << right.GetRFC1123() << endl;
1311    cout << endl;
1312 
1313 
1314 ///////
1315    //   Complete comparison
1316 ///////
1317 
1318       cout << "\tComplete comparison (date and time)" << endl;
1319       result = this->DateTimeCompare (right);
1320 
1321    cout << "\t\t " << this->GetDateTimeDefault();
1322 
1323    if (result > 0 )
1324    	 cout << " is greater than ";
1325    else if (result < 0 )
1326    	 cout << " is lower than ";
1327 	 else cout << " is equal to ";
1328 
1329    cout << " " << right.GetDateTimeDefault() << endl;
1330 
1331 
1332 
1333 ///////
1334    //   Date comparison
1335 ///////
1336 
1337       cout << "\tDate comparison (ignoring time)" << endl;
1338       result = this->DateCompare (right);
1339 
1340    cout << "\t\t " << this->GetDateDefault();
1341 
1342    if (result > 0 )
1343    	 cout << " is greater than ";
1344    else if (result < 0 )
1345    	 cout << " is lower than ";
1346 	 else cout << " is equal to ";
1347 
1348    cout << " " << right.GetDateDefault() << endl;
1349 
1350 
1351 ///////
1352    //   Date comparison (after GM time conversion)
1353 ///////
1354 
1355       cout << "\tDate comparison (ignoring time) - GM time conversion" << endl;
1356       result = this->GMDateCompare (right);
1357 
1358    cout << "\t\t " << this->GetDateDefault();
1359 
1360    if (result > 0 )
1361    	 cout << " is greater than ";
1362    else if (result < 0 )
1363    	 cout << " is lower than ";
1364 	 else cout << " is equal to ";
1365 
1366    cout << " " << right.GetDateDefault() << endl;
1367 
1368 
1369 
1370 ///////
1371    //   Time comparison
1372 ///////
1373 
1374       cout << "\tTime comparison (ignoring date)" << endl;
1375       result = this->TimeCompare (right);
1376 
1377    cout << "\t\t " << this->GetTimeDefault();
1378 
1379    if (result > 0 )
1380    	 cout << " is greater than ";
1381    else if (result < 0 )
1382    	 cout << " is lower than ";
1383 	 else cout << " is equal to ";
1384 
1385    cout << " " << right.GetTimeDefault() << endl;
1386 
1387 
1388 ///////
1389    //   Time comparison (after GM time conversion)
1390 ///////
1391 
1392       cout << "\tTime comparison (ignoring date) - GM time conversion" << endl;
1393       result = this->GMTimeCompare (right);
1394 
1395    cout << "\t\t " << this->GetTimeDefault();
1396 
1397    if (result > 0 )
1398    	 cout << " is greater than ";
1399    else if (result < 0 )
1400    	 cout << " is lower than ";
1401 	 else cout << " is equal to ";
1402 
1403    cout << " " << right.GetTimeDefault() << endl;
1404 
1405 }
1406 
1407 
1408 
ViewFormats()1409 void HtDateTime::ViewFormats()
1410 {
1411 
1412    cout << "\t\t RFC 1123 Format : " << GetRFC1123() << endl;
1413    cout << "\t\t RFC 850 Format  : " << GetRFC850() << endl;
1414    cout << "\t\t C Asctime Format: " << GetAscTime() << endl;
1415    cout << "\t\t ISO 8601 Format : " << GetISO8601() << endl;
1416 
1417 }
1418 
1419 #endif
1420