00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "config.h"
00037 #include <ctype.h>
00038 #include <string.h>
00039 #include <glib.h>
00040 #include "qof.h"
00041 #include "qofdate-p.h"
00042
00043 static QofLogModule log_module = QOF_MOD_DATE;
00044
00045 AS_STRING_FUNC (QofDateError , ENUM_ERR_LIST)
00046
00047 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
00048 # define match_string(cs1, s2) \
00049 (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
00050
00051
00052 #define get_number(from, to, n) \
00053 do { \
00054 gint __n = n; \
00055 val = 0; \
00056 while (*rp == ' ') \
00057 ++rp; \
00058 if (*rp < '0' || *rp > '9') \
00059 { \
00060 *error = ERR_OUT_OF_RANGE; \
00061 PERR (" error=%s", QofDateErrorasString (*error)); \
00062 return NULL; \
00063 } \
00064 do { \
00065 val *= 10; \
00066 val += *rp++ - '0'; \
00067 } \
00068 while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
00069 if (val < from || val > to) \
00070 { \
00071 *error = ERR_INVALID_DELIMITER; \
00072 PERR (" error=%s", QofDateErrorasString (*error)); \
00073 return NULL; \
00074 } \
00075 } while (0)
00076
00077
00078 # define get_alt_number(from, to, n) \
00079 get_number(from, to, n)
00080
00081 #define recursive(new_fmt) \
00082 (*(new_fmt) != '\0' && (rp = strptime_internal (rp, (new_fmt), qd, error)) != NULL)
00083
00084 static gchar const weekday_name[][10] = {
00085 "Sunday", "Monday", "Tuesday", "Wednesday",
00086 "Thursday", "Friday", "Saturday"
00087 };
00088 static gchar const ab_weekday_name[][4] = {
00089 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
00090 };
00091 static gchar const month_name[][10] = {
00092 "January", "February", "March", "April", "May", "June",
00093 "July", "August", "September", "October", "November", "December"
00094 };
00095 static gchar const ab_month_name[][4] = {
00096 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
00097 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
00098 };
00099
00100 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
00101 # define HERE_D_FMT "%m/%d/%y"
00102 # define HERE_AM_STR "AM"
00103 # define HERE_PM_STR "PM"
00104 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
00105 # define HERE_T_FMT "%H:%M:%S"
00106 #define raw 1;
00107
00108
00109
00110 static const gushort yeardays[2][13] = {
00111 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
00112 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
00113 };
00114
00115
00116 void
00117 set_day_of_the_week (QofDate * qd)
00118 {
00119 gint64 days;
00120
00121 days = days_between (1970, qd->qd_year);
00122
00123 if (days < 0)
00124 days *= -1;
00125 days--;
00126 days += qof_date_get_yday (qd->qd_mday,
00127 qd->qd_mon, qd->qd_year) + 4;
00128 qd->qd_wday = ((days % 7) + 7) % 7;
00129 }
00130
00131 gchar *
00132 strptime_internal (const gchar * rp, const gchar * fmt,
00133 QofDate * qd, QofDateError * error)
00134 {
00135 const gchar *rp_backup;
00136 gint64 val, century, want_century;
00137 gint want_era, have_wday, want_xday, have_yday;
00138 gint have_mon, have_mday, have_uweek, have_wweek;
00139 gint week_no, have_I, is_pm, cnt, decided, era_cnt;
00140 struct era_entry *era;
00141
00142 have_I = is_pm = 0;
00143 century = -1;
00144 decided = raw;
00145 era_cnt = -1;
00146 want_century = 0;
00147 want_era = 0;
00148 era = NULL;
00149 week_no = 0;
00150 *error = ERR_NO_ERROR;
00151
00152 have_wday = want_xday = have_yday = have_mon = 0;
00153 have_mday = have_uweek = have_wweek = 0;
00154
00155 while (*fmt != '\0')
00156 {
00157
00158
00159 if (isspace (*fmt))
00160 {
00161 while (isspace (*rp))
00162 ++rp;
00163 ++fmt;
00164 continue;
00165 }
00166
00167
00168
00169 if (*fmt != '%')
00170 {
00171 match_char (*fmt++, *rp++);
00172 continue;
00173 }
00174
00175 ++fmt;
00176
00177 start_over:
00178
00179
00180 rp_backup = rp;
00181
00182 switch (*fmt++)
00183 {
00184 case '%':
00185
00186 match_char ('%', *rp++);
00187 break;
00188 case 'a':
00189 case 'A':
00190
00191 for (cnt = 0; cnt < 7; ++cnt)
00192 {
00193 if (match_string (weekday_name[cnt], rp)
00194 || match_string (ab_weekday_name[cnt], rp))
00195 break;
00196 }
00197 if (cnt == 7)
00198 {
00199
00200 *error = ERR_WEEKDAY_NAME;
00201 PERR (" error=%s", QofDateErrorasString (*error));
00202 return NULL;
00203 }
00204 qd->qd_wday = cnt;
00205 have_wday = 1;
00206 break;
00207 case 'b':
00208 case 'B':
00209 case 'h':
00210
00211 for (cnt = 0; cnt < 12; ++cnt)
00212 {
00213 if (match_string (month_name[cnt], rp)
00214 || match_string (ab_month_name[cnt], rp))
00215 {
00216 decided = raw;
00217 break;
00218 }
00219 }
00220 if (cnt == 12)
00221 {
00222
00223 *error = ERR_MONTH_NAME;
00224 PERR (" error=%s", QofDateErrorasString (*error));
00225 return NULL;
00226 }
00227 qd->qd_mon = cnt;
00228 want_xday = 1;
00229 break;
00230 case 'c':
00231
00232 if (!recursive (HERE_D_T_FMT))
00233 {
00234 *error = ERR_LOCALE_DATE_TIME;
00235 PERR (" error=%s", QofDateErrorasString (*error));
00236 return NULL;
00237 }
00238 want_xday = 1;
00239 break;
00240 case 'C':
00241
00242 get_number (0, 99, 2);
00243 century = val;
00244 want_xday = 1;
00245 break;
00246 case 'd':
00247 case 'e':
00248
00249 get_number (1, 31, 2);
00250 qd->qd_mday = val;
00251 have_mday = 1;
00252 want_xday = 1;
00253 break;
00254 case 'F':
00255 if (!recursive ("%Y-%m-%d"))
00256 return NULL;
00257 want_xday = 1;
00258 break;
00259 case 'x':
00260
00261 case 'D':
00262
00263 if (!recursive (HERE_D_FMT))
00264 {
00265 *error = ERR_STANDARD_DAY;
00266 PERR (" error=%s", QofDateErrorasString (*error));
00267 return NULL;
00268 }
00269 want_xday = 1;
00270 break;
00271 case 'k':
00272 case 'H':
00273
00274 get_number (0, 23, 2);
00275 qd->qd_hour = val;
00276 have_I = 0;
00277 break;
00278 case 'l':
00279
00280 case 'I':
00281
00282 get_number (1, 12, 2);
00283 qd->qd_hour = val % 12;
00284 have_I = 1;
00285 break;
00286 case 'j':
00287
00288 get_number (1, 366, 3);
00289 qd->qd_yday = val - 1;
00290 have_yday = 1;
00291 break;
00292 case 'm':
00293
00294 get_number (1, 12, 2);
00295 qd->qd_mon = val;
00296 have_mon = 1;
00297 want_xday = 1;
00298 break;
00299 case 'M':
00300
00301 get_number (0, 59, 2);
00302 qd->qd_min = val;
00303 break;
00304 case 'N':
00305 {
00306
00307 gint n;
00308 n = val = 0;
00309 while (n < 9 && *rp >= '0' && *rp <= '9')
00310 {
00311 val = val * 10 + *rp++ - '0';
00312 ++n;
00313 }
00314 qd->qd_nanosecs = val;
00315 break;
00316 }
00317 case 'n':
00318 case 't':
00319
00320 while (isspace (*rp))
00321 ++rp;
00322 break;
00323 case 'p':
00324
00325 if (!match_string (HERE_AM_STR, rp))
00326 {
00327 if (match_string (HERE_PM_STR, rp))
00328 is_pm = 1;
00329 else
00330 {
00331 *error = ERR_LOCALE_AMPM;
00332 PERR (" error=%s", QofDateErrorasString (*error));
00333 return NULL;
00334 }
00335 }
00336 break;
00337 case 'r':
00338 if (!recursive (HERE_T_FMT_AMPM))
00339 {
00340 *error = ERR_TIME_AMPM;
00341 PERR (" error=%s", QofDateErrorasString (*error));
00342 return NULL;
00343 }
00344 break;
00345 case 'R':
00346 if (!recursive ("%H:%M"))
00347 {
00348 *error = ERR_RECURSIVE_R;
00349 PERR (" error=%s", QofDateErrorasString (*error));
00350 return NULL;
00351 }
00352 break;
00353 case 's':
00354 {
00355
00356
00357
00358
00359 QofTimeSecs secs = 0;
00360 if (*rp < '0' || *rp > '9')
00361
00362 {
00363 *error = ERR_SECS_NO_DIGITS;
00364 PERR (" error=%s", QofDateErrorasString (*error));
00365 return NULL;
00366 }
00367 do
00368 {
00369 secs *= 10;
00370 secs += *rp++ - '0';
00371 } while (*rp >= '0' && *rp <= '9');
00372 qd->qd_sec = secs;
00373
00374 qd->qd_year = 1970;
00375 }
00376 break;
00377 case 'S':
00378 get_number (0, 61, 2);
00379 qd->qd_sec = val;
00380 break;
00381 case 'X':
00382
00383 case 'T':
00384 if (!recursive (HERE_T_FMT))
00385 {
00386 *error = ERR_RECURSIVE_T;
00387 PERR (" error=%s", QofDateErrorasString (*error));
00388 return NULL;
00389 }
00390 break;
00391 case 'u':
00392 get_number (1, 7, 1);
00393 qd->qd_wday = val % 7;
00394 have_wday = 1;
00395 break;
00396 case 'g':
00397 get_number (0, 99, 2);
00398
00399 break;
00400 case 'G':
00401 if (*rp < '0' || *rp > '9')
00402 {
00403 *error = ERR_G_INCOMPLETE;
00404 PERR (" error=%s", QofDateErrorasString (*error));
00405 return NULL;
00406 }
00407
00408
00409 do
00410 ++rp;
00411 while (*rp >= '0' && *rp <= '9');
00412 break;
00413 case 'U':
00414 get_number (0, 53, 2);
00415 week_no = val;
00416 have_uweek = 1;
00417 break;
00418 case 'W':
00419 get_number (0, 53, 2);
00420 week_no = val;
00421 have_wweek = 1;
00422 break;
00423 case 'V':
00424 get_number (0, 53, 2);
00425
00426
00427 break;
00428 case 'w':
00429
00430 get_number (0, 6, 1);
00431 qd->qd_wday = val;
00432 have_wday = 1;
00433 break;
00434 case 'y':
00435
00436 get_number (0, 99, 2);
00437
00438
00439 qd->qd_year = val >= 69 ? val + 2000 : val + 1900;
00440
00441 want_century = 1;
00442 want_xday = 1;
00443 break;
00444 case 'Y':
00445
00446 get_number (0, 999999999, 9);
00447 qd->qd_year = val;
00448 want_century = 0;
00449 want_xday = 1;
00450 break;
00451 case 'Z':
00452
00453 PINFO (" Z format - todo?");
00454 break;
00455 case 'z':
00456
00457
00458
00459 {
00460 gboolean neg;
00461 gint n;
00462 val = 0;
00463 while (*rp == ' ')
00464 ++rp;
00465 if (*rp != '+' && *rp != '-')
00466 {
00467 *error = ERR_INVALID_Z;
00468 PERR (" error=%s", QofDateErrorasString (*error));
00469 return NULL;
00470 }
00471 neg = *rp++ == '-';
00472 n = 0;
00473 while (n < 4 && *rp >= '0' && *rp <= '9')
00474 {
00475 val = val * 10 + *rp++ - '0';
00476 ++n;
00477 }
00478 if (n == 2)
00479 val *= 100;
00480 else if (n != 4)
00481 {
00482
00483 *error = ERR_YEAR_DIGITS;
00484 PERR (" error=%s", QofDateErrorasString (*error));
00485 return NULL;
00486 }
00487 else
00488 {
00489
00490 if (val % 100 >= 60)
00491 {
00492 *error = ERR_MIN_TO_DECIMAL;
00493 PERR (" error=%s", QofDateErrorasString (*error));
00494 return NULL;
00495 }
00496 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
00497 }
00498 if (val > 1200)
00499 {
00500 *error = ERR_GMTOFF;
00501 PERR (" error=%s", QofDateErrorasString (*error));
00502 return NULL;
00503 }
00504 qd->qd_gmt_off = (val * 3600) / 100;
00505 if (neg)
00506 qd->qd_gmt_off = -qd->qd_gmt_off;
00507 }
00508 break;
00509 case 'E':
00510
00511
00512 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
00513 && *fmt != 'x' && *fmt != 'X')
00514 {
00515
00516 *error = ERR_INVALID_FORMAT;
00517 PERR (" error=%s", QofDateErrorasString (*error));
00518 return NULL;
00519 }
00520
00521 goto start_over;
00522 case 'O':
00523 switch (*fmt++)
00524 {
00525 case 'd':
00526 case 'e':
00527
00528 get_alt_number (1, 31, 2);
00529 qd->qd_mday = val;
00530 have_mday = 1;
00531 want_xday = 1;
00532 break;
00533 case 'H':
00534
00535
00536 get_alt_number (0, 23, 2);
00537 qd->qd_hour = val;
00538 have_I = 0;
00539 break;
00540 case 'I':
00541
00542
00543 get_alt_number (1, 12, 2);
00544 qd->qd_hour = val % 12;
00545 have_I = 1;
00546 break;
00547 case 'm':
00548
00549 get_alt_number (1, 12, 2);
00550 qd->qd_mon = val - 1;
00551 have_mon = 1;
00552 want_xday = 1;
00553 break;
00554 case 'M':
00555
00556 get_alt_number (0, 59, 2);
00557 qd->qd_min = val;
00558 break;
00559 case 'S':
00560
00561 get_alt_number (0, 61, 2);
00562 qd->qd_sec = val;
00563 break;
00564 case 'U':
00565 get_alt_number (0, 53, 2);
00566 week_no = val;
00567 have_uweek = 1;
00568 break;
00569 case 'W':
00570 get_alt_number (0, 53, 2);
00571 week_no = val;
00572 have_wweek = 1;
00573 break;
00574 case 'V':
00575 get_alt_number (0, 53, 2);
00576
00577
00578 break;
00579 case 'w':
00580
00581 get_alt_number (0, 6, 1);
00582 qd->qd_wday = val;
00583 have_wday = 1;
00584 break;
00585 case 'y':
00586
00587 get_alt_number (0, 99, 2);
00588 qd->qd_year = val >= 69 ? val : val + 100;
00589 want_xday = 1;
00590 break;
00591 default:
00592 {
00593 *error = ERR_UNKNOWN_ERR;
00594 PERR (" error=%s (first default)",
00595 QofDateErrorasString (*error));
00596 return NULL;
00597 }
00598 }
00599 break;
00600 default:
00601 {
00602 *error = ERR_UNKNOWN_ERR;
00603 PERR (" error=%s val=%s (second default)",
00604 QofDateErrorasString (*error), rp);
00605 return NULL;
00606 }
00607 }
00608 }
00609
00610 if (have_I && is_pm)
00611 qd->qd_hour += 12;
00612
00613 if (century != -1)
00614 {
00615 if (want_century)
00617 qd->qd_year = qd->qd_year % 100 + (century - 19) * 100;
00618 else
00619
00620 qd->qd_year = (century - 19) * 100;
00621 }
00622
00623 if (era_cnt != -1)
00624 {
00625 if (era == NULL)
00626 {
00627 *error = ERR_INVALID_ERA;
00628 PERR (" error=%s", QofDateErrorasString (*error));
00629 return NULL;
00630 }
00631 }
00632 else if (want_era)
00633 {
00634
00635
00637 if (want_century && century == -1 && qd->qd_year < 69)
00638 qd->qd_year += 100;
00639 }
00640
00641 if (want_xday && !have_wday)
00642 {
00643 if (!(have_mon && have_mday) && have_yday)
00644 {
00645
00646 gint t_mon = 0;
00647 gint leap = qof_date_isleap (qd->qd_year);
00648 while (yeardays[leap][t_mon] <=
00649 qd->qd_yday)
00650 t_mon++;
00651 if (!have_mon)
00652 qd->qd_mon = t_mon;
00653 if (!have_mday)
00654 qd->qd_mday = qd->qd_yday -
00655 yeardays[leap][t_mon - 1] + 1;
00656 }
00657 set_day_of_the_week (qd);
00658 }
00659
00660 if (want_xday && !have_yday)
00661 qd->qd_yday = qof_date_get_yday (qd->qd_mday,
00662 qd->qd_mon, qd->qd_year);
00663
00664 if ((have_uweek || have_wweek) && have_wday)
00665 {
00666 gint save_wday = qd->qd_wday;
00667 gint save_mday = qd->qd_mday;
00668 gint save_mon = qd->qd_mon;
00669 gint w_offset = have_uweek ? 0 : 1;
00670
00671 qd->qd_mday = 1;
00672 qd->qd_mon = 0;
00673 set_day_of_the_week (qd);
00674 if (have_mday)
00675 qd->qd_mday = save_mday;
00676 if (have_mon)
00677 qd->qd_mon = save_mon;
00678
00679 if (!have_yday)
00680 qd->qd_yday = ((7 - (qd->qd_wday - w_offset)) % 7
00681 + (week_no - 1) * 7 + save_wday - w_offset);
00682
00683 if (!have_mday || !have_mon)
00684 {
00685 gint t_mon = 0;
00686
00687 while (qof_date_get_yday (1, t_mon, qd->qd_year) <=
00688 qd->qd_yday)
00689 t_mon++;
00690 if (!have_mon)
00691 qd->qd_mon = t_mon - 1;
00692 if (!have_mday)
00693 qd->qd_mday = (qd->qd_yday -
00694 qof_date_get_yday (1, t_mon, qd->qd_year));
00695 }
00696
00697 qd->qd_wday = save_wday;
00698 }
00699
00700 return (gchar *) rp;
00701 }