common,gpg: Fix processing of search descriptions ending in '!'.
[gnupg.git] / common / gettime.c
1 /* gettime.c - Wrapper for time functions
2  * Copyright (C) 1998, 2002, 2007, 2011 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * This file is free software; you can redistribute it and/or modify
7  * it under the terms of either
8  *
9  *   - the GNU Lesser General Public License as published by the Free
10  *     Software Foundation; either version 3 of the License, or (at
11  *     your option) any later version.
12  *
13  * or
14  *
15  *   - the GNU General Public License as published by the Free
16  *     Software Foundation; either version 2 of the License, or (at
17  *     your option) any later version.
18  *
19  * or both in parallel, as here.
20  *
21  * This file is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, see <http://www.gnu.org/licenses/>.
28  */
29
30 #include <config.h>
31 #include <stdlib.h>
32 #include <time.h>
33 #include <ctype.h>
34 #ifdef HAVE_LANGINFO_H
35 #include <langinfo.h>
36 #endif
37
38 #include "util.h"
39 #include "i18n.h"
40 #include "gettime.h"
41
42 #ifdef HAVE_UNSIGNED_TIME_T
43 # define IS_INVALID_TIME_T(a) ((a) == (time_t)(-1))
44 #else
45   /* Error or 32 bit time_t and value after 2038-01-19.  */
46 # define IS_INVALID_TIME_T(a) ((a) < 0)
47 #endif
48
49
50 static unsigned long timewarp;
51 static enum { NORMAL = 0, FROZEN, FUTURE, PAST } timemode;
52
53 /* Correction used to map to real Julian days. */
54 #define JD_DIFF 1721060L
55
56
57 /* Wrapper for the time(3).  We use this here so we can fake the time
58    for tests */
59 time_t
60 gnupg_get_time ()
61 {
62   time_t current = time (NULL);
63   if (timemode == NORMAL)
64     return current;
65   else if (timemode == FROZEN)
66     return timewarp;
67   else if (timemode == FUTURE)
68     return current + timewarp;
69   else
70     return current - timewarp;
71 }
72
73
74 /* Wrapper around gmtime_r.
75
76    On systems without gmtime_r this implementation works within gnupg
77    because we use only one thread a time.  FIXME: An independent
78    library may use gmtime in one of its own thread (or via
79    npth_enter/npth_leave) - in this case we run into a problem.  The
80    solution would be to use a mutex here.  */
81 struct tm *
82 gnupg_gmtime (const time_t *timep, struct tm *result)
83 {
84 #ifdef HAVE_GMTIME_R
85   return gmtime_r (timep, result);
86 #else
87   struct tm *tp;
88
89   tp = gmtime (timep);
90   if (tp)
91     memcpy (result, tp, sizeof *result);
92   return tp;
93 #endif
94 }
95
96
97 /* Return the current time (possibly faked) in ISO format. */
98 void
99 gnupg_get_isotime (gnupg_isotime_t timebuf)
100 {
101   time_t atime = gnupg_get_time ();
102
103   if (atime == (time_t)(-1))
104     *timebuf = 0;
105   else
106     {
107       struct tm *tp;
108       struct tm tmbuf;
109
110       tp = gnupg_gmtime (&atime, &tmbuf);
111       if (!tp)
112         *timebuf = 0;
113       else
114         snprintf (timebuf, 16, "%04d%02d%02dT%02d%02d%02d",
115                   1900 + tp->tm_year, tp->tm_mon+1, tp->tm_mday,
116                   tp->tm_hour, tp->tm_min, tp->tm_sec);
117     }
118 }
119
120
121 /* Set the time to NEWTIME so that gnupg_get_time returns a time
122    starting with this one.  With FREEZE set to 1 the returned time
123    will never change.  Just for completeness, a value of (time_t)-1
124    for NEWTIME gets you back to reality.  Note that this is obviously
125    not thread-safe but this is not required. */
126 void
127 gnupg_set_time (time_t newtime, int freeze)
128 {
129   time_t current = time (NULL);
130
131   if ( newtime == (time_t)-1 || current == newtime)
132     {
133       timemode = NORMAL;
134       timewarp = 0;
135     }
136   else if (freeze)
137     {
138       timemode = FROZEN;
139       timewarp = current;
140     }
141   else if (newtime > current)
142     {
143       timemode = FUTURE;
144       timewarp = newtime - current;
145     }
146   else
147     {
148       timemode = PAST;
149       timewarp = current - newtime;
150     }
151 }
152
153 /* Returns true when we are in timewarp mode */
154 int
155 gnupg_faked_time_p (void)
156 {
157   return timemode;
158 }
159
160
161 /* This function is used by gpg because OpenPGP defines the timestamp
162    as an unsigned 32 bit value. */
163 u32
164 make_timestamp (void)
165 {
166   time_t t = gnupg_get_time ();
167
168   if (t == (time_t)-1)
169     log_fatal ("gnupg_get_time() failed\n");
170   return (u32)t;
171 }
172
173
174
175 /****************
176  * Scan a date string and return a timestamp.
177  * The only supported format is "yyyy-mm-dd"
178  * Returns 0 for an invalid date.
179  */
180 u32
181 scan_isodatestr( const char *string )
182 {
183     int year, month, day;
184     struct tm tmbuf;
185     time_t stamp;
186     int i;
187
188     if( strlen(string) != 10 || string[4] != '-' || string[7] != '-' )
189         return 0;
190     for( i=0; i < 4; i++ )
191         if( !digitp (string+i) )
192             return 0;
193     if( !digitp (string+5) || !digitp(string+6) )
194         return 0;
195     if( !digitp(string+8) || !digitp(string+9) )
196         return 0;
197     year = atoi(string);
198     month = atoi(string+5);
199     day = atoi(string+8);
200     /* some basic checks */
201     if( year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 )
202         return 0;
203     memset( &tmbuf, 0, sizeof tmbuf );
204     tmbuf.tm_mday = day;
205     tmbuf.tm_mon = month-1;
206     tmbuf.tm_year = year - 1900;
207     tmbuf.tm_isdst = -1;
208     stamp = mktime( &tmbuf );
209     if( stamp == (time_t)-1 )
210         return 0;
211     return stamp;
212 }
213
214
215 int
216 isotime_p (const char *string)
217 {
218   const char *s;
219   int i;
220
221   if (!*string)
222     return 0;
223   for (s=string, i=0; i < 8; i++, s++)
224     if (!digitp (s))
225       return 0;
226   if (*s != 'T')
227       return 0;
228   for (s++, i=9; i < 15; i++, s++)
229     if (!digitp (s))
230       return 0;
231   if ( !(!*s || (isascii (*s) && isspace(*s)) || *s == ':' || *s == ','))
232     return 0;  /* Wrong delimiter.  */
233
234   return 1;
235 }
236
237
238 /* Scan a string and return true if the string represents the human
239    readable format of an ISO time.  This format is:
240       yyyy-mm-dd[ hh[:mm[:ss]]]
241    Scanning stops at the second space or at a comma.  If DATE_ONLY is
242    true the time part is not expected and the scanning stops at the
243    first space or at a comma. */
244 int
245 isotime_human_p (const char *string, int date_only)
246 {
247   const char *s;
248   int i;
249
250   if (!*string)
251     return 0;
252   for (s=string, i=0; i < 4; i++, s++)
253     if (!digitp (s))
254       return 0;
255   if (*s != '-')
256     return 0;
257   s++;
258   if (!digitp (s) || !digitp (s+1) || s[2] != '-')
259     return 0;
260   i = atoi_2 (s);
261   if (i < 1 || i > 12)
262     return 0;
263   s += 3;
264   if (!digitp (s) || !digitp (s+1))
265     return 0;
266   i = atoi_2 (s);
267   if (i < 1 || i > 31)
268     return 0;
269   s += 2;
270   if (!*s || *s == ',')
271     return 1; /* Okay; only date given.  */
272   if (!spacep (s))
273     return 0;
274   if (date_only)
275     return 1; /* Okay; only date was requested.  */
276   s++;
277   if (spacep (s))
278     return 1; /* Okay, second space stops scanning.  */
279   if (!digitp (s) || !digitp (s+1))
280     return 0;
281   i = atoi_2 (s);
282   if (i < 0 || i > 23)
283     return 0;
284   s += 2;
285   if (!*s || *s == ',')
286     return 1; /* Okay; only date and hour given.  */
287   if (*s != ':')
288     return 0;
289   s++;
290   if (!digitp (s) || !digitp (s+1))
291     return 0;
292   i = atoi_2 (s);
293   if (i < 0 || i > 59)
294     return 0;
295   s += 2;
296   if (!*s || *s == ',')
297     return 1; /* Okay; only date, hour and minute given.  */
298   if (*s != ':')
299     return 0;
300   s++;
301   if (!digitp (s) || !digitp (s+1))
302     return 0;
303   i = atoi_2 (s);
304   if (i < 0 || i > 60)
305     return 0;
306   s += 2;
307   if (!*s || *s == ',' || spacep (s))
308     return 1; /* Okay; date, hour and minute and second given.  */
309
310   return 0; /* Unexpected delimiter.  */
311 }
312
313 /* Convert a standard isotime or a human readable variant into an
314    isotime structure.  The allowed formats are those described by
315    isotime_p and isotime_human_p.  The function returns 0 on failure
316    or the length of the scanned string on success.  */
317 size_t
318 string2isotime (gnupg_isotime_t atime, const char *string)
319 {
320   gnupg_isotime_t dummyatime;
321
322   if (!atime)
323     atime = dummyatime;
324
325   atime[0] = 0;
326   if (isotime_p (string))
327     {
328       memcpy (atime, string, 15);
329       atime[15] = 0;
330       return 15;
331     }
332   if (!isotime_human_p (string, 0))
333     return 0;
334   atime[0] = string[0];
335   atime[1] = string[1];
336   atime[2] = string[2];
337   atime[3] = string[3];
338   atime[4] = string[5];
339   atime[5] = string[6];
340   atime[6] = string[8];
341   atime[7] = string[9];
342   atime[8] = 'T';
343   memset (atime+9, '0', 6);
344   atime[15] = 0;
345   if (!spacep (string+10))
346     return 10;
347   if (spacep (string+11))
348     return 11; /* As per def, second space stops scanning.  */
349   atime[9] = string[11];
350   atime[10] = string[12];
351   if (string[13] != ':')
352     return 13;
353   atime[11] = string[14];
354   atime[12] = string[15];
355   if (string[16] != ':')
356     return 16;
357   atime[13] = string[17];
358   atime[14] = string[18];
359   return 19;
360 }
361
362
363 /* Scan an ISO timestamp and return an Epoch based timestamp.  The only
364    supported format is "yyyymmddThhmmss" delimited by white space, nul, a
365    colon or a comma.  Returns (time_t)(-1) for an invalid string.  */
366 time_t
367 isotime2epoch (const char *string)
368 {
369   int year, month, day, hour, minu, sec;
370   struct tm tmbuf;
371
372   if (!isotime_p (string))
373     return (time_t)(-1);
374
375   year  = atoi_4 (string);
376   month = atoi_2 (string + 4);
377   day   = atoi_2 (string + 6);
378   hour  = atoi_2 (string + 9);
379   minu  = atoi_2 (string + 11);
380   sec   = atoi_2 (string + 13);
381
382   /* Basic checks.  */
383   if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31
384       || hour > 23 || minu > 59 || sec > 61 )
385     return (time_t)(-1);
386
387   memset (&tmbuf, 0, sizeof tmbuf);
388   tmbuf.tm_sec  = sec;
389   tmbuf.tm_min  = minu;
390   tmbuf.tm_hour = hour;
391   tmbuf.tm_mday = day;
392   tmbuf.tm_mon  = month-1;
393   tmbuf.tm_year = year - 1900;
394   tmbuf.tm_isdst = -1;
395   return timegm (&tmbuf);
396 }
397
398
399 /* Convert an Epoch time to an iso time stamp. */
400 void
401 epoch2isotime (gnupg_isotime_t timebuf, time_t atime)
402 {
403   if (atime == (time_t)(-1))
404     *timebuf = 0;
405   else
406     {
407       struct tm *tp;
408 #ifdef HAVE_GMTIME_R
409       struct tm tmbuf;
410
411       tp = gmtime_r (&atime, &tmbuf);
412 #else
413       tp = gmtime (&atime);
414 #endif
415       snprintf (timebuf, 16, "%04d%02d%02dT%02d%02d%02d",
416                 1900 + tp->tm_year, tp->tm_mon+1, tp->tm_mday,
417                 tp->tm_hour, tp->tm_min, tp->tm_sec);
418     }
419 }
420
421
422 /* Parse a short ISO date string (YYYY-MM-DD) into a TM structure.
423    Returns 0 on success.  */
424 int
425 isodate_human_to_tm (const char *string, struct tm *t)
426 {
427   int year, month, day;
428
429   if (!isotime_human_p (string, 1))
430     return -1;
431
432   year  = atoi_4 (string);
433   month = atoi_2 (string + 5);
434   day   = atoi_2 (string + 8);
435
436   /* Basic checks.  */
437   if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31)
438     return -1;
439
440   memset (t, 0, sizeof *t);
441   t->tm_sec  = 0;
442   t->tm_min  = 0;
443   t->tm_hour = 0;
444   t->tm_mday = day;
445   t->tm_mon  = month-1;
446   t->tm_year = year - 1900;
447   t->tm_isdst = -1;
448   return 0;
449 }
450
451
452 /* This function is a copy of gpgme/src/conversion.c:_gpgme_timegm.
453    If you change it, then update the other one too.  */
454 #ifdef HAVE_W32_SYSTEM
455 static time_t
456 _win32_timegm (struct tm *tm)
457 {
458   /* This one is thread safe.  */
459   SYSTEMTIME st;
460   FILETIME ft;
461   unsigned long long cnsecs;
462
463   st.wYear   = tm->tm_year + 1900;
464   st.wMonth  = tm->tm_mon  + 1;
465   st.wDay    = tm->tm_mday;
466   st.wHour   = tm->tm_hour;
467   st.wMinute = tm->tm_min;
468   st.wSecond = tm->tm_sec;
469   st.wMilliseconds = 0; /* Not available.  */
470   st.wDayOfWeek = 0;    /* Ignored.  */
471
472   /* System time is UTC thus the conversion is pretty easy.  */
473   if (!SystemTimeToFileTime (&st, &ft))
474     {
475       gpg_err_set_errno (EINVAL);
476       return (time_t)(-1);
477     }
478
479   cnsecs = (((unsigned long long)ft.dwHighDateTime << 32)
480             | ft.dwLowDateTime);
481   cnsecs -= 116444736000000000ULL; /* The filetime epoch is 1601-01-01.  */
482   return (time_t)(cnsecs / 10000000ULL);
483 }
484 #endif
485
486
487 /* Parse the string TIMESTAMP into a time_t.  The string may either be
488    seconds since Epoch or in the ISO 8601 format like
489    "20390815T143012".  Returns 0 for an empty string or seconds since
490    Epoch. Leading spaces are skipped. If ENDP is not NULL, it will
491    point to the next non-parsed character in TIMESTRING.
492
493    This function is a copy of
494    gpgme/src/conversion.c:_gpgme_parse_timestamp.  If you change it,
495    then update the other one too.  */
496 time_t
497 parse_timestamp (const char *timestamp, char **endp)
498 {
499   /* Need to skip leading spaces, because that is what strtoul does
500      but not our ISO 8601 checking code. */
501   while (*timestamp && *timestamp== ' ')
502     timestamp++;
503   if (!*timestamp)
504     return 0;
505
506   if (strlen (timestamp) >= 15 && timestamp[8] == 'T')
507     {
508       struct tm buf;
509       int year;
510
511       year = atoi_4 (timestamp);
512       if (year < 1900)
513         return (time_t)(-1);
514
515       if (endp)
516         *endp = (char*)(timestamp + 15);
517
518       /* Fixme: We would better use a configure test to see whether
519          mktime can handle dates beyond 2038. */
520       if (sizeof (time_t) <= 4 && year >= 2038)
521         return (time_t)2145914603; /* 2037-12-31 23:23:23 */
522
523       memset (&buf, 0, sizeof buf);
524       buf.tm_year = year - 1900;
525       buf.tm_mon = atoi_2 (timestamp+4) - 1;
526       buf.tm_mday = atoi_2 (timestamp+6);
527       buf.tm_hour = atoi_2 (timestamp+9);
528       buf.tm_min = atoi_2 (timestamp+11);
529       buf.tm_sec = atoi_2 (timestamp+13);
530
531 #ifdef HAVE_W32_SYSTEM
532       return _win32_timegm (&buf);
533 #else
534 #ifdef HAVE_TIMEGM
535       return timegm (&buf);
536 #else
537       {
538         time_t tim;
539
540         putenv ("TZ=UTC");
541         tim = mktime (&buf);
542 #ifdef __GNUC__
543 #warning fixme: we must somehow reset TZ here.  It is not threadsafe anyway.
544 #endif
545         return tim;
546       }
547 #endif /* !HAVE_TIMEGM */
548 #endif /* !HAVE_W32_SYSTEM */
549     }
550   else
551     return (time_t)strtoul (timestamp, endp, 10);
552 }
553
554
555
556 u32
557 add_days_to_timestamp( u32 stamp, u16 days )
558 {
559     return stamp + days*86400L;
560 }
561
562
563 /****************
564  * Return a string with a time value in the form: x Y, n D, n H
565  */
566
567 const char *
568 strtimevalue( u32 value )
569 {
570     static char buffer[30];
571     unsigned int years, days, hours, minutes;
572
573     value /= 60;
574     minutes = value % 60;
575     value /= 60;
576     hours = value % 24;
577     value /= 24;
578     days = value % 365;
579     value /= 365;
580     years = value;
581
582     sprintf(buffer,"%uy%ud%uh%um", years, days, hours, minutes );
583     if( years )
584         return buffer;
585     if( days )
586         return strchr( buffer, 'y' ) + 1;
587     return strchr( buffer, 'd' ) + 1;
588 }
589
590
591
592 /* Return a malloced string with the time elapsed between NOW and
593    SINCE.  May return NULL on error. */
594 char *
595 elapsed_time_string (time_t since, time_t now)
596 {
597   char *result;
598   double diff;
599   unsigned long value;
600   unsigned int days, hours, minutes, seconds;
601
602   if (!now)
603     now = gnupg_get_time ();
604
605   diff = difftime (now, since);
606   if (diff < 0)
607     return xtrystrdup ("time-warp");
608
609   seconds = (unsigned long)diff % 60;
610   value = (unsigned long)(diff / 60);
611   minutes = value % 60;
612   value /= 60;
613   hours = value % 24;
614   value /= 24;
615   days = value % 365;
616
617   if (days)
618     result = xtryasprintf ("%ud%uh%um%us", days, hours, minutes, seconds);
619   else if (hours)
620     result = xtryasprintf ("%uh%um%us", hours, minutes, seconds);
621   else if (minutes)
622     result = xtryasprintf ("%um%us", minutes, seconds);
623   else
624     result = xtryasprintf ("%us", seconds);
625
626   return result;
627 }
628
629
630 /*
631  * Note: this function returns GMT
632  */
633 const char *
634 strtimestamp (u32 stamp)
635 {
636   static char buffer[11+5];
637   struct tm *tp;
638   time_t atime = stamp;
639
640   if (IS_INVALID_TIME_T (atime))
641     {
642       strcpy (buffer, "????" "-??" "-??");
643     }
644   else
645     {
646       tp = gmtime( &atime );
647       snprintf (buffer, sizeof buffer, "%04d-%02d-%02d",
648                 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
649     }
650   return buffer;
651 }
652
653
654 /*
655  * Note: this function returns GMT
656  */
657 const char *
658 isotimestamp (u32 stamp)
659 {
660   static char buffer[25+5];
661   struct tm *tp;
662   time_t atime = stamp;
663
664   if (IS_INVALID_TIME_T (atime))
665     {
666       strcpy (buffer, "????" "-??" "-??" " " "??" ":" "??" ":" "??");
667     }
668   else
669     {
670       tp = gmtime ( &atime );
671       snprintf (buffer, sizeof buffer, "%04d-%02d-%02d %02d:%02d:%02d",
672                 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
673                 tp->tm_hour, tp->tm_min, tp->tm_sec);
674     }
675   return buffer;
676 }
677
678
679 /****************
680  * Note: this function returns local time
681  */
682 const char *
683 asctimestamp (u32 stamp)
684 {
685   static char buffer[50];
686 #if defined (HAVE_STRFTIME) && defined (HAVE_NL_LANGINFO)
687   static char fmt[50];
688 #endif
689   struct tm *tp;
690   time_t atime = stamp;
691
692   if (IS_INVALID_TIME_T (atime))
693     {
694       strcpy (buffer, "????" "-??" "-??");
695       return buffer;
696     }
697
698   tp = localtime( &atime );
699 #ifdef HAVE_STRFTIME
700 # if defined(HAVE_NL_LANGINFO)
701   mem2str( fmt, nl_langinfo(D_T_FMT), DIM(fmt)-3 );
702   if (!strstr( fmt, "%Z" ))
703     strcat( fmt, " %Z");
704   /* NOTE: gcc -Wformat-noliteral will complain here.  I have found no
705      way to suppress this warning.  */
706   strftime (buffer, DIM(buffer)-1, fmt, tp);
707 # elif defined(HAVE_W32CE_SYSTEM)
708   /* tzset is not available but %Z nevertheless prints a default
709      nonsense timezone ("WILDABBR").  Thus we don't print the time
710      zone at all.  */
711   strftime (buffer, DIM(buffer)-1, "%c", tp);
712 # else
713    /* FIXME: we should check whether the locale appends a " %Z" These
714     * locales from glibc don't put the " %Z": fi_FI hr_HR ja_JP lt_LT
715     * lv_LV POSIX ru_RU ru_SU sv_FI sv_SE zh_CN.  */
716   strftime (buffer, DIM(buffer)-1, "%c %Z", tp);
717 # endif
718   buffer[DIM(buffer)-1] = 0;
719 #else
720   mem2str( buffer, asctime(tp), DIM(buffer) );
721 #endif
722   return buffer;
723 }
724
725
726
727 static int
728 days_per_year (int y)
729 {
730   int s ;
731
732   s = !(y % 4);
733   if ( !(y % 100))
734     if ((y%400))
735       s = 0;
736   return s ? 366 : 365;
737 }
738
739 static int
740 days_per_month (int y, int m)
741 {
742   int s;
743
744   switch(m)
745     {
746     case 1: case 3: case 5: case 7: case 8: case 10: case 12:
747       return 31 ;
748     case 2:
749       s = !(y % 4);
750       if (!(y % 100))
751         if ((y % 400))
752           s = 0;
753       return s? 29 : 28 ;
754     case 4: case 6: case 9: case 11:
755       return 30;
756     }
757   BUG();
758 }
759
760
761 /* Convert YEAR, MONTH and DAY into the Julian date.  We assume that
762    it is already noon.  We do not support dates before 1582-10-15. */
763 static unsigned long
764 date2jd (int year, int month, int day)
765 {
766   unsigned long jd;
767
768   jd = 365L * year + 31 * (month-1) + day + JD_DIFF;
769   if (month < 3)
770     year-- ;
771   else
772     jd -= (4 * month + 23) / 10;
773
774   jd += year / 4 - ((year / 100 + 1) *3) / 4;
775
776   return jd ;
777 }
778
779 /* Convert a Julian date back to YEAR, MONTH and DAY.  Return day of
780    the year or 0 on error.  This function uses some more or less
781    arbitrary limits, most important is that days before 1582 are not
782    supported. */
783 static int
784 jd2date (unsigned long jd, int *year, int *month, int *day)
785 {
786   int y, m, d;
787   long delta;
788
789   if (!jd)
790     return 0 ;
791   if (jd < 1721425 || jd > 2843085)
792     return 0;
793
794   y = (jd - JD_DIFF) / 366;
795   d = m = 1;
796
797   while ((delta = jd - date2jd (y, m, d)) > days_per_year (y))
798     y++;
799
800   m = (delta / 31) + 1;
801   while( (delta = jd - date2jd (y, m, d)) > days_per_month (y,m))
802     if (++m > 12)
803       {
804         m = 1;
805         y++;
806       }
807
808   d = delta + 1 ;
809   if (d > days_per_month (y, m))
810     {
811       d = 1;
812       m++;
813     }
814   if (m > 12)
815     {
816       m = 1;
817       y++;
818     }
819
820   if (year)
821     *year = y;
822   if (month)
823     *month = m;
824   if (day)
825     *day = d ;
826
827   return (jd - date2jd (y, 1, 1)) + 1;
828 }
829
830
831 /* Check that the 15 bytes in ATIME represent a valid ISO time.  Note
832    that this function does not expect a string but a plain 15 byte
833    isotime buffer. */
834 gpg_error_t
835 check_isotime (const gnupg_isotime_t atime)
836 {
837   int i;
838   const char *s;
839
840   if (!*atime)
841     return gpg_error (GPG_ERR_NO_VALUE);
842
843   for (s=atime, i=0; i < 8; i++, s++)
844     if (!digitp (s))
845       return gpg_error (GPG_ERR_INV_TIME);
846   if (*s != 'T')
847       return gpg_error (GPG_ERR_INV_TIME);
848   for (s++, i=9; i < 15; i++, s++)
849     if (!digitp (s))
850       return gpg_error (GPG_ERR_INV_TIME);
851   return 0;
852 }
853
854
855 /* Dump the ISO time T to the log stream without a LF.  */
856 void
857 dump_isotime (const gnupg_isotime_t t)
858 {
859   if (!t || !*t)
860     log_printf ("%s", _("[none]"));
861   else
862     log_printf ("%.4s-%.2s-%.2s %.2s:%.2s:%s",
863                 t, t+4, t+6, t+9, t+11, t+13);
864 }
865
866
867 /* Copy one ISO date to another, this is inline so that we can do a
868    minimal sanity check.  A null date (empty string) is allowed.  */
869 void
870 gnupg_copy_time (gnupg_isotime_t d, const gnupg_isotime_t s)
871 {
872   if (*s)
873     {
874       if ((strlen (s) != 15 || s[8] != 'T'))
875         BUG();
876       memcpy (d, s, 15);
877       d[15] = 0;
878     }
879   else
880     *d = 0;
881 }
882
883
884 /* Add SECONDS to ATIME.  SECONDS may not be negative and is limited
885    to about the equivalent of 62 years which should be more then
886    enough for our purposes. */
887 gpg_error_t
888 add_seconds_to_isotime (gnupg_isotime_t atime, int nseconds)
889 {
890   gpg_error_t err;
891   int year, month, day, hour, minute, sec, ndays;
892   unsigned long jd;
893
894   err = check_isotime (atime);
895   if (err)
896     return err;
897
898   if (nseconds < 0 || nseconds >= (0x7fffffff - 61) )
899     return gpg_error (GPG_ERR_INV_VALUE);
900
901   year  = atoi_4 (atime+0);
902   month = atoi_2 (atime+4);
903   day   = atoi_2 (atime+6);
904   hour  = atoi_2 (atime+9);
905   minute= atoi_2 (atime+11);
906   sec   = atoi_2 (atime+13);
907
908   if (year <= 1582) /* The julian date functions don't support this. */
909     return gpg_error (GPG_ERR_INV_VALUE);
910
911   sec    += nseconds;
912   minute += sec/60;
913   sec    %= 60;
914   hour   += minute/60;
915   minute %= 60;
916   ndays  = hour/24;
917   hour   %= 24;
918
919   jd = date2jd (year, month, day) + ndays;
920   jd2date (jd, &year, &month, &day);
921
922   if (year > 9999 || month > 12 || day > 31
923       || year < 0 || month < 1 || day < 1)
924     return gpg_error (GPG_ERR_INV_VALUE);
925
926   snprintf (atime, 16, "%04d%02d%02dT%02d%02d%02d",
927             year, month, day, hour, minute, sec);
928   return 0;
929 }
930
931
932 gpg_error_t
933 add_days_to_isotime (gnupg_isotime_t atime, int ndays)
934 {
935   gpg_error_t err;
936   int year, month, day, hour, minute, sec;
937   unsigned long jd;
938
939   err = check_isotime (atime);
940   if (err)
941     return err;
942
943   if (ndays < 0 || ndays >= 9999*366 )
944     return gpg_error (GPG_ERR_INV_VALUE);
945
946   year  = atoi_4 (atime+0);
947   month = atoi_2 (atime+4);
948   day   = atoi_2 (atime+6);
949   hour  = atoi_2 (atime+9);
950   minute= atoi_2 (atime+11);
951   sec   = atoi_2 (atime+13);
952
953   if (year <= 1582) /* The julian date functions don't support this. */
954     return gpg_error (GPG_ERR_INV_VALUE);
955
956   jd = date2jd (year, month, day) + ndays;
957   jd2date (jd, &year, &month, &day);
958
959   if (year > 9999 || month > 12 || day > 31
960       || year < 0 || month < 1 || day < 1)
961     return gpg_error (GPG_ERR_INV_VALUE);
962
963   snprintf (atime, 16, "%04d%02d%02dT%02d%02d%02d",
964             year, month, day, hour, minute, sec);
965   return 0;
966 }