varnish-cache/lib/libvarnish/vtim.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2011 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 *
30
 * Semi-trivial functions to handle HTTP header timestamps according to
31
 * RFC 2616 section 3.3.
32
 *
33
 * We must parse four different formats:
34
 *       000000000011111111112222222222
35
 *       012345678901234567890123456789
36
 *       ------------------------------
37
 *      "Sun, 06 Nov 1994 08:49:37 GMT"         RFC822 & RFC1123
38
 *      "Sunday, 06-Nov-94 08:49:37 GMT"        RFC850
39
 *      "Sun Nov  6 08:49:37 1994"              ANSI-C asctime()
40
 *      "1994-11-06T08:49:37"                   ISO 8601
41
 *
42
 * And always output the RFC1123 format.
43
 *
44
 * So why are these functions hand-built ?
45
 *
46
 * Because the people behind POSIX were short-sighted morons who didn't think
47
 * anybody would ever need to deal with timestamps in multiple different
48
 * timezones at the same time -- for that matter, convert timestamps to
49
 * broken down UTC/GMT time.
50
 *
51
 * We could, and used to, get by by smashing our TZ variable to "UTC" but
52
 * that ruins the LOCALE for VMODs.
53
 *
54
 */
55
56
#include "config.h"
57
58
#include <time.h>
59
#include <sys/time.h>
60
61
#include <math.h>
62
#include <stdint.h>
63
#include <stdio.h>
64
#include <stdlib.h>
65
#include <string.h>
66
#ifdef __MACH__
67
#include <mach/mach_time.h>
68
#endif
69
70
#include "vdef.h"
71
72
#include "vas.h"
73
#include "vtim.h"
74
75
/* relax vtim parsing */
76
unsigned VTIM_postel = 0;
77
78
static const char * const weekday_name[] = {
79
        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
80
};
81
82
static const char * const more_weekday[] = {
83
        "day", "day", "sday", "nesday", "rsday", "day", "urday"
84
};
85
86
static const char * const month_name[] = {
87
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
88
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
89
};
90
91
static const int days_in_month[] = {
92
        31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
93
};
94
95
static const int days_before_month[] = {
96
        0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
97
};
98
99
#ifdef __MACH__
100
// http://stackoverflow.com/a/21352348
101
static uint64_t mt_base;
102
static double   mt_scale;
103
104
static void
105
mach_time_init(void)
106
{
107
        mach_timebase_info_data_t timebase;
108
109
        mt_base = mach_absolute_time();
110
111
        AZ(mach_timebase_info(&timebase));
112
        mt_scale = (double)timebase.numer / (double)timebase.denom * 1e-9;
113
}
114
115
static __attribute__((constructor)) void
116
init(void)
117
{
118
        mach_time_init();
119
}
120
#endif
121
122
/*
123
 * On older Solaris-incarnations, gethrtime() was faster than
124
 * clock_gettime(CLOCK_MONOTONIC). Our configure script prefers
125
 * clock_gettime if it is consistently at least twice as fast as
126
 * gethrtime(), which is the case on modern Solaris descendents.
127
 */
