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