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