OpenDNSSEC-enforcer  1.3.15
datetime.c
Go to the documentation of this file.
1 /*
2  * $Id: datetime.c 4654 2011-03-25 08:43:46Z sion $
3  *
4  * Copyright (c) 2008-2009 Nominet UK. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
21  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
25  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28 
29 /*+
30  * datetime - Miscellaneous Date/Time Utilities
31  *
32  * Description:
33  * Miscellaneous date/time utility functions used by the commands.
34 -*/
35 
36 #define _GNU_SOURCE /* glibc2 needs this */
37 #include "config.h"
38 
39 #include <assert.h>
40 #include <ctype.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <time.h>
44 #include <limits.h>
45 
46 #include "compat.h"
47 
48 #include "ksm/ksm.h"
49 #include "ksm/datetime.h"
50 #include "ksm/message.h"
51 #include "ksm/kmedef.h"
52 #include "ksm/string_util.h"
53 #include "ksm/string_util2.h"
54 
55 /* Macros to copy characters from one array to another */
56 
57 #define COPY1(src, srcidx, dst, dstidx) \
58  { \
59  (dst)[(dstidx)] = (src)[(srcidx)];\
60  }
61 #define COPY2(src, srcidx, dst, dstidx) \
62  { \
63  COPY1((src), (srcidx), (dst), (dstidx)); \
64  COPY1((src), (srcidx) + 1, (dst), (dstidx) + 1); \
65  }
66 #define COPY3(src, srcidx, dst, dstidx) \
67  { \
68  COPY2((src), (srcidx), (dst), (dstidx)); \
69  COPY1((src), (srcidx) + 2, (dst), (dstidx) + 2); \
70  }
71 #define COPY4(src, srcidx, dst, dstidx) \
72  { \
73  COPY3((src), (srcidx), (dst), (dstidx)); \
74  COPY1((src), (srcidx) + 3, (dst), (dstidx) + 3); \
75  }
76 
77 
78 
79 /*+
80  * DtDateTimeNow - Return Current Date and Time
81  *
82  * Description:
83  * Returns a structure containing the current date and time.
84  *
85  * Arguments:
86  * struct tm* datetime
87  * Returned structure holding the current date and time.
88  *
89  * Returns:
90  * int
91  * 0 Success
92  * 1 Some error
93 -*/
94 
95 int DtNow(struct tm* datetime)
96 {
97  time_t curtime; /* Current time */
98  struct tm *ptr; /* Pointer to returned result */
99 
100 #ifdef ENFORCER_TIMESHIFT
101  char *override;
102  int status;
103 
104  override = getenv("ENFORCER_TIMESHIFT");
105  if (override) {
106  (void) MsgLog(KME_TIMESHIFT, override);
107  status = DtParseDateTime(override, datetime);
108 
109  if (status) {
110  printf("Couldn't turn \"%s\" into a date, quitting...\n", override);
111  exit(1);
112  }
113 
114  return status;
115  }
116 #endif /* ENFORCER_TIMESHIFT */
117 
118  (void) time(&curtime);
119  ptr = localtime_r(&curtime, datetime);
120  return (ptr ? 0 : 1);
121 }
122 
123 
124 
125 
126 /*+
127  * DtNumeric - Parse Numeric Date and Timestrncat
128  *
129  * Description:
130  * Given a string of the form YYYY[MM[DD[HH[MM[SS]]]]], return a struct tm
131  * structure holding the interpreted date and time.
132  *
133  * Arguments:
134  * const char* string
135  * String. All the characters are known to be digits.
136  *
137  * struct tm* datetime
138  * Returned structure holding the current date and time.
139  *
140  * Returns:
141  * int
142  * 0 Success
143  * 1 Some error
144 -*/
145 
146 int DtNumeric(const char* string, struct tm* datetime)
147 {
148  int i; /* Loop counter */
149  int length; /* Length of the string */
150  char buffer[15]; /* Fully expanded string */
151  char ebuffer[20]; /* Expanded string with spaces between */
152  int status = 0; /* Assumed success return */
153  char* ptr; /* Result pointer */
154 
155  /*
156  * A numeric string is only valid if:
157  *
158  * a) it contains an even number of characters.
159  * b) It has a minimum of 8 characters
160  * c) It has a maximum of 14 characters.
161  */
162 
163  length = strlen(string);
164  if ((length >= 8) && (length <= 14) && ((length % 2) == 0)) {
165 
166  /* Valid string length, pad out to 14 characters with zeroes */
167 
168  strlcpy(buffer, string, 15);
169  for (i = length; i < (int) (sizeof(buffer) - 1); ++i) {
170  buffer[i] = '0';
171  }
172  buffer[sizeof(buffer) - 1] = '\0';
173 
174  /* Expand the character array to allow strptime to work */
175 
176  memset(ebuffer, ' ', sizeof(ebuffer));
177  ebuffer[sizeof(ebuffer) - 1] = '\0';
178 
179  COPY4(buffer, 0, ebuffer, 0);
180  COPY2(buffer, 4, ebuffer, 5);
181  COPY2(buffer, 6, ebuffer, 8);
182  COPY2(buffer, 8, ebuffer, 11);
183  COPY2(buffer, 10, ebuffer, 14);
184  COPY2(buffer, 12, ebuffer, 17);
185 
186  /* ... and convert */
187 
188  ptr = strptime(ebuffer, "%Y %m %d %H %M %S", datetime);
189  status = ptr ? 0 : 1;
190  }
191  else {
192 
193  /* Wrong number of characters */
194 
195  status = 1;
196  }
197 
198  return status;
199 }
200 
201 
202 /*+
203  * DtAppendTime - Append Time to Date
204  *
205  * Description:
206  * Interprets the time part of a date/time string and appends it to the
207  * normalized date/time field.
208  *
209  * Arguments:
210  * char* fulldt
211  * Full date and time. On entry, this points to a buffer holding the
212  * date part of the full date and time. On exit, the data in the
213  * buffer is extended to include the time part.
214  *
215  * Note: The buffer holding the full date and time is assumed to be
216  * big enough to allow the string in it to be extended by nine
217  * characters (i.e. _00:00:00).
218  *
219  * const char* timepart
220  * Time part to append. This must be one of the following allowed time
221  * formats:
222  *
223  * [[:| ]hh[:mm[:ss]]]
224  *
225  * i.e. the first characters is a space or a colon, followed by a two-
226  * digit hour. If minutes are present, they are separated from the
227  * hour by a colon, and likewise for seconds.
228  *
229  * Any absent fields are assumed to be "00".
230  *
231  * Returns:
232  * int
233  * Status return
234  * 0 Success
235  * 1 Some problem with the date
236 -*/
237 
238 int DtAppendTime(char* fulldt, const char* timepart)
239 {
240  int length; /* Length of the time part */
241  int status = 0; /* Return status, assumed success */
242 
243  if (fulldt == NULL) {
244  return 1;
245  }
246 
247  if ((timepart == NULL) || (*timepart == '\0')) {
248 
249  /* No time part, set default */
250 
251  strcat(fulldt, " 00:00:00");
252  }
253  else {
254  if ((*timepart == ' ') || (*timepart == ':')) {
255 
256  /* Valid separator */
257 
258  length = strlen(timepart); /* Must be > 0 */
259 
260  /*
261  * Now just check lengths. If the length is correct
262  * but the string is incorrect, it will be caught when
263  * we try to interpret the time.
264  */
265 
266  if (length == 3) {
267  strcat(fulldt, timepart);
268  strcat(fulldt, ":00:00");
269  }
270  else if (length == 6) {
271  strcat(fulldt, timepart);
272  strcat(fulldt, ":00");
273  }
274  else if (length == 9) {
275  strcat(fulldt, timepart);
276  }
277  else {
278  status = 1;
279  }
280  }
281  else {
282 
283  /* Time part did not start with ' ' or ':' */
284 
285  status = 1;
286  }
287  }
288 
289  return status;
290 }
291 
292 
293 
294 /*+
295  * DtGeneral - Parse Date and Time
296  *
297  * Description:
298  * Given a string that represents a date, parse it and fill in a struct tm
299  * tm structure holding the interpreted date and time.
300  *
301  * Allowed date/time strings are of the form:
302  *
303  * YYYYMMDD[HH[MM[SS]]] (all numeric)
304  *
305  * or D-MMM-YYYY[:| ]HH[:MM[:SS]] (alphabetic month)
306  * or DD-MMM-YYYY[:| ]HH[:MM[:SS]] (alphabetic month)
307  * or YYYY-MMM-DD[:| ]HH[:MM[:SS]] (alphabetic month)
308  *
309  * D-MM-YYYY[:| ]HH[:MM[:SS]] (numeric month)
310  * DD-MM-YYYY[:| ]HH[:MM[:SS]] (numeric month)
311  * or YYYY-MM-DD[:| ]HH[:MM[:SS]] (numeric month)
312  *
313  * ... and the distinction between them is given by the location of the
314  * hyphens.
315  *
316  * Arguments:
317  * const char* string (input)
318  * String to check. This is known to be non-null and not all spaces.
319  *
320  * struct tm* datetime (modified)
321  * Structure which is returned holding the current date and time.
322  *
323  * Returns:
324  * int
325  * 0 Success
326  * <>0 Some error
327 -*/
328 
329 int DtGeneral(const char* string, struct tm* datetime)
330 {
331  int alphadate = 0; /* Set 1 if alphabetic form of the date */
332  int length; /* Length of the string */
333  char copy[32]; /* Copy of input string */
334  char fulldt[32]; /* Full date and time */
335  char* ptr; /* Pointer to tm structure */
336  int status = 0; /* Return status */
337  int timeoff; /* Offset of time part in given string */
338 
339  /* Assert what we know about the input */
340 
341  if (string == NULL || *string == '\0') {
342  return 1;
343  }
344 
345  /* Check the string */
346 
347  if (StrIsDigits(string)) {
348 
349  /* Possibly a numeric date/time - perform analysis */
350 
351  status = DtNumeric(string, datetime);
352  }
353  else {
354  length = strlen(string);
355  if (length >= 9) {
356 
357  /*
358  * String has minimum length to be valid. Copy it to a buffer of
359  * a known length to ensure that all future index references are
360  * valid (even if they do reference null characters).
361  */
362 
363  memset(copy, 0, sizeof(copy));
364  StrStrncpy(copy, string, sizeof(copy));
365 
366  /*
367  * Normalize alphabetic dates to DD-MMM-YYYY, and numeric dates to
368  * [D]D-MM-YYYY. Characters are copied via individual assignments,
369  * this being assumed to be faster than copying via memcpy when the
370  * call/return overhead is taken into account.
371  */
372 
373  if ((copy[1] == '-') && (copy[5] == '-')) { /* D-MMM-YYYY */
374  strcpy(fulldt, "0");
375  strlcat(fulldt + 1, copy, 11);
376  /* *(fulldt + 11) = '\0'; */
377  timeoff = 10;
378  alphadate = 1;
379  }
380  else if ((copy[1] == '-') && (copy[4] == '-')) { /* D-MM-YYYY */
381  strcpy(fulldt, "0");
382  strlcat(fulldt + 1, copy, 10);
383  /* *(fulldt + 10) = '\0'; */
384  timeoff = 9;
385  alphadate = 0;
386  }
387  else if ((copy[2] == '-') && (copy[6] == '-')) {/* DD-MMM-YYYY */
388  strlcpy(fulldt, copy, 12);
389  /* *(fulldt + 11) = '\0'; */
390  timeoff = 11;
391  alphadate = 1;
392  }
393  else if ((copy[2] == '-') && (copy[5] == '-')) { /* DD-MM-YYYY */
394  strlcpy(fulldt, copy, 11);
395  /* *(fulldt + 10) = '\0'; */
396  timeoff = 10;
397  alphadate = 0;
398  }
399  else if ((copy[4] == '-') && (copy[8] == '-')) {/* YYYY-MMM-DD */
400  COPY2(copy, 9, fulldt, 0);
401  *(fulldt + 2) = '-';
402  COPY3(copy, 5, fulldt, 3);
403  *(fulldt + 6) = '-';
404  COPY4(copy, 0, fulldt, 7);
405  *(fulldt + 11) = '\0';
406  timeoff = 11;
407  alphadate = 1;
408  }
409  else if ((copy[4] == '-') && (copy[7] == '-')) {/* YYYY-MM-DD */
410  COPY2(copy, 8, fulldt, 0);
411  *(fulldt + 2) = '-';
412  COPY2(copy, 5, fulldt, 3);
413  *(fulldt + 5) = '-';
414  COPY4(copy, 0, fulldt, 6);
415  *(fulldt + 10) = '\0';
416  timeoff = 10;
417  alphadate = 0;
418  }
419  else {
420  status = 1; /* Unrecognised format */
421  }
422 
423  if (status == 0) {
424 
425  /* Date OK, so process time part (if any). First set delimiter to space if it is ':' */
426  if (copy[timeoff] == ':') {
427  copy[timeoff] = ' ';
428  }
429 
430  status = DtAppendTime(fulldt, &copy[timeoff]);
431  if (status == 0) {
432  if (alphadate) {
433  ptr = strptime(fulldt, "%d-%b-%Y %H:%M:%S", datetime);
434  }
435  else {
436  ptr = strptime(fulldt, "%d-%m-%Y %H:%M:%S", datetime);
437  }
438  status = ptr ? 0 : 2;
439  }
440  }
441  else {
442 
443  /* String is too short to be a valid date/time */
444 
445  status = 3;
446  }
447  }
448  else {
449  status = 3; /* Too short */
450  }
451  }
452 
453  return status;
454 }
455 
456 
457 /*+
458  * DtGeneralString - Parse Date and Time
459  *
460  * Description:
461  * As DtGeneral, but returns the result in a string of the form
462  *
463  * YYYY-MM-DD HH:MM:SS
464  *
465  * ... which is suitable for ASCII input into MySql (after surrounding it
466  * with quotes).
467  *
468  * Arguments:
469  * const char* string (input)
470  * String to analyze. This is known to be non-null and not all spaces.
471  *
472  * Returns:
473  * char*
474  * String of the form YYYY-MM-DD HH:MM:SS representing the date
475  * and time. If NULL, there was some error.
476  *
477  * The string should be freed via a call to StrFree.
478 -*/
479 
480 char* DtGeneralString(const char* string)
481 {
482  struct tm datetime; /* Used for getting the date/time */
483  char buffer[KSM_TIME_LENGTH]; /* YYYY-MM-DD HH:MM:SS + NULL */
484  char* retval = NULL; /* Returned string */
485  int status; /* Status return */
486 
487  if (string == NULL) {
488  return NULL;
489  }
490 
491  status = DtGeneral(string, &datetime);
492  if (status == 0) {
493  snprintf(buffer, KSM_TIME_LENGTH, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
494  datetime.tm_year + 1900, datetime.tm_mon + 1, datetime.tm_mday,
495  datetime.tm_hour, datetime.tm_min, datetime.tm_sec);
496  retval = StrStrdup(buffer);
497  }
498 
499  return retval;
500 }
501 
502 
503 
504 /*+
505  * DtParseDateTime - Parse Date and Time
506  *
507  * Description:
508  * Date/times can be specified in one of several formats:
509  *
510  * now
511  * YYYYMMDDHHMMSS
512  * YYYY-MM-DD HH:MM:SS
513  * DD-MMM-YYYY HH:MM:SS
514  * DD-MMM-YYYY:HH:MM:SS
515  * DD-MM-YYYY HH:MM:SS
516  * DD-MM-YYYY:HH:MM:SS
517  *
518  * In the all strings, trailing time fields can be omitted and default to
519  * 00:00:00 on the current day.
520  *
521  * YYYY-MM-DD Defaults to 00:00:00 on the day specified.
522  * YYYYMMDD Defaults to 00:00:00 on the day specified.
523  * DD-MM-YYYY Defaults to 00:00:00 on the day specified.
524  * YYYYMMDDHH Defaults to 00:00:00 on the day specified.
525  * DD-MM-YYYY:HH Defaults to HH o'clock of the day specified
526  *
527  * Also, leading DDs can be abbreviated to a single character.
528  *
529  * The other specification is:
530  *
531  * now The date/time at which the command is executed
532  *
533  * Arguments:
534  * const char* string
535  * The input string to parse.
536  *
537  * struct tm* datetime
538  * Output time/date
539  *
540  * Returns:
541  * int
542  * 0 Success
543  * 1 Parse error
544 -*/
545 
546 int DtParseDateTime(const char* string, struct tm* datetime)
547 {
548  char* buffer; /* Duplicate of the string to parse */
549  int len; /* Length of the string */
550  int status = 0; /* Return status */
551  char* text; /* First non-blank character in duplicated string */
552 
553  /* Can only work if the string is non-null */
554 
555  if (string) {
556 
557  /* Normalise the string */
558 
559  buffer = StrStrdup(string);
560  StrTrimR(buffer);
561  text = StrTrimL(buffer);
562  StrToLower(text);
563 
564  len = strlen(text);
565  if (len != 0) {
566 
567  /* Something in the string, decide what to do */
568 
569  if (strcmp(text, "now") == 0) {
570  status = DtNow(datetime);
571  }
572  else {
573  status = DtGeneral(text, datetime);
574  }
575  }
576  else {
577 
578  /* Nothing in the normalized string */
579 
580  status = 1;
581  }
582 
583  /* Free up allocated memory */
584 
585  StrFree(buffer);
586  }
587  else {
588 
589  /* Passed pointer is NULL */
590 
591  status = 1;
592  }
593 
594  return status;
595 }
596 
597 
598 /*+
599  * DtParseDateTimeString - Parse Date And Time
600  *
601  * Description:
602  * As DtParseDateTime, but returns the result in a dynamically-allocated
603  * string.
604  *
605  * Arguments:
606  * const char* string (input)
607  * String to analyze.
608  *
609  * Returns:
610  * char*
611  * String of the form YYYY-MM-DD HH:MM:SS representing the date
612  * and time. If NULL, there was some error.
613  *
614  * The string should be freed via a call to StrFree.
615 -*/
616 
617 char* DtParseDateTimeString(const char* string)
618 {
619  char buffer[KSM_TIME_LENGTH]; /* Length of YYYY-MM-DD HH:MM:SS + NULL */
620  struct tm datetime; /* Local date and time */
621  char* retval = NULL; /* Result string */
622  int status; /* Status return from called function */
623 
624  if (string && *string) {
625  status = DtParseDateTime(string, &datetime);
626  if (status == 0) {
627  snprintf(buffer, KSM_TIME_LENGTH,
628  "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
629  datetime.tm_year + 1900, datetime.tm_mon + 1,
630  datetime.tm_mday, datetime.tm_hour, datetime.tm_min,
631  datetime.tm_sec);
632  retval = StrStrdup(buffer);
633  }
634  }
635 
636  return retval;
637 }
638 
639 
640 /*+
641  * DtIntervalSeconds - Parse Interval String
642  *
643  * Description:
644  * Parses an interval string which is of the form:
645  *
646  * <number>
647  * or <number><interval-type>
648  *
649  * Without an interval type, the interval is assumed to be in seconds.
650  * Otherwise, the following interval types recognised are:
651  *
652  * s Seconds
653  * m Minutes - multiply number by 60 (no. seconds in a minute)
654  * h Hours - multiple number by 3600 (no. seconds in an hour)
655  * d Day - multiple number by 86400 (no. seconds in a day)
656  * w Week - multiple number by 604,800 (no. seconds in a week)
657  *
658  * Upper-case characters are not recognised.
659  *
660  * Example: The string 2d would translate to 172,800
661  *
662  * Arguments:
663  * const char* text
664  * Interval as a string.
665  *
666  * long* interval
667  * Returned interval.
668  *
669  * Returns:
670  * int
671  * 0 Success, string translated OK
672  * 1 Error - invalid interval-type
673  * 2 Error - unable to translate string.
674  * 3 Error - string too long to be a number.
675  * 4 Error - invalid pointers or text string NULL.
676 -*/
677 
678 int DtIntervalSeconds(const char* text, int* interval)
679 {
680  char number[32]; /* Long enough for any number */
681  int status = 0; /* Status return */
682  int length; /* Lengthof the string */
683  long multiplier = 1; /* Multiplication factor */
684 
685  if (text && interval && *text) {
686 
687  /* Is there a multiplier? If so, interpret it. */
688 
689  length = strlen(text);
690  if (isdigit(text[length - 1])) {
691  multiplier = 1; /* No, set the factor to 1 */
692  }
693  else {
694  switch (text[length - 1]) {
695  case 's':
696  multiplier = 1;
697  break;
698 
699  case 'm':
700  multiplier = 60;
701  break;
702 
703  case 'h':
704  multiplier = 60 * 60;
705  break;
706 
707  case 'd':
708  multiplier = 24 * 60 * 60;
709  break;
710 
711  case 'w':
712  multiplier = 7 * 24 * 60 * 60;
713  break;
714 
715  default:
716  status = 1;
717  }
718  --length; /* Reduce bytes we are going to copy */
719  }
720 
721  if (status == 0) {
722 
723  /* Copy all but the multiplier to the buffer for interpretation */
724 
725  if (length <= (long) (sizeof(number) - 1)) {
726  (void) memcpy(number, text, length);
727  number[length] = '\0';
728  status = StrStrtoi(number, interval);
729  if (status == 0) {
730 
731  /* Successful, conversion, factor in the multiplier */
732 
733  *interval *= multiplier;
734  }
735  else {
736  status = 2; /* Can't translate string/overflow */
737  }
738  }
739  else {
740 
741  /* String is too long to be a valid number */
742 
743  status = 3;
744  }
745  }
746  }
747  else {
748 
749  /* Input pointers NULL or empty string */
750 
751  status = 4;
752  }
753 
754  return status;
755 }
756 
757 
758 /*+
759  * DtSecondsInterval - Convert Seconds to Interval
760  *
761  * Description:
762  * Given an interval in seconds, convert to an interval if possible.
763  * A suffix is added to indicate the result.
764  *
765  * Arguments:
766  * int interval
767  * Interval to convert.
768  *
769  * char* text
770  * Converted text (possibly truncated) is placed here. The buffer
771  * should be about 32 characters long (maximum).
772  *
773  * size_t textlen
774  * Length of the buffer pointed to by "text".
775 -*/
776 
777 void DtSecondsInterval(int interval, char* text, size_t textlen)
778 {
779  char buffer[64];
780 
781  if (text && (textlen > 0)) {
782  if (interval != 0) {
783  if (interval % (60 * 60 * 24 * 7) == 0) {
784  snprintf(buffer, 64, "%dw", interval / (60 * 60 * 24 * 7));
785  }
786  else if (interval % (60 * 60 * 24) == 0) {
787  snprintf(buffer, 64,"%dd", interval / (60 * 60 * 24));
788  }
789  else if (interval % (60 * 60) == 0) {
790  snprintf(buffer, 64, "%dh", interval / (60 * 60));
791  }
792  else if (interval % 60 == 0) {
793  snprintf(buffer, 64, "%dm", interval / 60);
794  }
795  else {
796  snprintf(buffer, 64, "%ds", interval);
797  }
798  }
799  else {
800  strcpy(buffer, "0s");
801  }
802 
803  StrStrncpy(text, buffer, textlen);
804  }
805 
806  return;
807 }
808 
809 
810 /*+
811  * DtDateDiff - Return Different in Dates
812  *
813  * Description:
814  * Returns the different between two dates as the number of seconds.
815  *
816  * Arguments:
817  * const char* date1, const char* date2
818  * Dates, given in the form "YYYY-MM-DD HH:MM:SS"
819  *
820  * int* result
821  * Seconds between the two dates.
822  *
823  * Returns:
824  * int
825  * Status return. 0 => success, other => some error in the input.
826 -*/
827 
828 int DtDateDiff(const char* date1, const char* date2, int* result)
829 {
830  static const char* FORMAT = "%Y-%m-%d %H:%M:%S";
831  char* cstatus; /* Character status return */
832  int status; /* Status return */
833  struct tm tm1; /* Converted first time */
834  struct tm tm2; /* Converted second time */
835  time_t t1; /* First time as seconds */
836  time_t t2; /* Second time as seconds */
837 
838  /* Do sanity check on the argument */
839  if (result == NULL) {
840  return 4;
841  }
842 
843  if (date1 && *date1 && date2 && *date2) {
844 
845  /* Convert dates to struct tm */
846 
847  memset(&tm1, 0, sizeof(tm1));
848  cstatus = strptime(date1, FORMAT, &tm1);
849  if (cstatus) {
850  memset(&tm2, 0, sizeof(tm2));
851  cstatus = strptime(date2, FORMAT, &tm2);
852  if (cstatus) {
853 
854  /*
855  * tm1 and tm2 contain valid dates. Convert to seconds since
856  * 1 Jan 1970.
857  */
858 
859  t1 = mktime(&tm1);
860  t2 = mktime(&tm2);
861  *result = (int) (t1 - t2);
862  status = 0;
863  }
864  else {
865  status = 2; /* Second date is invalid */
866  }
867  }
868  else {
869  status = 1; /* First date is invalid */
870  }
871  }
872  else {
873  status = 3; /* One or both dates are NULL or empty */
874  }
875 
876  return status;
877 }
878 
879 /*+
880  * DtXMLIntervalSeconds - Parse xsd:durations Interval String
881  *
882  * Description:
883  * Parses an interval string which is of the form:
884  *
885  * P<number>
886  * or P<number><interval-type>
887  * or PT<number><interval-type> (if the interval-type is H, M or S)
888  *
889  * Without an interval type, the interval is assumed to be in seconds.
890  * Otherwise, the following interval types recognised are:
891  *
892  * S Seconds
893  * M Minutes - multiply number by 60 (no. seconds in a minute)
894  * H Hours - multiply number by 3600 (no. seconds in an hour)
895  * D Day - multiply number by 86400 (no. seconds in a day)
896  * W Week - multiply number by 604,800 (no. seconds in a week)
897  * M Month - multiply number by 2,678,400 (no. seconds in 31 days)
898  * Y Year - multiply number by 31,536,000 (no. seconds in 365 days)
899  *
900  * Lower-case characters are not recognised.
901  *
902  * Example: The string P2D would translate to 172,800
903  *
904  * Arguments:
905  * const char* text
906  * Interval as a string.
907  *
908  * long* interval
909  * Returned interval.
910  *
911  * Returns:
912  * int
913  * -1 Success, string translated OK _BUT_ may not be what was expected
914  * (Year or Month used which gives approximate answer).
915  * 0 Success, string translated OK
916  * 2 Error - unable to translate string.
917  * 3 Error - string too long to be a number.
918  * 4 Error - invalid pointers or text string NULL.
919  *
920  * Known issues:
921  *
922  * 1. Years and months are only approximate as it has no concept of "now"
923  * We use 30 days = 1 month and 365 days = 1 year.
924  * 2. The "T" only effects the value of "M" (P1S should be illegal as correctly
925  * it would be PT1S)
926 -*/
927 
928 int DtXMLIntervalSeconds(const char* text, int* interval)
929 {
930  int length = 0; /* Length of the string */
931  short is_time = 0; /* Do we have a Time section or not */
932  short is_neg = 0; /* Do we have a negative number */
933  short warning = 0; /* Do we need a warning code for duration approximation? */
934  short got_temp = 0; /* Have we seen a number? */
935  long long temp = 0; /* Number from this section */
936  const char *ptr = text; /* allow us to read through */
937 
938  int status = 0;
939 
940  long long temp_interval = 0;
941 
942  if (text && interval && *text) {
943  length = strlen(text);
944  } else {
945  return(4);
946  }
947 
948  if (ptr && length && interval) {
949  const char *end = text + length;
950  if (*ptr == '-') {
951  is_neg = 1;
952  ptr++;
953  }
954  if (*ptr == 'P') {
955  ptr++;
956  }
957  do {
958  switch (*ptr) {
959  case 'S':
960  if (got_temp) {
961  temp_interval += temp;
962  temp = 0;
963  got_temp = 0;
964  } else {
965  return(2);
966  }
967  break;
968 
969  case 'M':
970  if (got_temp) {
971  if (is_time) {
972  temp_interval += 60 * temp;
973  } else {
974  temp_interval += 31 * 24 * 60 * 60 * temp;
975  warning = 1;
976  }
977  temp = 0;
978  got_temp = 0;
979  } else {
980  return(2);
981  }
982  break;
983 
984  case 'H':
985  if (got_temp) {
986  temp_interval += 60 * 60 * temp;
987  temp = 0;
988  got_temp = 0;
989  } else {
990  return(2);
991  }
992  break;
993 
994  case 'D':
995  if (got_temp) {
996  temp_interval += 24 * 60 * 60 * temp;
997  temp = 0;
998  got_temp = 0;
999  } else {
1000  return(2);
1001  }
1002  break;
1003 
1004  case 'W':
1005  if (got_temp) {
1006  temp_interval += 7 * 24 * 60 * 60 * temp;
1007  temp = 0;
1008  got_temp = 0;
1009  } else {
1010  return(2);
1011  }
1012  break;
1013 
1014  case 'Y':
1015  if (got_temp) {
1016  temp_interval += 365 * 24 * 60 * 60 * temp;
1017  temp = 0;
1018  warning = 1;
1019  got_temp = 0;
1020  } else {
1021  return(2);
1022  }
1023  break;
1024 
1025  case 'T':
1026  is_time = 1;
1027  break;
1028 
1029  case '0':
1030  case '1':
1031  case '2':
1032  case '3':
1033  case '4':
1034  case '5':
1035  case '6':
1036  case '7':
1037  case '8':
1038  case '9':
1039  if (!temp) {
1040  temp = atoll(ptr);
1041  got_temp = 1;
1042  if ((temp_interval <= INT_MIN) || (temp_interval >= INT_MAX)) {
1043  return(3);
1044  }
1045  }
1046  break;
1047 
1048  default:
1049  if (ptr != end) {
1050  return(2);
1051  }
1052  }
1053  } while (ptr++ < end);
1054  }
1055  else {
1056  status = 2; /* Can't translate string/overflow */
1057  }
1058 
1059  /* If we had no trailing letter then it is an implicit "S" */
1060  if (temp) {
1061  temp_interval += temp;
1062  temp = 0;
1063  }
1064 
1065  if (is_neg == 1) {
1066  temp_interval = 0 - temp_interval;
1067  }
1068 
1069  if (warning == 1) {
1070  status = -1;
1071  }
1072 
1073  if ((temp_interval >= INT_MIN) && (temp_interval <= INT_MAX)) {
1074  *interval = (int) temp_interval;
1075  }
1076  else {
1077  status = 3; /* Integer overflow */
1078  }
1079 
1080  return status;
1081 }