00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "postgres.h"
00022
00023 #include <fcntl.h>
00024 #include <locale.h>
00025
00026 #include "private.h"
00027 #include "tzfile.h"
00028
00029
00030 struct lc_time_T
00031 {
00032 const char *mon[MONSPERYEAR];
00033 const char *month[MONSPERYEAR];
00034 const char *wday[DAYSPERWEEK];
00035 const char *weekday[DAYSPERWEEK];
00036 const char *X_fmt;
00037 const char *x_fmt;
00038 const char *c_fmt;
00039 const char *am;
00040 const char *pm;
00041 const char *date_fmt;
00042 };
00043
00044 #define Locale (&C_time_locale)
00045
00046 static const struct lc_time_T C_time_locale = {
00047 {
00048 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
00049 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
00050 }, {
00051 "January", "February", "March", "April", "May", "June",
00052 "July", "August", "September", "October", "November", "December"
00053 }, {
00054 "Sun", "Mon", "Tue", "Wed",
00055 "Thu", "Fri", "Sat"
00056 }, {
00057 "Sunday", "Monday", "Tuesday", "Wednesday",
00058 "Thursday", "Friday", "Saturday"
00059 },
00060
00061
00062 "%H:%M:%S",
00063
00064
00065
00066
00067
00068
00069
00070 "%m/%d/%y",
00071
00072
00073
00074
00075
00076
00077
00078
00079 "%a %b %e %T %Y",
00080
00081
00082 "AM",
00083
00084
00085 "PM",
00086
00087
00088 "%a %b %e %H:%M:%S %Z %Y"
00089 };
00090
00091 static char *_add(const char *, char *, const char *);
00092 static char *_conv(int, const char *, char *, const char *);
00093 static char *_fmt(const char *, const struct pg_tm *, char *,
00094 const char *, int *);
00095 static char *_yconv(const int, const int, const int, const int,
00096 char *, const char *const);
00097
00098 #define IN_NONE 0
00099 #define IN_SOME 1
00100 #define IN_THIS 2
00101 #define IN_ALL 3
00102
00103
00104 size_t
00105 pg_strftime(char *s, size_t maxsize, const char *format,
00106 const struct pg_tm * t)
00107 {
00108 char *p;
00109 int warn;
00110
00111 warn = IN_NONE;
00112 p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
00113 if (p == s + maxsize)
00114 return 0;
00115 *p = '\0';
00116 return p - s;
00117 }
00118
00119 static char *
00120 _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
00121 int *warnp)
00122 {
00123 for (; *format; ++format)
00124 {
00125 if (*format == '%')
00126 {
00127 label:
00128 switch (*++format)
00129 {
00130 case '\0':
00131 --format;
00132 break;
00133 case 'A':
00134 pt = _add((t->tm_wday < 0 ||
00135 t->tm_wday >= DAYSPERWEEK) ?
00136 "?" : Locale->weekday[t->tm_wday],
00137 pt, ptlim);
00138 continue;
00139 case 'a':
00140 pt = _add((t->tm_wday < 0 ||
00141 t->tm_wday >= DAYSPERWEEK) ?
00142 "?" : Locale->wday[t->tm_wday],
00143 pt, ptlim);
00144 continue;
00145 case 'B':
00146 pt = _add((t->tm_mon < 0 ||
00147 t->tm_mon >= MONSPERYEAR) ?
00148 "?" : Locale->month[t->tm_mon],
00149 pt, ptlim);
00150 continue;
00151 case 'b':
00152 case 'h':
00153 pt = _add((t->tm_mon < 0 ||
00154 t->tm_mon >= MONSPERYEAR) ?
00155 "?" : Locale->mon[t->tm_mon],
00156 pt, ptlim);
00157 continue;
00158 case 'C':
00159
00160
00161
00162
00163
00164
00165 pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,
00166 pt, ptlim);
00167 continue;
00168 case 'c':
00169 {
00170 int warn2 = IN_SOME;
00171
00172 pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2);
00173 if (warn2 == IN_ALL)
00174 warn2 = IN_THIS;
00175 if (warn2 > *warnp)
00176 *warnp = warn2;
00177 }
00178 continue;
00179 case 'D':
00180 pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
00181 continue;
00182 case 'd':
00183 pt = _conv(t->tm_mday, "%02d", pt, ptlim);
00184 continue;
00185 case 'E':
00186 case 'O':
00187
00188
00189
00190
00191
00192
00193
00194 goto label;
00195 case 'e':
00196 pt = _conv(t->tm_mday, "%2d", pt, ptlim);
00197 continue;
00198 case 'F':
00199 pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
00200 continue;
00201 case 'H':
00202 pt = _conv(t->tm_hour, "%02d", pt, ptlim);
00203 continue;
00204 case 'I':
00205 pt = _conv((t->tm_hour % 12) ?
00206 (t->tm_hour % 12) : 12,
00207 "%02d", pt, ptlim);
00208 continue;
00209 case 'j':
00210 pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
00211 continue;
00212 case 'k':
00213
00214
00215
00216
00217
00218
00219
00220
00221 pt = _conv(t->tm_hour, "%2d", pt, ptlim);
00222 continue;
00223 #ifdef KITCHEN_SINK
00224 case 'K':
00225
00226
00227
00228
00229 pt = _add("kitchen sink", pt, ptlim);
00230 continue;
00231 #endif
00232 case 'l':
00233
00234
00235
00236
00237
00238
00239
00240 pt = _conv((t->tm_hour % 12) ?
00241 (t->tm_hour % 12) : 12,
00242 "%2d", pt, ptlim);
00243 continue;
00244 case 'M':
00245 pt = _conv(t->tm_min, "%02d", pt, ptlim);
00246 continue;
00247 case 'm':
00248 pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
00249 continue;
00250 case 'n':
00251 pt = _add("\n", pt, ptlim);
00252 continue;
00253 case 'p':
00254 pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
00255 Locale->pm :
00256 Locale->am,
00257 pt, ptlim);
00258 continue;
00259 case 'R':
00260 pt = _fmt("%H:%M", t, pt, ptlim, warnp);
00261 continue;
00262 case 'r':
00263 pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
00264 continue;
00265 case 'S':
00266 pt = _conv(t->tm_sec, "%02d", pt, ptlim);
00267 continue;
00268 case 'T':
00269 pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
00270 continue;
00271 case 't':
00272 pt = _add("\t", pt, ptlim);
00273 continue;
00274 case 'U':
00275 pt = _conv((t->tm_yday + DAYSPERWEEK -
00276 t->tm_wday) / DAYSPERWEEK,
00277 "%02d", pt, ptlim);
00278 continue;
00279 case 'u':
00280
00281
00282
00283
00284
00285
00286 pt = _conv((t->tm_wday == 0) ?
00287 DAYSPERWEEK : t->tm_wday,
00288 "%d", pt, ptlim);
00289 continue;
00290 case 'V':
00291 case 'G':
00292 case 'g':
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311 {
00312 int year;
00313 int base;
00314 int yday;
00315 int wday;
00316 int w;
00317
00318 year = t->tm_year;
00319 base = TM_YEAR_BASE;
00320 yday = t->tm_yday;
00321 wday = t->tm_wday;
00322 for (;;)
00323 {
00324 int len;
00325 int bot;
00326 int top;
00327
00328 len = isleap_sum(year, base) ?
00329 DAYSPERLYEAR :
00330 DAYSPERNYEAR;
00331
00332
00333
00334
00335
00336 bot = ((yday + 11 - wday) %
00337 DAYSPERWEEK) - 3;
00338
00339
00340
00341
00342 top = bot -
00343 (len % DAYSPERWEEK);
00344 if (top < -3)
00345 top += DAYSPERWEEK;
00346 top += len;
00347 if (yday >= top)
00348 {
00349 ++base;
00350 w = 1;
00351 break;
00352 }
00353 if (yday >= bot)
00354 {
00355 w = 1 + ((yday - bot) /
00356 DAYSPERWEEK);
00357 break;
00358 }
00359 --base;
00360 yday += isleap_sum(year, base) ?
00361 DAYSPERLYEAR :
00362 DAYSPERNYEAR;
00363 }
00364 if (*format == 'V')
00365 pt = _conv(w, "%02d",
00366 pt, ptlim);
00367 else if (*format == 'g')
00368 {
00369 *warnp = IN_ALL;
00370 pt = _yconv(year, base, 0, 1,
00371 pt, ptlim);
00372 }
00373 else
00374 pt = _yconv(year, base, 1, 1,
00375 pt, ptlim);
00376 }
00377 continue;
00378 case 'v':
00379
00380
00381
00382
00383
00384 pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
00385 continue;
00386 case 'W':
00387 pt = _conv((t->tm_yday + DAYSPERWEEK -
00388 (t->tm_wday ?
00389 (t->tm_wday - 1) :
00390 (DAYSPERWEEK - 1))) / DAYSPERWEEK,
00391 "%02d", pt, ptlim);
00392 continue;
00393 case 'w':
00394 pt = _conv(t->tm_wday, "%d", pt, ptlim);
00395 continue;
00396 case 'X':
00397 pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
00398 continue;
00399 case 'x':
00400 {
00401 int warn2 = IN_SOME;
00402
00403 pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
00404 if (warn2 == IN_ALL)
00405 warn2 = IN_THIS;
00406 if (warn2 > *warnp)
00407 *warnp = warn2;
00408 }
00409 continue;
00410 case 'y':
00411 *warnp = IN_ALL;
00412 pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,
00413 pt, ptlim);
00414 continue;
00415 case 'Y':
00416 pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,
00417 pt, ptlim);
00418 continue;
00419 case 'Z':
00420 if (t->tm_zone != NULL)
00421 pt = _add(t->tm_zone, pt, ptlim);
00422
00423
00424
00425
00426
00427 continue;
00428 case 'z':
00429 {
00430 int diff;
00431 char const *sign;
00432
00433 if (t->tm_isdst < 0)
00434 continue;
00435 diff = t->tm_gmtoff;
00436 if (diff < 0)
00437 {
00438 sign = "-";
00439 diff = -diff;
00440 }
00441 else
00442 sign = "+";
00443 pt = _add(sign, pt, ptlim);
00444 diff /= 60;
00445 pt = _conv((diff / 60) * 100 + diff % 60,
00446 "%04d", pt, ptlim);
00447 }
00448 continue;
00449 case '+':
00450 pt = _fmt(Locale->date_fmt, t, pt, ptlim,
00451 warnp);
00452 continue;
00453 case '%':
00454
00455
00456
00457
00458
00459
00460 default:
00461 break;
00462 }
00463 }
00464 if (pt == ptlim)
00465 break;
00466 *pt++ = *format;
00467 }
00468 return pt;
00469 }
00470
00471 static char *
00472 _conv(int n, const char *format, char *pt, const char *ptlim)
00473 {
00474 char buf[INT_STRLEN_MAXIMUM(int) +1];
00475
00476 (void) sprintf(buf, format, n);
00477 return _add(buf, pt, ptlim);
00478 }
00479
00480 static char *
00481 _add(const char *str, char *pt, const char *ptlim)
00482 {
00483 while (pt < ptlim && (*pt = *str++) != '\0')
00484 ++pt;
00485 return pt;
00486 }
00487
00488
00489
00490
00491
00492
00493
00494
00495 static char *
00496 _yconv(const int a, const int b, const int convert_top,
00497 const int convert_yy, char *pt, const char *const ptlim)
00498 {
00499 int lead;
00500 int trail;
00501
00502 #define DIVISOR 100
00503 trail = a % DIVISOR + b % DIVISOR;
00504 lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
00505 trail %= DIVISOR;
00506 if (trail < 0 && lead > 0)
00507 {
00508 trail += DIVISOR;
00509 --lead;
00510 }
00511 else if (lead < 0 && trail > 0)
00512 {
00513 trail -= DIVISOR;
00514 ++lead;
00515 }
00516 if (convert_top)
00517 {
00518 if (lead == 0 && trail < 0)
00519 pt = _add("-0", pt, ptlim);
00520 else
00521 pt = _conv(lead, "%02d", pt, ptlim);
00522 }
00523 if (convert_yy)
00524 pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
00525 return pt;
00526 }