128
129
vtim_mono
130 17476432
VTIM_mono(void)
131
{
132
#if defined(HAVE_CLOCK_GETTIME) && !defined(USE_GETHRTIME)
133
        struct timespec ts;
134
135 17476432
        AZ(clock_gettime(CLOCK_MONOTONIC, &ts));
136 17476432
        return (ts.tv_sec + 1e-9 * ts.tv_nsec);
137
#elif defined(HAVE_GETHRTIME)
138
        return (gethrtime() * 1e-9);
139
#elif defined(__MACH__)
140
        uint64_t mt = mach_absolute_time() - mt_base;
141
142
        return (mt * mt_scale);
143
#else
144
#error Varnish needs some monotonic time source
145
#endif
146
}
147
148
vtim_real
149 6753897
VTIM_real(void)
150
{
151
#ifdef HAVE_CLOCK_GETTIME
152
        struct timespec ts;
153
154 6753897
        AZ(clock_gettime(CLOCK_REALTIME, &ts));
155 6753897
        return (ts.tv_sec + 1e-9 * ts.tv_nsec);
156
#else
157
        struct timeval tv;
158
159
        AZ(gettimeofday(&tv, NULL));
160
        return (tv.tv_sec + 1e-6 * tv.tv_usec);
161
#endif
162
}
163
164
void
165 154412322
VTIM_format(vtim_real t, char *p)
166
{
167
        struct tm tm;
168
        time_t tt;
169
170 154412322
        AN(p);
171 154412322
        *p = '\0';
172
173 154412322
        if (t < (vtim_real)INTMAX_MIN || t > (vtim_real)INTMAX_MAX)
174 27
                return;
175
176 154412297
        tt = (time_t)(intmax_t)t;
177 154412297
        if (gmtime_r(&tt, &tm) == NULL)
178 0
                return;
179
180 154412297
        AN(snprintf(p, VTIM_FORMAT_SIZE,
181
            "%s, %02d %s %4d %02d:%02d:%02d GMT",
182
            weekday_name[tm.tm_wday],
183
            tm.tm_mday, month_name[tm.tm_mon], tm.tm_year + 1900,
184
            tm.tm_hour, tm.tm_min, tm.tm_sec));
185 154412324
}
186
187
#ifdef TEST_DRIVER
188
#define FAIL()  \
189
        do { printf("\nFAIL <<%d>>\n", __LINE__); return (0); } while (0)
190
#else
191
#define FAIL()  \
192
        do { return (0); } while (0)
193
#endif
194
195
#define DIGIT(mult, fld)                                        \
196
        do {                                                    \
197
                if (*p < '0' || *p > '9')                       \
198
                        FAIL();                                 \
199
                fld += (*p - '0') * mult;                       \
200
                p++;                                            \
201
        } while(0)
202
203
#define MUSTBE(chr)                                             \
204
        do {                                                    \
205
                if (*p != chr)                                  \
206
                        FAIL();                                 \
207
                p++;                                            \
208
        } while(0)
209
210
#define WEEKDAY()                                               \
211
        do {                                                    \
212
                int i;                                          \
213
                for (i = 0; i < 7; i++) {                       \
214
                        if (!memcmp(p, weekday_name[i], 3)) {   \
215
                                weekday = i;                    \
216
                                break;                          \
217
                        }                                       \
218
                }                                               \
219
                if (i == 7)                                     \
220
                        FAIL();                                 \
221
                p += 3;                                         \
222
        } while(0)
223
224
225
#define MONTH()                                                 \
226
        do {                                                    \
227
                int i;                                          \
228
                for (i = 0; i < 12; i++) {                      \
229
                        if (!memcmp(p, month_name[i], 3)) {     \
230
                                month = i + 1;                  \
231
                                break;                          \
232
                        }                                       \
233
                }                                               \
234
                if (i == 12)                                    \
235
                        FAIL();                                 \
236
                p += 3;                                         \
237
        } while(0)
238
239
#define TIMESTAMP()                                             \
240
        do {                                                    \
241
                DIGIT(10, hour);                                \
242
                DIGIT(1, hour);                                 \
243
                MUSTBE(':');                                    \
244
                DIGIT(10, min);                                 \
245
                DIGIT(1, min);                                  \
246
                MUSTBE(':');                                    \
247
                DIGIT(10, sec);                                 \
248
                DIGIT(1, sec);                                  \
249
        } while(0)
