common: New function rfctimestamp.
[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 /* Return the timestamp STAMP in RFC-2822 format.  This is always done
727  * in the C locale.  We return the gmtime to avoid computing the
728  * timezone. The caller must release the returned string.
729  *
730  * Example: "Mon, 27 Jun 2016 1:42:00 +0000".
731  */
732 char *
733 rfctimestamp (u32 stamp)
734 {
735   time_t atime = stamp;
736   struct tm tmbuf, *tp;
737
738
739   if (IS_INVALID_TIME_T (atime))
740     {
741       gpg_err_set_errno (EINVAL);
742       return NULL;
743     }
744
745   tp = gnupg_gmtime (&atime, &tmbuf);
746   if (!tp)
747     return NULL;
748   return xtryasprintf ("%.3s, %02d %.3s %04d %02d:%02d:%02d +0000",
749                        ("SunMonTueWedThuFriSat" + (tp->tm_wday%7)*3),
750                        tp->tm_mday,
751                        ("JanFebMarAprMayJunJulAugSepOctNovDec"
752                         + (tp->tm_mon%12)*3),
753                        tp->tm_year + 1900,
754                        tp->tm_hour,
755                        tp->tm_min,
756                        tp->tm_sec);
757 }
758
759
760 static int
761 days_per_year (int y)
762 {
763   int s ;
764
765   s = !(y % 4);
766   if ( !(y % 100))
767     if ((y%400))
768       s = 0;
769   return s ? 366 : 365;
770 }
771
772 static int
773 days_per_month (int y, int m)
774 {
775   int s;
776
777   switch(m)
778     {
779     case 1: case 3: case 5: case 7: case 8: case 10: case 12:
780       return 31 ;
781     case 2:
782       s = !(y % 4);
783       if (!(y % 100))
784         if ((y % 400))
785           s = 0;
786       return s? 29 : 28 ;
787     case 4: case 6: case 9: case 11:
788       return 30;
789     }
790   BUG();
791 }
792
793
794 /* Convert YEAR, MONTH and DAY into the Julian date.  We assume that
795    it is already noon.  We do not support dates before 1582-10-15. */
796 static unsigned long
797 date2jd (int year, int month, int day)
798 {
799   unsigned long jd;
800
801   jd = 365L * year + 31 * (month-1) + day + JD_DIFF;
802   if (month < 3)
803     year-- ;
804   else
805     jd -= (4 * month + 23) / 10;
806
807   jd += year / 4 - ((year / 100 + 1) *3) / 4;
808
809   return jd ;
810 }
811
812 /* Convert a Julian date back to YEAR, MONTH and DAY.  Return day of
813    the year or 0 on error.  This function uses some more or less
814    arbitrary limits, most important is that days before 1582 are not
815    supported. */
816 static int
817 jd2date (unsigned long jd, int *year, int *month, int *day)
818 {
819   int y, m, d;
820   long delta;
821
822   if (!jd)
823     return 0 ;
824   if (jd < 1721425 || jd > 2843085)
825     return 0;
826
827   y = (jd - JD_DIFF) / 366;
828   d = m = 1;
829
830   while ((delta = jd - date2jd (y, m, d)) > days_per_year (y))
831     y++;
832
833   m = (delta / 31) + 1;
834   while( (delta = jd - date2jd (y, m, d)) > days_per_month (y,m))
835     if (++m > 12)
836       {
837         m = 1;
838         y++;
839       }
840
841   d = delta + 1 ;
842   if (d > days_per_month (y, m))
843     {
844       d = 1;
845       m++;
846     }
847   if (m > 12)
848     {
849       m = 1;
850       y++;
851     }
852
853   if (year)
854     *year = y;
855   if (month)
856     *month = m;
857   if (day)
858     *day = d ;
859
860   return (jd - date2jd (y, 1, 1)) + 1;
861 }
862
863
864 /* Check that the 15 bytes in ATIME represent a valid ISO time.  Note
865    that this function does not expect a string but a plain 15 byte
866    isotime buffer. */
867 gpg_error_t
868 check_isotime (const gnupg_isotime_t atime)
869 {
870   int i;
871   const char *s;
872
873   if (!*atime)
874     return gpg_error (GPG_ERR_NO_VALUE);
875
876   for (s=atime, i=0; i < 8; i++, s++)
877     if (!digitp (s))
878       return gpg_error (GPG_ERR_INV_TIME);
879   if (*s != 'T')
880       return gpg_error (GPG_ERR_INV_TIME);
881   for (s++, i=9; i < 15; i++, s++)
882     if (!digitp (s))
883       return gpg_error (GPG_ERR_INV_TIME);
884   return 0;
885 }
886
887
888 /* Dump the ISO time T to the log stream without a LF.  */
889 void
890 dump_isotime (const gnupg_isotime_t t)
891 {
892   if (!t || !*t)
893     log_printf ("%s", _("[none]"));
894   else
895     log_printf ("%.4s-%.2s-%.2s %.2s:%.2s:%s",
896                 t, t+4, t+6, t+9, t+11, t+13);
897 }
898
899
900 /* Copy one ISO date to another, this is inline so that we can do a
901    minimal sanity check.  A null date (empty string) is allowed.  */
902 void
903 gnupg_copy_time (gnupg_isotime_t d, const gnupg_isotime_t s)
904 {
905   if (*s)
906     {
907       if ((strlen (s) != 15 || s[8] != 'T'))
908         BUG();
909       memcpy (d, s, 15);
910       d[15] = 0;
911     }
912   else
913     *d = 0;
914 }
915
916
917 /* Add SECONDS to ATIME.  SECONDS may not be negative and is limited
918    to about the equivalent of 62 years which should be more then
919    enough for our purposes. */
920 gpg_error_t
921 add_seconds_to_isotime (gnupg_isotime_t atime, int nseconds)
922 {
923   gpg_error_t err;
924   int year, month, day, hour, minute, sec, ndays;
925   unsigned long jd;
926
927   err = check_isotime (atime);
928   if (err)
929     return err;
930
931   if (nseconds < 0 || nseconds >= (0x7fffffff - 61) )
932     return gpg_error (GPG_ERR_INV_VALUE);
933
934   year  = atoi_4 (atime+0);
935   month = atoi_2 (atime+4);
936   day   = atoi_2 (atime+6);
937   hour  = atoi_2 (atime+9);
938   minute= atoi_2 (atime+11);
939   sec   = atoi_2 (atime+13);
940
941   if (year <= 1582) /* The julian date functions don't support this. */
942     return gpg_error (GPG_ERR_INV_VALUE);
943
944   sec    += nseconds;
945   minute += sec/60;
946   sec    %= 60;
947   hour   += minute/60;
948   minute %= 60;
949   ndays  = hour/24;
950   hour   %= 24;
951
952   jd = date2jd (year, month, day) + ndays;
953   jd2date (jd, &year, &month, &day);
954
955   if (year > 9999 || month > 12 || day > 31
956       || year < 0 || month < 1 || day < 1)
957     return gpg_error (GPG_ERR_INV_VALUE);
958
959   snprintf (atime, 16, "%04d%02d%02dT%02d%02d%02d",
960             year, month, day, hour, minute, sec);
961   return 0;
962 }
963
964
965 gpg_error_t
966 add_days_to_isotime (gnupg_isotime_t atime, int ndays)
967 {
968   gpg_error_t err;
969   int year, month, day, hour, minute, sec;
970   unsigned long jd;
971
972   err = check_isotime (atime);
973   if (err)
974     return err;
975
976   if (ndays < 0 || ndays >= 9999*366 )
977     return gpg_error (GPG_ERR_INV_VALUE);
978
979   year  = atoi_4 (atime+0);
980   month = atoi_2 (atime+4);
981   day   = atoi_2 (atime+6);
982   hour  = atoi_2 (atime+9);
983   minute= atoi_2 (atime+11);
984   sec   = atoi_2 (atime+13);
985
986   if (year <= 1582) /* The julian date functions don't support this. */
987     return gpg_error (GPG_ERR_INV_VALUE);
988
989   jd = date2jd (year, month, day) + ndays;
990   jd2date (jd, &year, &month, &day);
991
992   if (year > 9999 || month > 12 || day > 31
993       || year < 0 || month < 1 || day < 1)
994     return gpg_error (GPG_ERR_INV_VALUE);
995
996   snprintf (atime, 16, "%04d%02d%02dT%02d%02d%02d",
997             year, month, day, hour, minute, sec);
998   return 0;
999 }