~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/timezone/strftime.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 1989 The Regents of the University of California.
 
3
 * All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms are permitted
 
6
 * provided that the above copyright notice and this paragraph are
 
7
 * duplicated in all such forms and that any documentation,
 
8
 * advertising materials, and other materials related to such
 
9
 * distribution and use acknowledge that the software was developed
 
10
 * by the University of California, Berkeley.  The name of the
 
11
 * University may not be used to endorse or promote products derived
 
12
 * from this software without specific prior written permission.
 
13
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 
14
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 
15
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
16
 *
 
17
 * IDENTIFICATION
 
18
 *        $PostgreSQL: pgsql/src/timezone/strftime.c,v 1.5 2004-08-29 05:07:02 momjian Exp $
 
19
 */
 
20
 
 
21
#include "postgres.h"
 
22
 
 
23
#include <fcntl.h>
 
24
#include <locale.h>
 
25
 
 
26
#include "pgtz.h"
 
27
#include "private.h"
 
28
#include "tzfile.h"
 
29
 
 
30
 
 
31
struct lc_time_T
 
32
{
 
33
        const char *mon[MONSPERYEAR];
 
34
        const char *month[MONSPERYEAR];
 
35
        const char *wday[DAYSPERWEEK];
 
36
        const char *weekday[DAYSPERWEEK];
 
37
        const char *X_fmt;
 
38
        const char *x_fmt;
 
39
        const char *c_fmt;
 
40
        const char *am;
 
41
        const char *pm;
 
42
        const char *date_fmt;
 
43
};
 
44
 
 
45
#define Locale  (&C_time_locale)
 
46
 
 
47
static const struct lc_time_T C_time_locale = {
 
48
        {
 
49
                "Jan", "Feb", "Mar", "Apr", "May", "Jun",
 
50
                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
 
51
        }, {
 
52
                "January", "February", "March", "April", "May", "June",
 
53
                "July", "August", "September", "October", "November", "December"
 
54
        }, {
 
55
                "Sun", "Mon", "Tue", "Wed",
 
56
                "Thu", "Fri", "Sat"
 
57
        }, {
 
58
                "Sunday", "Monday", "Tuesday", "Wednesday",
 
59
                "Thursday", "Friday", "Saturday"
 
60
        },
 
61
 
 
62
        /* X_fmt */
 
63
        "%H:%M:%S",
 
64
 
 
65
        /*
 
66
         * x_fmt
 
67
         *
 
68
         * C99 requires this format. Using just numbers (as here) makes Quakers
 
69
         * happier; it's also compatible with SVR4.
 
70
         */
 
71
        "%m/%d/%y",
 
72
 
 
73
        /*
 
74
         * c_fmt
 
75
         *
 
76
         * C99 requires this format. Previously this code used "%D %X", but we
 
77
         * now conform to C99. Note that "%a %b %d %H:%M:%S %Y" is used by
 
78
         * Solaris 2.3.
 
79
         */
 
80
        "%a %b %e %T %Y",
 
81
 
 
82
        /* am */
 
83
        "AM",
 
84
 
 
85
        /* pm */
 
86
        "PM",
 
87
 
 
88
        /* date_fmt */
 
89
        "%a %b %e %H:%M:%S %Z %Y"
 
90
};
 
91
 
 
92
static char *_add(const char *, char *, const char *);
 
93
static char *_conv(int, const char *, char *, const char *);
 
94
static char *_fmt(const char *, const struct pg_tm *, char *,
 
95
         const char *, int *);
 
96
 
 
97
#define IN_NONE 0
 
98
#define IN_SOME 1
 
99
#define IN_THIS 2
 
100
#define IN_ALL  3
 
101
 
 
102
 
 
103
size_t
 
104
pg_strftime(char *s, size_t maxsize, const char *format,
 
105
                        const struct pg_tm * t)
 
