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