250
251
vtim_real
252 484868424
VTIM_parse(const char *p)
253
{
254
        vtim_real t;
255 484868424
        int month = 0, year = 0, weekday = -1, mday = 0;
256 484868424
        int hour = 0, min = 0, sec = 0;
257
        int d, leap;
258
259 484868424
        if (p == NULL || *p == '\0')
260 25
                FAIL();
261
262 484868449
        while (*p == ' ')
263 50
                p++;
264
265 484868399
        if (*p >= '0' && *p <= '9') {
266
                /* ISO8601 -- "1994-11-06T08:49:37" */
267 154278525
                DIGIT(1000, year);
268 154278525
                DIGIT(100, year);
269 154278525
                DIGIT(10, year);
270 154278525
                DIGIT(1, year);
271 154278525
                MUSTBE('-');
272 154278175
                DIGIT(10, month);
273 154278175
                DIGIT(1, month);
274 154278175
                MUSTBE('-');
275 154278175
                DIGIT(10, mday);
276 154278175
                DIGIT(1, mday);
277 154278175
                MUSTBE('T');
278 154278175
                TIMESTAMP();
279 154278175
        } else {
280 1322166345
                WEEKDAY();
281 330589774
                assert(weekday >= 0 && weekday <= 6);
282 330589774
                if (*p == ',') {
283
                        /* RFC822 & RFC1123 - "Sun, 06 Nov 1994 08:49:37 GMT" */
284 154340498
                        p++;
285 154340498
                        MUSTBE(' ');
286 154340498
                        if (VTIM_postel && *p && p[1] == ' ')
287 100
                                DIGIT(1, mday);
288
                        else {
289 154340398
                                DIGIT(10, mday);
290 154340398
                                DIGIT(1, mday);
291
                        }
292 154340498
                        MUSTBE(' ');
293 1006545390
                        MONTH();
294 154340473
                        MUSTBE(' ');
295 154340473
                        DIGIT(1000, year);
296 154340473
                        DIGIT(100, year);
297 154340473
                        DIGIT(10, year);
298 154340473
                        DIGIT(1, year);
299 154340473
                        MUSTBE(' ');
300 154340473
                        TIMESTAMP();
301 154340473
                        MUSTBE(' ');
302 154340473
                        MUSTBE('G');
303 154340448
                        MUSTBE('M');
304 154340448
                        MUSTBE('T');
305 330589724
                } else if (*p == ' ') {
306
                        /* ANSI-C asctime() -- "Sun Nov  6 08:49:37 1994" */
307 154302976
                        p++;
308 1006443301
                        MONTH();
309 154302876
                        MUSTBE(' ');
310 154302876
                        if (*p != ' ')
311 108657050
                                DIGIT(10, mday);
312
                        else
313 45645826
                                p++;
314 154302876
                        DIGIT(1, mday);
315 154302876
                        MUSTBE(' ');
316 154302876
                        TIMESTAMP();
317 154302876
                        MUSTBE(' ');
318 154302876
                        DIGIT(1000, year);
319 154302876
                        DIGIT(100, year);
320 154302876
                        DIGIT(10, year);
321 154302876
                        DIGIT(1, year);
322 198195476
                } else if (!memcmp(p, more_weekday[weekday],
323 21946300
                    strlen(more_weekday[weekday]))) {
324
                        /* RFC850 -- "Sunday, 06-Nov-94 08:49:37 GMT" */
325 21946200
                        p += strlen(more_weekday[weekday]);
326 21946200
                        MUSTBE(',');
327 21946200
                        MUSTBE(' ');
328 21946200
                        DIGIT(10, mday);
329 21946200
                        DIGIT(1, mday);
330 21946200
                        MUSTBE('-');
331 143265225
                        MONTH();
332 21946200
                        MUSTBE('-');
333 21946200
                        DIGIT(10, year);
334 21946200
                        DIGIT(1, year);
335 21946200
                        year += 1900;
336 21946200
                        if (year < 1969)
337 15126300
                                year += 100;
338 21946200
                        MUSTBE(' ');
339 21946200
                        TIMESTAMP();
340 21946200
                        MUSTBE(' ');
341 21946200
                        MUSTBE('G');
342 21946200
                        MUSTBE('M');
343 21946200
                        MUSTBE('T');
344 21946200
                } else
345 100
                        FAIL();
346
        }
347
348 484867799
        while (*p == ' ')
349 100
                p++;
350
351 484867699
        if (*p != '\0')
352 100
                FAIL();
353
354 484867599
        if (sec < 0 || sec > 60)        /* Leapseconds! */
355 100
                FAIL();
356 484867499
        if (min < 0 || min > 59)
357 102
                FAIL();
358 484867397
        if (hour < 0 || hour > 23)
359 100
                FAIL();
360 484867297
        if (month < 1 || month > 12)
361 100
                FAIL();
362 484867197
        if (mday < 1 || mday > days_in_month[month - 1])
363 100
                FAIL();
364 484867097
        if (year < 1899)
365 0
                FAIL();
366
367 484867097
        leap =
368 606363820
            ((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0);
369
370 484867097
        if (month == 2 && mday > 28 && !leap)
371 100
                FAIL();
372
373 484866997
        if (sec == 60)                  /* Ignore Leapseconds */
374 100
                sec--;
375
376 484866997
        t = ((hour * 60.) + min) * 60. + sec;
377
378 484866997
        d = (mday - 1) + days_before_month[month - 1];
379
380 484866997
        if (month > 2 && leap)
381 98260625
                d++;
382
383 484866997
        d += (year % 100) * 365;        /* There are 365 days in a year */
384
385 484866997
        if ((year % 100) > 0)           /* And a leap day every four years */
386 479386297
                d += (((year % 100) - 1) / 4);
387
388 484866997
        d += ((year / 100) - 20) *      /* Days relative to y2000 */
389
            (100 * 365 + 24);           /* 24 leapdays per year in a century */
390
391 484866997
        d += ((year - 1) / 400) - 4;    /* And one more every 400 years */
392
393
        /*
394
         * Now check weekday, if we have one.
395
         * 6 is because 2000-01-01 was a saturday.
396
         * 10000 is to make sure the modulus argument is always positive
397
         */
398 484866997
        if (weekday != -1 && (d + 6 + 7 * 10000) % 7 != weekday)
399 200
                FAIL();
400
401 484866797
        t += d * 86400.;
402
403 484866797
        t += 10957. * 86400.;           /* 10957 days frm UNIX epoch to y2000 */
404
405 484866797
        return (t);
406 484868422
}
407
408
void
409 2409964
VTIM_sleep(vtim_dur t)
410
{
411
#ifdef HAVE_NANOSLEEP
412
        struct timespec ts;
413
414 2409964
        ts = VTIM_timespec(t);
415
416 2409964
        (void)nanosleep(&ts, NULL);
417
#else
418
        if (t >= 1.) {
419
                (void)sleep(floor(t));
420
                t -= floor(t);
421
        }
422
        /* XXX: usleep() is not mandated to be thread safe */
423
        t *= 1e6;
424
        if (t > 0)
425
                (void)usleep(floor(t));
426
#endif
427 2409964
}
428
429
/*
430
 * VTIM_timeval and VTIM_timespec may need variants with different signatures
431
 * when vtim_real / vtim_mono typedefs are changed
432
 */
433
434
struct timeval
435 133250
VTIM_timeval(vtim_dur t)
436
{
437
        struct timeval tv;
438
439 133250
        AZ(isnan(t));
440 133250
        tv.tv_sec = (time_t)trunc(t);
441 133250
        tv.tv_usec = (int)(1e6 * (t - tv.tv_sec));
442 133250
        return (tv);
443
}
444
445
struct timespec
446 2882231
VTIM_timespec(vtim_dur t)
447
{
448
        struct timespec tv;
449
450 2882231
        AZ(isnan(t));
451 2882231
        tv.tv_sec = (time_t)trunc(t);
452 2882231
        tv.tv_nsec = (int)(1e9 * (t - tv.tv_sec));
453 2882231
        return (tv);
454
}
455
456
int
457 197413
VTIM_poll_tmo(vtim_dur tmo)
458
{
459
460 197413
        if (isnan(tmo))
461 75
                return (-1);
462 197338
        return (vmax_t(int, 0, ((int)(tmo * 1e3))));
463 197413
}
464
465
#ifdef TEST_DRIVER
466
467
#pragma GCC diagnostic ignored "-Wformat-y2k"
468
469
#include <stdint.h>
470
471
static void
472 100
tst(const char *s, time_t good)
473
{
474
        time_t t;
475
        char buf[BUFSIZ];
476
477 100
        t = VTIM_parse(s);
478 100
        VTIM_format(t, buf);
479 100
        printf("%-30s -> %12jd -> %s\n", s, (intmax_t)t, buf);
480 100
        if (t != good) {
481 0
                printf("Parse error! Got: %jd should have %jd diff %jd\n",
482 0
                    (intmax_t)t, (intmax_t)good, (intmax_t)(t - good));
483 0
                exit(4);
484
        }
485 100
}
486
487
/* XXX keep as double for the time being */
488
static int
489 50
tst_delta_check(const char *name, double begin, double end, vtim_dur ref)
490
{
491 50
        const double tol_max = 1.1;
492 50
        const double tol_min = 1;
493
494 50
        printf("%s delta for %fs sleep: %f\n", name, ref, (end - begin));
495
496 50
        if ((end - begin) > tol_max * ref) {
497 0
                printf("%s delta above tolerance: ((%f - %f) = %f) > %f\n",
498 0
                    name, end, begin, (end - begin), tol_max);
499 0
                return (1);
500 50
        } else if ((end - begin) < tol_min * ref) {
501 0
                printf("%s delta below tolerance: ((%f - %f) = %f) < %f\n",
502 0
                    name, end, begin, (end - begin), tol_min);
503 0
                return (1);
504
        }
505 50
        return (0);
506 50
}
507
508
static void
509 25
tst_delta(void)
510
{
511
        vtim_mono m_begin, m_end;
512
        vtim_real r_begin, r_end;
513 25
        const vtim_dur ref = 1;
514 25
        int err = 0;
515
516 25
        r_begin = VTIM_real();
517 25
        m_begin = VTIM_mono();
518 25
        VTIM_sleep(ref);
519 25
        r_end = VTIM_real();
520 25
        m_end = VTIM_mono();
521
522 25
        err += tst_delta_check("VTIM_mono", m_begin, m_end, ref);
523 25
        err += tst_delta_check("VTIM_real", r_begin, r_end, ref);
524
525 25
        if (err) {
526 0
                printf("%d time delta test errors\n", err);
527 0
                exit(4);
528
        }
529 25
}
530
531
static void
532 25
bench(void)
533
{
534
        vtim_mono s, e;
535
        vtim_mono t_m;
536
        vtim_real t_r;
537
        unsigned long t_i;
538
        int i;
539
        char buf[64];
540
541 25
        t_m = 0;
542 25
        t_r = 0;
543 25
        s = VTIM_mono();
544 2500025
        for (i=0; i<100000; i++)
545 2500000
                t_r += VTIM_real();
546 25
        e = VTIM_mono();
547 25
        printf("real: %fs / %d = %fns - tst val %f\n",
548 25
            e - s, i, 1e9 * (e - s) / i, t_r);
549
550 25
        t_i = 0;
551 25
        s = VTIM_mono();
552 2500025
        for (i=0; i<100000; i++)
553 2500000
                t_m += VTIM_mono();
554 25
        e = VTIM_mono();
555 25
        printf("mono: %fs / %d = %fns - tst val %f\n",
556 25
            e - s, i, 1e9 * (e - s) / i, t_m);
557
558 25
        t_i = 0;
559 25
        s = VTIM_mono();
560 2500025
        for (i=0; i<100000; i++) {
561 2500000
                snprintf(buf, sizeof(buf), "%.6f", s);
562 2500000
                t_i += buf[4];
563 2500000
        }
564 25
        e = VTIM_mono();
565 25
        printf("printf %%.6f: %fs / %d = %fns - tst val %lu %s\n",
566 25
            e - s, i, 1e9 * (e - s) / i, t_i, buf);
567
568 25
        t_i = 0;
569 25
        s = VTIM_mono();
570 2500025
        for (i=0; i<100000; i++) {
571 5000000
                snprintf(buf, sizeof(buf), "%ju.%06ju",
572 2500000
                    (uintmax_t)floor(s),
573 2500000
                    (uintmax_t)floor((s * 1e6)) % 1000000UL);
574 2500000
                t_i += buf[4];
575 2500000
        }
576 25
        e = VTIM_mono();
577 25
        printf("printf %%ju.%%06ju: %fs / %d = %fns - tst val %lu %s\n",
578 25
            e - s, i, 1e9 * (e - s) / i, t_i, buf);
579 25
}
580
581
static void
582 484754925
parse_check(time_t t, const char *s)
583
{
584
        vtim_real tt;
585
        char buf[BUFSIZ];
586
587 484754925
        tt = VTIM_parse(s);
588 484754925
        if (tt != t) {
589 0
                VTIM_format(tt, buf);
590 0
                printf("  fm: %12jd <%s>\n", (intmax_t)t, s);
591 0
                printf("  to: %12.0f <%s>\n", tt, buf);
592 0
                exit(2);
593
        }
594 484754925
}
595
596
#define TTEST_MIN (sizeof(time_t) >= 8 ? -2209852800 : INT32_MIN)
597
#define TTEST_MAX (sizeof(time_t) >= 8 ? 20000000000 : INT32_MAX)
598
599
int
600 25
main(int argc, char **argv)
601
{
602
        time_t t;
603
        intmax_t iter;
604
        struct tm tm;
605
        char buf[BUFSIZ];
606
        char buf1[BUFSIZ];
607
608 25
        (void)argc;
609 25
        (void)argv;
610
611 25
        AZ(setenv("TZ", "UTC", 1));
612
613 25
        bench();
614
615
        /* Brute force test against libc version */
616 154277975
        for (iter = TTEST_MIN; iter < TTEST_MAX; iter += 3599) {
617 154277950
                t = (time_t)iter;
618 154277950
                gmtime_r(&t, &tm);
619 154277950
                strftime(buf1, sizeof buf1, "%a, %d %b %Y %T GMT", &tm);
620 154277950
                VTIM_format(t, buf);
621 154277950
                if (strcmp(buf, buf1)) {
622 0
                        printf("libc: <%s> Vtim <%s> %jd\n",
623 0
                            buf1, buf, (intmax_t)t);
624 0
                        exit(2);
625
                }
626 154277950
                parse_check(t, buf1);
627
628 154277950
                strftime(buf1, sizeof buf1, "%a %b %e %T %Y", &tm);
629 154277950
                parse_check(t, buf1);
630
631 154277950
                strftime(buf1, sizeof buf1, "%Y-%m-%dT%T", &tm);
632 154277950
                parse_check(t, buf1);
633
634 154277950
                if (tm.tm_year >= 69 && tm.tm_year < 169) {
635 21921075
                        strftime(buf1, sizeof buf1, "%A, %d-%b-%y %T GMT", &tm);
636 21921075
                        parse_check(t, buf1);
637 21921075
                }
638 154277950
        }
639
640
        /* Examples from RFC2616 section 3.3.1 */
641 25
        tst("Sun, 06 Nov 1994 08:49:37 GMT", 784111777);
642 25
        tst("Sunday, 06-Nov-94 08:49:37 GMT", 784111777);
643 25
        tst("Sun Nov  6 08:49:37 1994", 784111777);
644
645 25
        tst("1994-11-06T08:49:37", 784111777);
646
647 25
        tst_delta();
648
649 25
        return (0);
650
}
651
652
#endif