106
{
 
107
        char       *p;
 
108
        int                     warn;
 
109
 
 
110
        warn = IN_NONE;
 
111
        p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
 
112
        if (p == s + maxsize)
 
113
                return 0;
 
114
        *p = '\0';
 
115
        return p - s;
 
116
}
 
117
 
 
118
static char *
 
119
_fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
 
120
         int *warnp)
 
121
{
 
122
        for (; *format; ++format)
 
123
        {
 
124
                if (*format == '%')
 
125
                {
 
126
        label:
 
127
                        switch (*++format)
 
128
                        {
 
129
                                case '\0':
 
130
                                        --format;
 
131
                                        break;
 
132
                                case 'A':
 
133
                                        pt = _add((t->tm_wday < 0 ||
 
134
                                                           t->tm_wday >= DAYSPERWEEK) ?
 
135
                                                          "?" : Locale->weekday[t->tm_wday],
 
136
                                                          pt, ptlim);
 
137
                                        continue;
 
138
                                case 'a':
 
139
                                        pt = _add((t->tm_wday < 0 ||
 
140
                                                           t->tm_wday >= DAYSPERWEEK) ?
 
141
                                                          "?" : Locale->wday[t->tm_wday],
 
142
                                                          pt, ptlim);
 
143
                                        continue;
 
144
                                case 'B':
 
145
                                        pt = _add((t->tm_mon < 0 ||
 
146
                                                           t->tm_mon >= MONSPERYEAR) ?
 
147
                                                          "?" : Locale->month[t->tm_mon],
 
148
                                                          pt, ptlim);
 
149
                                        continue;
 
150
                                case 'b':
 
151
                                case 'h':
 
152
                                        pt = _add((t->tm_mon < 0 ||
 
153
                                                           t->tm_mon >= MONSPERYEAR) ?
 
154
                                                          "?" : Locale->mon[t->tm_mon],
 
155
                                                          pt, ptlim);
 
156
                                        continue;
 
157
                                case 'C':
 
158
 
 
159
                                        /*
 
160
                                         * %C used to do a... _fmt("%a %b %e %X %Y", t);
 
161
                                         * ...whereas now POSIX 1003.2 calls for something
 
162
                                         * completely different. (ado, 1993-05-24)
 
163
                                         */
 
164
                                        pt = _conv((t->tm_year + TM_YEAR_BASE) / 100,
 
165
                                                           "%02d", pt, ptlim);
 
166
                                        continue;
 
167
                                case 'c':
 
168
                                        {
 
169
                                                int                     warn2 = IN_SOME;
 
170
 
 
171
                                                pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp);
 
172
                                                if (warn2 == IN_ALL)
 
173
                                                        warn2 = IN_THIS;
 
174
                                                if (warn2 > *warnp)
 
175
                                                        *warnp = warn2;
 
176
                                        }
 
177
                                        continue;
 
178
                                case 'D':
 
179
                                        pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
 
180
                                        continue;
 
181
                                case 'd':
 
182
                                        pt = _conv(t->tm_mday, "%02d", pt, ptlim);
 
183
                                        continue;
 
184
                                case 'E':
 
185
                                case 'O':
 
186
 
 
187
                                        /*
 
188
                                         * C99 locale modifiers. The sequences  %Ec %EC %Ex
 
189
                                         * %EX %Ey %EY  %Od %oe %OH %OI %Om %OM  %OS %Ou %OU
 
190
                                         * %OV %Ow %OW %Oy are supposed to provide alternate
 
191
                                         * representations.
 
192
                                         */
 
193
                                        goto label;
 
194
                                case 'e':
 
195
                                        pt = _conv(t->tm_mday, "%2d", pt, ptlim);
 
196
                                        continue;
 
197
                                case 'F':
 
198
                                        pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
 
199
                                        continue;
 
200
                                case 'H':
 
201
                                        pt = _conv(t->tm_hour, "%02d", pt, ptlim);
 
202
                                        continue;
 
203
                                case 'I':
 
204
                                        pt = _conv((t->tm_hour % 12) ?
 
205
                                                           (t->tm_hour % 12) : 12,
 
206
                                                           "%02d", pt, ptlim);
 
207
                                        continue;
 
208
                                case 'j':
 
209
                                        pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
 
210
                                        continue;
 
211
                                case 'k':
 
212
 
 
213
                                        /*
 
214
                                         * This used to be...  _conv(t->tm_hour % 12 ?
 
215
                                         * t->tm_hour % 12 : 12, 2, ' '); ...and has been
 
216
                                         * changed to the below to match SunOS 4.1.1 and
 
217
                                         * Arnold Robbins' strftime version 3.0.  That is,
 
218
                                         * "%k" and "%l" have been swapped. (ado, 1993-05-24)
 
219
                                         */
 
220
                                        pt = _conv(t->tm_hour, "%2d", pt, ptlim);
 
221
                                        continue;
 
222
#ifdef KITCHEN_SINK
 
223
                                case 'K':
 
224
 
 
225
                                        /*
 
226
                                         * * After all this time, still unclaimed!
 
227
                                         */
 
228
                                        pt = _add("kitchen sink", pt, ptlim);
 
229
                                        continue;
 
230
#endif   /* defined KITCHEN_SINK */
 
231
                                case 'l':
 
232
 
 
233
                                        /*
 
234
                                         * This used to be...  _conv(t->tm_hour, 2, ' ');
 
235
                                         * ...and has been changed to the below to match SunOS
 
236
                                         * 4.1.1 and Arnold Robbin's strftime version 3.0.
 
237
                                         * That is, "%k" and "%l" have been swapped. (ado,
 
238
                                         * 1993-05-24)
 
239
                                         */
 
240
                                        pt = _conv((t->tm_hour % 12) ?
 
241
                                                           (t->tm_hour % 12) : 12,
 
242
                                                           "%2d", pt, ptlim);
 
243
                                        continue;
 
244
                                case 'M':
 
245
                                        pt = _conv(t->tm_min, "%02d", pt, ptlim);
 
246
                                        continue;
 
247
                                case 'm':
 
248
                                        pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
 
249
                                        continue;
 
250
                                case 'n':
 
251
                                        pt = _add("\n", pt, ptlim);
 
252
                                        continue;
 
253
                                case 'p':
 
254
                                        pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
 
255
                                                          Locale->pm :
 
256
                                                          Locale->am,
 
257
                                                          pt, ptlim);
 
258
                                        continue;
 
259
                                case 'R':
 
260
                                        pt = _fmt("%H:%M", t, pt, ptlim, warnp);
 
261
                                        continue;
 
262
                                case 'r':
 
263
                                        pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
 
264
                                        continue;
 
265
                                case 'S':
 
266
                                        pt = _conv(t->tm_sec, "%02d", pt, ptlim);
 
267
                                        continue;
 
268
                                case 'T':
 
269
                                        pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
 
270
                                        continue;
 
271
                                case 't':
 
272
                                        pt = _add("\t", pt, ptlim);
 
273
                                        continue;
 
274
                                case 'U':
 
275
                                        pt = _conv((t->tm_yday + DAYSPERWEEK -
 
276
                                                                t->tm_wday) / DAYSPERWEEK,
 
277
                                                           "%02d", pt, ptlim);
 
278
                                        continue;
 
279
                                case 'u':
 
280
 
 
281
                                        /*
 
282
                                         * From Arnold Robbins' strftime version 3.0: "ISO
 
283
                                         * 8601: Weekday as a decimal number [1 (Monday) - 7]"
 
284
                                         * (ado, 1993-05-24)
 
285
                                         */
 
286
                                        pt = _conv((t->tm_wday == 0) ?
 
287
                                                           DAYSPERWEEK : t->tm_wday,
 
288
                                                           "%d", pt, ptlim);
 
289
                                        continue;
 
290
                                case 'V':               /* ISO 8601 week number */
 
291
                                case 'G':               /* ISO 8601 year (four digits) */
 
292
                                case 'g':               /* ISO 8601 year (two digits) */
 
293
/*
 
294
 * From Arnold Robbins' strftime version 3.0:  "the week number of the
 
295
 * year (the first Monday as the first day of week 1) as a decimal number
 
296
 * (01-53)."
 
297
 * (ado, 1993-05-24)
 
298
 *
 
299
 * From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn:
 
300
 * "Week 01 of a year is per definition the first week which has the
 
301
 * Thursday in this year, which is equivalent to the week which contains
 
302
 * the fourth day of January. In other words, the first week of a new year
 
303
 * is the week which has the majority of its days in the new year. Week 01
 
304
 * might also contain days from the previous year and the week before week
 
305
 * 01 of a year is the last week (52 or 53) of the previous year even if
 
306
 * it contains days from the new year. A week starts with Monday (day 1)
 
307
 * and ends with Sunday (day 7).  For example, the first week of the year
 
308
 * 1997 lasts from 1996-12-30 to 1997-01-05..."
 
309
 * (ado, 1996-01-02)
 
310
 */
 
311
                                        {
 
312
                                                int                     year;
 
313
                                                int                     yday;
 
314
                                                int                     wday;
 
315
                                                int                     w;
 
316
 
 
317
                                                year = t->tm_year + TM_YEAR_BASE;
 
318
                                                yday = t->tm_yday;
 
319
                                                wday = t->tm_wday;
 
320
                                                for (;;)
 
321
                                                {
 
322
                                                        int                     len;
 
323
                                                        int                     bot;
 
324
                                                        int                     top;
 
325
 
 
326
                                                        len = isleap(year) ?
 
327
                                                                DAYSPERLYEAR :
 
328
                                                                DAYSPERNYEAR;
 
329
 
 
330
                                                        /*
 
331
                                                         * What yday (-3 ... 3) does the ISO year
 
332
                                                         * begin on?
 
333
                                                         */
 
334
                                                        bot = ((yday + 11 - wday) %
 
335
                                                                   DAYSPERWEEK) - 3;
 
336
 
 
337
                                                        /*
 
338
                                                         * What yday does the NEXT ISO year begin on?
 
339
                                                         */
 
340
                                                        top = bot -
 
341
                                                                (len % DAYSPERWEEK);
 
342
                                                        if (top < -3)
 
343
                                                                top += DAYSPERWEEK;
 
344
                                                        top += len;
 
345
                                                        if (yday >= top)
 
346
                                                        {
 
347
                                                                ++year;
 
348
                                                                w = 1;
 
349
                                                                break;
 
350
                                                        }
 
351
                                                        if (yday >= bot)
 
352
                                                        {
 
353
                                                                w = 1 + ((yday - bot) /
 
354
                                                                                 DAYSPERWEEK);
 
355
                                                                break;
 
356
                                                        }
 
357
                                                        --year;
 
358
                                                        yday += isleap(year) ?
 
359
                                                                DAYSPERLYEAR :
 
360
                                                                DAYSPERNYEAR;
 
361
                                                }
 
362
                                                if (*format == 'V')
 
363
                                                        pt = _conv(w, "%02d",
 
364
                                                                           pt, ptlim);
 
365
                                                else if (*format == 'g')
 
366
                                                {
 
367
                                                        *warnp = IN_ALL;
 
368
                                                        pt = _conv(year % 100, "%02d",
 
369
                                                                           pt, ptlim);
 
370
                                                }
 
371
                                                else
 
372
                                                        pt = _conv(year, "%04d",
 
373
                                                                           pt, ptlim);
 
374
                                        }
 
375
                                        continue;
 
376
                                case 'v':
 
377
 
 
378
                                        /*
 
379
                                         * From Arnold Robbins' strftime version 3.0: "date as
 
380
                                         * dd-bbb-YYYY" (ado, 1993-05-24)
 
381
                                         */
 
382
                                        pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
 
383
                                        continue;
 
384
                                case 'W':
 
385
                                        pt = _conv((t->tm_yday + DAYSPERWEEK -
 
386
                                                                (t->tm_wday ?
 
387
                                                                 (t->tm_wday - 1) :
 
388
                                                                 (DAYSPERWEEK - 1))) / DAYSPERWEEK,
 
389
                                                           "%02d", pt, ptlim);
 
390
                                        continue;
 
391
                                case 'w':
 
392
                                        pt = _conv(t->tm_wday, "%d", pt, ptlim);
 
393
                                        continue;
 
394
                                case 'X':
 
395
                                        pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
 
396
                                        continue;
 
397
                                case 'x':
 
398
                                        {
 
399
                                                int                     warn2 = IN_SOME;
 
400
 
 
401
                                                pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
 
402
                                                if (warn2 == IN_ALL)
 
403
                                                        warn2 = IN_THIS;
 
404
                                                if (warn2 > *warnp)
 
405
                                                        *warnp = warn2;
 
406
                                        }
 
407
                                        continue;
 
408
                                case 'y':
 
409
                                        *warnp = IN_ALL;
 
410
                                        pt = _conv((t->tm_year + TM_YEAR_BASE) % 100,
 
411
                                                           "%02d", pt, ptlim);
 
412
                                        continue;
 
413
                                case 'Y':
 
414
                                        pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d",
 
415
                                                           pt, ptlim);
 
416
                                        continue;
 
417
                                case 'Z':
 
418
                                        if (t->tm_zone != NULL)
 
419
                                                pt = _add(t->tm_zone, pt, ptlim);
 
420
 
 
421
                                        /*
 
422
                                         * C99 says that %Z must be replaced by the empty
 
423
                                         * string if the time zone is not determinable.
 
424
                                         */
 
425
                                        continue;
 
426
                                case 'z':
 
427
                                        {
 
428
                                                int                     diff;
 
429
                                                char const *sign;
 
430
 
 
431
                                                if (t->tm_isdst < 0)
 
432
                                                        continue;
 
433
                                                diff = t->tm_gmtoff;
 
434
                                                if (diff < 0)
 
435
                                                {
 
436
                                                        sign = "-";
 
437
                                                        diff = -diff;
 
438
                                                }
 
439
                                                else
 
440
                                                        sign = "+";
 
441
                                                pt = _add(sign, pt, ptlim);
 
442
                                                diff /= 60;
 
443
                                                pt = _conv((diff / 60) * 100 + diff % 60,
 
444
                                                                   "%04d", pt, ptlim);
 
445
                                        }
 
446
                                        continue;
 
447
                                case '+':
 
448
                                        pt = _fmt(Locale->date_fmt, t, pt, ptlim,
 
449
                                                          warnp);
 
450
                                        continue;
 
451
                                case '%':
 
452
 
 
453
                                        /*
 
454
                                         * X311J/88-090 (4.12.3.5): if conversion char is
 
455
                                         * undefined, behavior is undefined.  Print out the
 
456
                                         * character itself as printf(3) also does.
 
457
                                         */
 
458
                                default:
 
459
                                        break;
 
460
                        }
 
461
                }
 
462
                if (pt == ptlim)
 
463
                        break;
 
464
                *pt++ = *format;
 
465
        }
 
466
        return pt;
 
467
}
 
468
 
 
469
static char *
 
470
_conv(const int n, const char *format, char *pt, const char *ptlim)
 
471
{
 
472
        char            buf[INT_STRLEN_MAXIMUM(int) +1];
 
473
 
 
474
        (void) sprintf(buf, format, n);
 
475
        return _add(buf, pt, ptlim);
 
476
}
 
477
 
 
478
static char *
 
479
_add(const char *str, char *pt, const char *ptlim)
 
480
{
 
481
        while (pt < ptlim && (*pt = *str++) != '\0')
 
482
                ++pt;
 
483
        return pt;
 
484
}