Git fork
at reftables-rust 1420 lines 34 kB view raw
1/* 2 * GIT - The information manager from hell 3 * 4 * Copyright (C) Linus Torvalds, 2005 5 */ 6 7#define DISABLE_SIGN_COMPARE_WARNINGS 8 9#include "git-compat-util.h" 10#include "date.h" 11#include "gettext.h" 12#include "pager.h" 13#include "strbuf.h" 14 15/* 16 * This is like mktime, but without normalization of tm_wday and tm_yday. 17 */ 18time_t tm_to_time_t(const struct tm *tm) 19{ 20 static const int mdays[] = { 21 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 22 }; 23 int year = tm->tm_year - 70; 24 int month = tm->tm_mon; 25 int day = tm->tm_mday; 26 27 if (year < 0 || year > 129) /* algo only works for 1970-2099 */ 28 return -1; 29 if (month < 0 || month > 11) /* array bounds */ 30 return -1; 31 if (month < 2 || (year + 2) % 4) 32 day--; 33 if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0) 34 return -1; 35 return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL + 36 tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec; 37} 38 39static const char *month_names[] = { 40 "January", "February", "March", "April", "May", "June", 41 "July", "August", "September", "October", "November", "December" 42}; 43 44static const char *weekday_names[] = { 45 "Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays" 46}; 47 48static time_t gm_time_t(timestamp_t time, int tz) 49{ 50 int minutes; 51 52 minutes = tz < 0 ? -tz : tz; 53 minutes = (minutes / 100)*60 + (minutes % 100); 54 minutes = tz < 0 ? -minutes : minutes; 55 56 if (minutes > 0) { 57 if (unsigned_add_overflows(time, minutes * 60)) 58 die("Timestamp+tz too large: %"PRItime" +%04d", 59 time, tz); 60 } else if (time < -minutes * 60) 61 die("Timestamp before Unix epoch: %"PRItime" %04d", time, tz); 62 time += minutes * 60; 63 if (date_overflows(time)) 64 die("Timestamp too large for this system: %"PRItime, time); 65 return (time_t)time; 66} 67 68/* 69 * The "tz" thing is passed in as this strange "decimal parse of tz" 70 * thing, which means that tz -0100 is passed in as the integer -100, 71 * even though it means "sixty minutes off" 72 */ 73static struct tm *time_to_tm(timestamp_t time, int tz, struct tm *tm) 74{ 75 time_t t = gm_time_t(time, tz); 76 return gmtime_r(&t, tm); 77} 78 79static struct tm *time_to_tm_local(timestamp_t time, struct tm *tm) 80{ 81 time_t t = time; 82 return localtime_r(&t, tm); 83} 84 85/* 86 * Fill in the localtime 'struct tm' for the supplied time, 87 * and return the local tz. 88 */ 89static int local_time_tzoffset(time_t t, struct tm *tm) 90{ 91 time_t t_local; 92 int offset, eastwest; 93 94 localtime_r(&t, tm); 95 t_local = tm_to_time_t(tm); 96 if (t_local == -1) 97 return 0; /* error; just use +0000 */ 98 if (t_local < t) { 99 eastwest = -1; 100 offset = t - t_local; 101 } else { 102 eastwest = 1; 103 offset = t_local - t; 104 } 105 offset /= 60; /* in minutes */ 106 offset = (offset % 60) + ((offset / 60) * 100); 107 return offset * eastwest; 108} 109 110/* 111 * What value of "tz" was in effect back then at "time" in the 112 * local timezone? 113 */ 114static int local_tzoffset(timestamp_t time) 115{ 116 struct tm tm; 117 118 if (date_overflows(time)) 119 die("Timestamp too large for this system: %"PRItime, time); 120 121 return local_time_tzoffset((time_t)time, &tm); 122} 123 124static void get_time(struct timeval *now) 125{ 126 const char *x; 127 128 x = getenv("GIT_TEST_DATE_NOW"); 129 if (x) { 130 now->tv_sec = atoi(x); 131 now->tv_usec = 0; 132 } 133 else 134 gettimeofday(now, NULL); 135} 136 137void show_date_relative(timestamp_t time, struct strbuf *timebuf) 138{ 139 struct timeval now; 140 timestamp_t diff; 141 142 get_time(&now); 143 if (now.tv_sec < time) { 144 strbuf_addstr(timebuf, _("in the future")); 145 return; 146 } 147 diff = now.tv_sec - time; 148 if (diff < 90) { 149 strbuf_addf(timebuf, 150 Q_("%"PRItime" second ago", "%"PRItime" seconds ago", diff), diff); 151 return; 152 } 153 /* Turn it into minutes */ 154 diff = (diff + 30) / 60; 155 if (diff < 90) { 156 strbuf_addf(timebuf, 157 Q_("%"PRItime" minute ago", "%"PRItime" minutes ago", diff), diff); 158 return; 159 } 160 /* Turn it into hours */ 161 diff = (diff + 30) / 60; 162 if (diff < 36) { 163 strbuf_addf(timebuf, 164 Q_("%"PRItime" hour ago", "%"PRItime" hours ago", diff), diff); 165 return; 166 } 167 /* We deal with number of days from here on */ 168 diff = (diff + 12) / 24; 169 if (diff < 14) { 170 strbuf_addf(timebuf, 171 Q_("%"PRItime" day ago", "%"PRItime" days ago", diff), diff); 172 return; 173 } 174 /* Say weeks for the past 10 weeks or so */ 175 if (diff < 70) { 176 strbuf_addf(timebuf, 177 Q_("%"PRItime" week ago", "%"PRItime" weeks ago", (diff + 3) / 7), 178 (diff + 3) / 7); 179 return; 180 } 181 /* Say months for the past 12 months or so */ 182 if (diff < 365) { 183 strbuf_addf(timebuf, 184 Q_("%"PRItime" month ago", "%"PRItime" months ago", (diff + 15) / 30), 185 (diff + 15) / 30); 186 return; 187 } 188 /* Give years and months for 5 years or so */ 189 if (diff < 1825) { 190 timestamp_t totalmonths = (diff * 12 * 2 + 365) / (365 * 2); 191 timestamp_t years = totalmonths / 12; 192 timestamp_t months = totalmonths % 12; 193 if (months) { 194 struct strbuf sb = STRBUF_INIT; 195 strbuf_addf(&sb, Q_("%"PRItime" year", "%"PRItime" years", years), years); 196 strbuf_addf(timebuf, 197 /* TRANSLATORS: "%s" is "<n> years" */ 198 Q_("%s, %"PRItime" month ago", "%s, %"PRItime" months ago", months), 199 sb.buf, months); 200 strbuf_release(&sb); 201 } else 202 strbuf_addf(timebuf, 203 Q_("%"PRItime" year ago", "%"PRItime" years ago", years), years); 204 return; 205 } 206 /* Otherwise, just years. Centuries is probably overkill. */ 207 strbuf_addf(timebuf, 208 Q_("%"PRItime" year ago", "%"PRItime" years ago", (diff + 183) / 365), 209 (diff + 183) / 365); 210} 211 212struct date_mode date_mode_from_type(enum date_mode_type type) 213{ 214 struct date_mode mode = DATE_MODE_INIT; 215 if (type == DATE_STRFTIME) 216 BUG("cannot create anonymous strftime date_mode struct"); 217 mode.type = type; 218 return mode; 219} 220 221static void show_date_normal(struct strbuf *buf, timestamp_t time, struct tm *tm, int tz, struct tm *human_tm, int human_tz, int local) 222{ 223 struct { 224 unsigned int year:1, 225 date:1, 226 wday:1, 227 time:1, 228 seconds:1, 229 tz:1; 230 } hide = { 0 }; 231 232 hide.tz = local || tz == human_tz; 233 hide.year = tm->tm_year == human_tm->tm_year; 234 if (hide.year) { 235 if (tm->tm_mon == human_tm->tm_mon) { 236 if (tm->tm_mday > human_tm->tm_mday) { 237 /* Future date: think timezones */ 238 } else if (tm->tm_mday == human_tm->tm_mday) { 239 hide.date = hide.wday = 1; 240 } else if (tm->tm_mday + 5 > human_tm->tm_mday) { 241 /* Leave just weekday if it was a few days ago */ 242 hide.date = 1; 243 } 244 } 245 } 246 247 /* Show "today" times as just relative times */ 248 if (hide.wday) { 249 show_date_relative(time, buf); 250 return; 251 } 252 253 /* 254 * Always hide seconds for human-readable. 255 * Hide timezone if showing date. 256 * Hide weekday and time if showing year. 257 * 258 * The logic here is two-fold: 259 * (a) only show details when recent enough to matter 260 * (b) keep the maximum length "similar", and in check 261 */ 262 if (human_tm->tm_year) { 263 hide.seconds = 1; 264 hide.tz |= !hide.date; 265 hide.wday = hide.time = !hide.year; 266 } 267 268 if (!hide.wday) 269 strbuf_addf(buf, "%.3s ", weekday_names[tm->tm_wday]); 270 if (!hide.date) 271 strbuf_addf(buf, "%.3s %d ", month_names[tm->tm_mon], tm->tm_mday); 272 273 /* Do we want AM/PM depending on locale? */ 274 if (!hide.time) { 275 strbuf_addf(buf, "%02d:%02d", tm->tm_hour, tm->tm_min); 276 if (!hide.seconds) 277 strbuf_addf(buf, ":%02d", tm->tm_sec); 278 } else 279 strbuf_rtrim(buf); 280 281 if (!hide.year) 282 strbuf_addf(buf, " %d", tm->tm_year + 1900); 283 284 if (!hide.tz) 285 strbuf_addf(buf, " %+05d", tz); 286} 287 288const char *show_date(timestamp_t time, int tz, struct date_mode mode) 289{ 290 struct tm *tm; 291 struct tm tmbuf = { 0 }; 292 struct tm human_tm = { 0 }; 293 int human_tz = -1; 294 static struct strbuf timebuf = STRBUF_INIT; 295 296 if (mode.type == DATE_UNIX) { 297 strbuf_reset(&timebuf); 298 strbuf_addf(&timebuf, "%"PRItime, time); 299 return timebuf.buf; 300 } 301 302 if (mode.type == DATE_HUMAN) { 303 struct timeval now; 304 305 get_time(&now); 306 307 /* Fill in the data for "current time" in human_tz and human_tm */ 308 human_tz = local_time_tzoffset(now.tv_sec, &human_tm); 309 } 310 311 if (mode.local) 312 tz = local_tzoffset(time); 313 314 if (mode.type == DATE_RAW) { 315 strbuf_reset(&timebuf); 316 strbuf_addf(&timebuf, "%"PRItime" %+05d", time, tz); 317 return timebuf.buf; 318 } 319 320 if (mode.type == DATE_RELATIVE) { 321 strbuf_reset(&timebuf); 322 show_date_relative(time, &timebuf); 323 return timebuf.buf; 324 } 325 326 if (mode.local) 327 tm = time_to_tm_local(time, &tmbuf); 328 else 329 tm = time_to_tm(time, tz, &tmbuf); 330 if (!tm) { 331 tm = time_to_tm(0, 0, &tmbuf); 332 tz = 0; 333 } 334 335 strbuf_reset(&timebuf); 336 if (mode.type == DATE_SHORT) 337 strbuf_addf(&timebuf, "%04d-%02d-%02d", tm->tm_year + 1900, 338 tm->tm_mon + 1, tm->tm_mday); 339 else if (mode.type == DATE_ISO8601) 340 strbuf_addf(&timebuf, "%04d-%02d-%02d %02d:%02d:%02d %+05d", 341 tm->tm_year + 1900, 342 tm->tm_mon + 1, 343 tm->tm_mday, 344 tm->tm_hour, tm->tm_min, tm->tm_sec, 345 tz); 346 else if (mode.type == DATE_ISO8601_STRICT) { 347 strbuf_addf(&timebuf, "%04d-%02d-%02dT%02d:%02d:%02d", 348 tm->tm_year + 1900, 349 tm->tm_mon + 1, 350 tm->tm_mday, 351 tm->tm_hour, tm->tm_min, tm->tm_sec); 352 if (tz == 0) { 353 strbuf_addch(&timebuf, 'Z'); 354 } else { 355 strbuf_addch(&timebuf, tz >= 0 ? '+' : '-'); 356 tz = abs(tz); 357 strbuf_addf(&timebuf, "%02d:%02d", tz / 100, tz % 100); 358 } 359 } else if (mode.type == DATE_RFC2822) 360 strbuf_addf(&timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d", 361 weekday_names[tm->tm_wday], tm->tm_mday, 362 month_names[tm->tm_mon], tm->tm_year + 1900, 363 tm->tm_hour, tm->tm_min, tm->tm_sec, tz); 364 else if (mode.type == DATE_STRFTIME) 365 strbuf_addftime(&timebuf, mode.strftime_fmt, tm, tz, 366 !mode.local); 367 else 368 show_date_normal(&timebuf, time, tm, tz, &human_tm, human_tz, mode.local); 369 return timebuf.buf; 370} 371 372/* 373 * Check these. And note how it doesn't do the summer-time conversion. 374 * 375 * In my world, it's always summer, and things are probably a bit off 376 * in other ways too. 377 */ 378static const struct { 379 const char *name; 380 int offset; 381 int dst; 382} timezone_names[] = { 383 { "IDLW", -12, 0, }, /* International Date Line West */ 384 { "NT", -11, 0, }, /* Nome */ 385 { "CAT", -10, 0, }, /* Central Alaska */ 386 { "HST", -10, 0, }, /* Hawaii Standard */ 387 { "HDT", -10, 1, }, /* Hawaii Daylight */ 388 { "YST", -9, 0, }, /* Yukon Standard */ 389 { "YDT", -9, 1, }, /* Yukon Daylight */ 390 { "PST", -8, 0, }, /* Pacific Standard */ 391 { "PDT", -8, 1, }, /* Pacific Daylight */ 392 { "MST", -7, 0, }, /* Mountain Standard */ 393 { "MDT", -7, 1, }, /* Mountain Daylight */ 394 { "CST", -6, 0, }, /* Central Standard */ 395 { "CDT", -6, 1, }, /* Central Daylight */ 396 { "EST", -5, 0, }, /* Eastern Standard */ 397 { "EDT", -5, 1, }, /* Eastern Daylight */ 398 { "AST", -3, 0, }, /* Atlantic Standard */ 399 { "ADT", -3, 1, }, /* Atlantic Daylight */ 400 { "WAT", -1, 0, }, /* West Africa */ 401 402 { "GMT", 0, 0, }, /* Greenwich Mean */ 403 { "UTC", 0, 0, }, /* Universal (Coordinated) */ 404 { "Z", 0, 0, }, /* Zulu, alias for UTC */ 405 406 { "WET", 0, 0, }, /* Western European */ 407 { "BST", 0, 1, }, /* British Summer */ 408 { "CET", +1, 0, }, /* Central European */ 409 { "MET", +1, 0, }, /* Middle European */ 410 { "MEWT", +1, 0, }, /* Middle European Winter */ 411 { "MEST", +1, 1, }, /* Middle European Summer */ 412 { "CEST", +1, 1, }, /* Central European Summer */ 413 { "MESZ", +1, 1, }, /* Middle European Summer */ 414 { "FWT", +1, 0, }, /* French Winter */ 415 { "FST", +1, 1, }, /* French Summer */ 416 { "EET", +2, 0, }, /* Eastern Europe, USSR Zone 1 */ 417 { "EEST", +2, 1, }, /* Eastern European Daylight */ 418 { "WAST", +7, 0, }, /* West Australian Standard */ 419 { "WADT", +7, 1, }, /* West Australian Daylight */ 420 { "CCT", +8, 0, }, /* China Coast, USSR Zone 7 */ 421 { "JST", +9, 0, }, /* Japan Standard, USSR Zone 8 */ 422 { "EAST", +10, 0, }, /* Eastern Australian Standard */ 423 { "EADT", +10, 1, }, /* Eastern Australian Daylight */ 424 { "GST", +10, 0, }, /* Guam Standard, USSR Zone 9 */ 425 { "NZT", +12, 0, }, /* New Zealand */ 426 { "NZST", +12, 0, }, /* New Zealand Standard */ 427 { "NZDT", +12, 1, }, /* New Zealand Daylight */ 428 { "IDLE", +12, 0, }, /* International Date Line East */ 429}; 430 431static int match_string(const char *date, const char *str) 432{ 433 int i = 0; 434 435 for (i = 0; *date; date++, str++, i++) { 436 if (*date == *str) 437 continue; 438 if (toupper(*date) == toupper(*str)) 439 continue; 440 if (!isalnum(*date)) 441 break; 442 return 0; 443 } 444 return i; 445} 446 447static int skip_alpha(const char *date) 448{ 449 int i = 0; 450 do { 451 i++; 452 } while (isalpha(date[i])); 453 return i; 454} 455 456/* 457* Parse month, weekday, or timezone name 458*/ 459static int match_alpha(const char *date, struct tm *tm, int *offset) 460{ 461 int i; 462 463 for (i = 0; i < 12; i++) { 464 int match = match_string(date, month_names[i]); 465 if (match >= 3) { 466 tm->tm_mon = i; 467 return match; 468 } 469 } 470 471 for (i = 0; i < 7; i++) { 472 int match = match_string(date, weekday_names[i]); 473 if (match >= 3) { 474 tm->tm_wday = i; 475 return match; 476 } 477 } 478 479 for (i = 0; i < ARRAY_SIZE(timezone_names); i++) { 480 int match = match_string(date, timezone_names[i].name); 481 if (match >= 3 || match == strlen(timezone_names[i].name)) { 482 int off = timezone_names[i].offset; 483 484 /* This is bogus, but we like summer */ 485 off += timezone_names[i].dst; 486 487 /* Only use the tz name offset if we don't have anything better */ 488 if (*offset == -1) 489 *offset = 60*off; 490 491 return match; 492 } 493 } 494 495 if (match_string(date, "PM") == 2) { 496 tm->tm_hour = (tm->tm_hour % 12) + 12; 497 return 2; 498 } 499 500 if (match_string(date, "AM") == 2) { 501 tm->tm_hour = (tm->tm_hour % 12) + 0; 502 return 2; 503 } 504 505 /* ISO-8601 allows yyyymmDD'T'HHMMSS, with less precision */ 506 if (*date == 'T' && isdigit(date[1]) && tm->tm_hour == -1) { 507 tm->tm_min = tm->tm_sec = 0; 508 return 1; 509 } 510 511 /* BAD CRAP */ 512 return skip_alpha(date); 513} 514 515static int set_date(int year, int month, int day, struct tm *now_tm, time_t now, struct tm *tm) 516{ 517 if (month > 0 && month < 13 && day > 0 && day < 32) { 518 struct tm check = *tm; 519 struct tm *r = (now_tm ? &check : tm); 520 time_t specified; 521 522 r->tm_mon = month - 1; 523 r->tm_mday = day; 524 if (year == -1) { 525 if (!now_tm) 526 return 1; 527 r->tm_year = now_tm->tm_year; 528 } 529 else if (year >= 1970 && year < 2100) 530 r->tm_year = year - 1900; 531 else if (year > 70 && year < 100) 532 r->tm_year = year; 533 else if (year < 38) 534 r->tm_year = year + 100; 535 else 536 return -1; 537 if (!now_tm) 538 return 0; 539 540 specified = tm_to_time_t(r); 541 542 /* Be it commit time or author time, it does not make 543 * sense to specify timestamp way into the future. Make 544 * sure it is not later than ten days from now... 545 */ 546 if ((specified != -1) && (now + 10*24*3600 < specified)) 547 return -1; 548 tm->tm_mon = r->tm_mon; 549 tm->tm_mday = r->tm_mday; 550 if (year != -1) 551 tm->tm_year = r->tm_year; 552 return 0; 553 } 554 return -1; 555} 556 557static int set_time(long hour, long minute, long second, struct tm *tm) 558{ 559 /* We accept 61st second because of leap second */ 560 if (0 <= hour && hour <= 24 && 561 0 <= minute && minute < 60 && 562 0 <= second && second <= 60) { 563 tm->tm_hour = hour; 564 tm->tm_min = minute; 565 tm->tm_sec = second; 566 return 0; 567 } 568 return -1; 569} 570 571static int is_date_known(struct tm *tm) 572{ 573 return tm->tm_year != -1 && tm->tm_mon != -1 && tm->tm_mday != -1; 574} 575 576static int match_multi_number(timestamp_t num, char c, const char *date, 577 char *end, struct tm *tm, time_t now) 578{ 579 struct tm now_tm; 580 struct tm *refuse_future; 581 long num2, num3; 582 583 num2 = strtol(end+1, &end, 10); 584 num3 = -1; 585 if (*end == c && isdigit(end[1])) 586 num3 = strtol(end+1, &end, 10); 587 588 /* Time? Date? */ 589 switch (c) { 590 case ':': 591 if (num3 < 0) 592 num3 = 0; 593 if (set_time(num, num2, num3, tm) == 0) { 594 /* 595 * If %H:%M:%S was just parsed followed by: .<num4> 596 * Consider (& discard) it as fractional second 597 * if %Y%m%d is parsed before. 598 */ 599 if (*end == '.' && isdigit(end[1]) && is_date_known(tm)) 600 strtol(end + 1, &end, 10); 601 break; 602 } 603 return 0; 604 605 case '-': 606 case '/': 607 case '.': 608 if (!now) 609 now = time(NULL); 610 refuse_future = NULL; 611 if (gmtime_r(&now, &now_tm)) 612 refuse_future = &now_tm; 613 614 if (num > 70) { 615 /* yyyy-mm-dd? */ 616 if (set_date(num, num2, num3, NULL, now, tm) == 0) 617 break; 618 /* yyyy-dd-mm? */ 619 if (set_date(num, num3, num2, NULL, now, tm) == 0) 620 break; 621 } 622 /* Our eastern European friends say dd.mm.yy[yy] 623 * is the norm there, so giving precedence to 624 * mm/dd/yy[yy] form only when separator is not '.' 625 */ 626 if (c != '.' && 627 set_date(num3, num, num2, refuse_future, now, tm) == 0) 628 break; 629 /* European dd.mm.yy[yy] or funny US dd/mm/yy[yy] */ 630 if (set_date(num3, num2, num, refuse_future, now, tm) == 0) 631 break; 632 /* Funny European mm.dd.yy */ 633 if (c == '.' && 634 set_date(num3, num, num2, refuse_future, now, tm) == 0) 635 break; 636 return 0; 637 } 638 return end - date; 639} 640 641/* 642 * Have we filled in any part of the time/date yet? 643 * We just do a binary 'and' to see if the sign bit 644 * is set in all the values. 645 */ 646static inline int nodate(struct tm *tm) 647{ 648 return (tm->tm_year & 649 tm->tm_mon & 650 tm->tm_mday & 651 tm->tm_hour & 652 tm->tm_min & 653 tm->tm_sec) < 0; 654} 655 656/* 657 * Have we seen an ISO-8601-alike date, i.e. 20220101T0, 658 * In which, hour is still unset, 659 * and minutes and second has been set to 0. 660 */ 661static inline int maybeiso8601(struct tm *tm) 662{ 663 return tm->tm_hour == -1 && 664 tm->tm_min == 0 && 665 tm->tm_sec == 0; 666} 667 668/* 669 * We've seen a digit. Time? Year? Date? 670 */ 671static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt) 672{ 673 int n; 674 char *end; 675 timestamp_t num; 676 677 num = parse_timestamp(date, &end, 10); 678 679 /* 680 * Seconds since 1970? We trigger on that for any numbers with 681 * more than 8 digits. This is because we don't want to rule out 682 * numbers like 20070606 as a YYYYMMDD date. 683 */ 684 if (num >= 100000000 && nodate(tm)) { 685 time_t time = num; 686 if (gmtime_r(&time, tm)) { 687 *tm_gmt = 1; 688 return end - date; 689 } 690 } 691 692 /* 693 * Check for special formats: num[-.:/]num[same]num 694 */ 695 switch (*end) { 696 case ':': 697 case '.': 698 case '/': 699 case '-': 700 if (isdigit(end[1])) { 701 int match = match_multi_number(num, *end, date, end, tm, 0); 702 if (match) 703 return match; 704 } 705 } 706 707 /* 708 * None of the special formats? Try to guess what 709 * the number meant. We use the number of digits 710 * to make a more educated guess.. 711 */ 712 n = 0; 713 do { 714 n++; 715 } while (isdigit(date[n])); 716 717 /* 8 digits, compact style of ISO-8601's date: YYYYmmDD */ 718 /* 6 digits, compact style of ISO-8601's time: HHMMSS */ 719 if (n == 8 || n == 6) { 720 unsigned int num1 = num / 10000; 721 unsigned int num2 = (num % 10000) / 100; 722 unsigned int num3 = num % 100; 723 if (n == 8) 724 set_date(num1, num2, num3, NULL, time(NULL), tm); 725 else if (n == 6 && set_time(num1, num2, num3, tm) == 0 && 726 *end == '.' && isdigit(end[1])) 727 strtoul(end + 1, &end, 10); 728 return end - date; 729 } 730 731 /* reduced precision of ISO-8601's time: HHMM or HH */ 732 if (maybeiso8601(tm)) { 733 unsigned int num1 = num; 734 unsigned int num2 = 0; 735 if (n == 4) { 736 num1 = num / 100; 737 num2 = num % 100; 738 } 739 if ((n == 4 || n == 2) && !nodate(tm) && 740 set_time(num1, num2, 0, tm) == 0) 741 return n; 742 /* 743 * We thought this is an ISO-8601 time string, 744 * we set minutes and seconds to 0, 745 * turn out it isn't, rollback the change. 746 */ 747 tm->tm_min = tm->tm_sec = -1; 748 } 749 750 /* Four-digit year or a timezone? */ 751 if (n == 4) { 752 if (num <= 1400 && *offset == -1) { 753 unsigned int minutes = num % 100; 754 unsigned int hours = num / 100; 755 *offset = hours*60 + minutes; 756 } else if (num > 1900 && num < 2100) 757 tm->tm_year = num - 1900; 758 return n; 759 } 760 761 /* 762 * Ignore lots of numerals. We took care of 4-digit years above. 763 * Days or months must be one or two digits. 764 */ 765 if (n > 2) 766 return n; 767 768 /* 769 * NOTE! We will give precedence to day-of-month over month or 770 * year numbers in the 1-12 range. So 05 is always "mday 5", 771 * unless we already have a mday.. 772 * 773 * IOW, 01 Apr 05 parses as "April 1st, 2005". 774 */ 775 if (num > 0 && num < 32 && tm->tm_mday < 0) { 776 tm->tm_mday = num; 777 return n; 778 } 779 780 /* Two-digit year? */ 781 if (n == 2 && tm->tm_year < 0) { 782 if (num < 10 && tm->tm_mday >= 0) { 783 tm->tm_year = num + 100; 784 return n; 785 } 786 if (num >= 70) { 787 tm->tm_year = num; 788 return n; 789 } 790 } 791 792 if (num > 0 && num < 13 && tm->tm_mon < 0) 793 tm->tm_mon = num-1; 794 795 return n; 796} 797 798static int match_tz(const char *date, int *offp) 799{ 800 char *end; 801 int hour = strtoul(date + 1, &end, 10); 802 int n = end - (date + 1); 803 int min = 0; 804 805 if (n == 4) { 806 /* hhmm */ 807 min = hour % 100; 808 hour = hour / 100; 809 } else if (n != 2) { 810 min = 99; /* random crap */ 811 } else if (*end == ':') { 812 /* hh:mm? */ 813 min = strtoul(end + 1, &end, 10); 814 if (end - (date + 1) != 5) 815 min = 99; /* random crap */ 816 } /* otherwise we parsed "hh" */ 817 818 /* 819 * Don't accept any random crap. Even though some places have 820 * offset larger than 12 hours (e.g. Pacific/Kiritimati is at 821 * UTC+14), there is something wrong if hour part is much 822 * larger than that. We might also want to check that the 823 * minutes are divisible by 15 or something too. (Offset of 824 * Kathmandu, Nepal is UTC+5:45) 825 */ 826 if (min < 60 && hour < 24) { 827 int offset = hour * 60 + min; 828 if (*date == '-') 829 offset = -offset; 830 *offp = offset; 831 } 832 return end - date; 833} 834 835static void date_string(timestamp_t date, int offset, struct strbuf *buf) 836{ 837 int sign = '+'; 838 839 if (offset < 0) { 840 offset = -offset; 841 sign = '-'; 842 } 843 strbuf_addf(buf, "%"PRItime" %c%02d%02d", date, sign, offset / 60, offset % 60); 844} 845 846/* 847 * Parse a string like "0 +0000" as ancient timestamp near epoch, but 848 * only when it appears not as part of any other string. 849 */ 850static int match_object_header_date(const char *date, timestamp_t *timestamp, int *offset) 851{ 852 char *end; 853 timestamp_t stamp; 854 int ofs; 855 856 if (*date < '0' || '9' < *date) 857 return -1; 858 stamp = parse_timestamp(date, &end, 10); 859 if (*end != ' ' || stamp == TIME_MAX || (end[1] != '+' && end[1] != '-')) 860 return -1; 861 date = end + 2; 862 ofs = strtol(date, &end, 10); 863 if ((*end != '\0' && (*end != '\n')) || end != date + 4) 864 return -1; 865 ofs = (ofs / 100) * 60 + (ofs % 100); 866 if (date[-1] == '-') 867 ofs = -ofs; 868 *timestamp = stamp; 869 *offset = ofs; 870 return 0; 871} 872 873 874/* timestamp of 2099-12-31T23:59:59Z, including 32 leap days */ 875static const timestamp_t timestamp_max = (((timestamp_t)2100 - 1970) * 365 + 32) * 24 * 60 * 60 - 1; 876 877/* Gr. strptime is crap for this; it doesn't have a way to require RFC2822 878 (i.e. English) day/month names, and it doesn't work correctly with %z. */ 879int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset) 880{ 881 struct tm tm; 882 int tm_gmt; 883 timestamp_t dummy_timestamp; 884 int dummy_offset; 885 886 if (!timestamp) 887 timestamp = &dummy_timestamp; 888 if (!offset) 889 offset = &dummy_offset; 890 891 memset(&tm, 0, sizeof(tm)); 892 tm.tm_year = -1; 893 tm.tm_mon = -1; 894 tm.tm_mday = -1; 895 tm.tm_isdst = -1; 896 tm.tm_hour = -1; 897 tm.tm_min = -1; 898 tm.tm_sec = -1; 899 *offset = -1; 900 tm_gmt = 0; 901 902 if (*date == '@' && 903 !match_object_header_date(date + 1, timestamp, offset)) 904 return 0; /* success */ 905 for (;;) { 906 int match = 0; 907 unsigned char c = *date; 908 909 /* Stop at end of string or newline */ 910 if (!c || c == '\n') 911 break; 912 913 if (isalpha(c)) 914 match = match_alpha(date, &tm, offset); 915 else if (isdigit(c)) 916 match = match_digit(date, &tm, offset, &tm_gmt); 917 else if ((c == '-' || c == '+') && isdigit(date[1])) 918 match = match_tz(date, offset); 919 920 if (!match) { 921 /* BAD CRAP */ 922 match = 1; 923 } 924 925 date += match; 926 } 927 928 /* do not use mktime(), which uses local timezone, here */ 929 *timestamp = tm_to_time_t(&tm); 930 if (*timestamp == -1) 931 return -1; 932 933 if (*offset == -1) { 934 time_t temp_time; 935 936 /* gmtime_r() in match_digit() may have clobbered it */ 937 tm.tm_isdst = -1; 938 temp_time = mktime(&tm); 939 if ((time_t)*timestamp > temp_time) { 940 *offset = ((time_t)*timestamp - temp_time) / 60; 941 } else { 942 *offset = -(int)((temp_time - (time_t)*timestamp) / 60); 943 } 944 } 945 946 if (!tm_gmt) { 947 if (*offset > 0 && *offset * 60 > *timestamp) 948 return -1; 949 if (*offset < 0 && -*offset * 60 > timestamp_max - *timestamp) 950 return -1; 951 *timestamp -= *offset * 60; 952 } 953 954 return 0; /* success */ 955} 956 957int parse_expiry_date(const char *date, timestamp_t *timestamp) 958{ 959 int errors = 0; 960 961 if (!strcmp(date, "never") || !strcmp(date, "false")) 962 *timestamp = 0; 963 else if (!strcmp(date, "all") || !strcmp(date, "now")) 964 /* 965 * We take over "now" here, which usually translates 966 * to the current timestamp. This is because the user 967 * really means to expire everything that was done in 968 * the past, and by definition reflogs are the record 969 * of the past, and there is nothing from the future 970 * to be kept. 971 */ 972 *timestamp = TIME_MAX; 973 else 974 *timestamp = approxidate_careful(date, &errors); 975 976 return errors; 977} 978 979int parse_date(const char *date, struct strbuf *result) 980{ 981 timestamp_t timestamp; 982 int offset; 983 if (parse_date_basic(date, &timestamp, &offset)) 984 return -1; 985 date_string(timestamp, offset, result); 986 return 0; 987} 988 989static enum date_mode_type parse_date_type(const char *format, const char **end) 990{ 991 if (skip_prefix(format, "relative", end)) 992 return DATE_RELATIVE; 993 if (skip_prefix(format, "iso8601-strict", end) || 994 skip_prefix(format, "iso-strict", end)) 995 return DATE_ISO8601_STRICT; 996 if (skip_prefix(format, "iso8601", end) || 997 skip_prefix(format, "iso", end)) 998 return DATE_ISO8601; 999 if (skip_prefix(format, "rfc2822", end) || 1000 skip_prefix(format, "rfc", end)) 1001 return DATE_RFC2822; 1002 if (skip_prefix(format, "short", end)) 1003 return DATE_SHORT; 1004 if (skip_prefix(format, "default", end)) 1005 return DATE_NORMAL; 1006 if (skip_prefix(format, "human", end)) 1007 return DATE_HUMAN; 1008 if (skip_prefix(format, "raw", end)) 1009 return DATE_RAW; 1010 if (skip_prefix(format, "unix", end)) 1011 return DATE_UNIX; 1012 if (skip_prefix(format, "format", end)) 1013 return DATE_STRFTIME; 1014 /* 1015 * Please update $__git_log_date_formats in 1016 * git-completion.bash when you add new formats. 1017 */ 1018 1019 die("unknown date format %s", format); 1020} 1021 1022void parse_date_format(const char *format, struct date_mode *mode) 1023{ 1024 const char *p; 1025 1026 /* "auto:foo" is "if tty/pager, then foo, otherwise normal" */ 1027 if (skip_prefix(format, "auto:", &p)) { 1028 if (isatty(1) || pager_in_use()) 1029 format = p; 1030 else 1031 format = "default"; 1032 } 1033 1034 /* historical alias */ 1035 if (!strcmp(format, "local")) 1036 format = "default-local"; 1037 1038 mode->type = parse_date_type(format, &p); 1039 mode->local = 0; 1040 1041 if (skip_prefix(p, "-local", &p)) 1042 mode->local = 1; 1043 1044 if (mode->type == DATE_STRFTIME) { 1045 if (!skip_prefix(p, ":", &p)) 1046 die("date format missing colon separator: %s", format); 1047 mode->strftime_fmt = xstrdup(p); 1048 } else if (*p) 1049 die("unknown date format %s", format); 1050} 1051 1052void date_mode_release(struct date_mode *mode) 1053{ 1054 free((char *)mode->strftime_fmt); 1055} 1056 1057void datestamp(struct strbuf *out) 1058{ 1059 time_t now; 1060 int offset; 1061 struct tm tm = { 0 }; 1062 1063 time(&now); 1064 1065 offset = tm_to_time_t(localtime_r(&now, &tm)) - now; 1066 offset /= 60; 1067 1068 date_string(now, offset, out); 1069} 1070 1071/* 1072 * Relative time update (eg "2 days ago"). If we haven't set the time 1073 * yet, we need to set it from current time. 1074 */ 1075static time_t update_tm(struct tm *tm, struct tm *now, time_t sec) 1076{ 1077 time_t n; 1078 1079 if (tm->tm_mday < 0) 1080 tm->tm_mday = now->tm_mday; 1081 if (tm->tm_mon < 0) 1082 tm->tm_mon = now->tm_mon; 1083 if (tm->tm_year < 0) { 1084 tm->tm_year = now->tm_year; 1085 if (tm->tm_mon > now->tm_mon) 1086 tm->tm_year--; 1087 } 1088 1089 n = mktime(tm) - sec; 1090 localtime_r(&n, tm); 1091 return n; 1092} 1093 1094/* 1095 * Do we have a pending number at the end, or when 1096 * we see a new one? Let's assume it's a month day, 1097 * as in "Dec 6, 1992" 1098 */ 1099static void pending_number(struct tm *tm, int *num) 1100{ 1101 int number = *num; 1102 1103 if (number) { 1104 *num = 0; 1105 if (tm->tm_mday < 0 && number < 32) 1106 tm->tm_mday = number; 1107 else if (tm->tm_mon < 0 && number < 13) 1108 tm->tm_mon = number-1; 1109 else if (tm->tm_year < 0) { 1110 if (number > 1969 && number < 2100) 1111 tm->tm_year = number - 1900; 1112 else if (number > 69 && number < 100) 1113 tm->tm_year = number; 1114 else if (number < 38) 1115 tm->tm_year = 100 + number; 1116 /* We screw up for number = 00 ? */ 1117 } 1118 } 1119} 1120 1121static void date_now(struct tm *tm, struct tm *now, int *num) 1122{ 1123 *num = 0; 1124 update_tm(tm, now, 0); 1125} 1126 1127static void date_yesterday(struct tm *tm, struct tm *now, int *num) 1128{ 1129 *num = 0; 1130 update_tm(tm, now, 24*60*60); 1131} 1132 1133static void date_time(struct tm *tm, struct tm *now, int hour) 1134{ 1135 if (tm->tm_hour < hour) 1136 update_tm(tm, now, 24*60*60); 1137 tm->tm_hour = hour; 1138 tm->tm_min = 0; 1139 tm->tm_sec = 0; 1140} 1141 1142static void date_midnight(struct tm *tm, struct tm *now, int *num) 1143{ 1144 pending_number(tm, num); 1145 date_time(tm, now, 0); 1146} 1147 1148static void date_noon(struct tm *tm, struct tm *now, int *num) 1149{ 1150 pending_number(tm, num); 1151 date_time(tm, now, 12); 1152} 1153 1154static void date_tea(struct tm *tm, struct tm *now, int *num) 1155{ 1156 pending_number(tm, num); 1157 date_time(tm, now, 17); 1158} 1159 1160static void date_pm(struct tm *tm, struct tm *now UNUSED, int *num) 1161{ 1162 int hour, n = *num; 1163 *num = 0; 1164 1165 hour = tm->tm_hour; 1166 if (n) { 1167 hour = n; 1168 tm->tm_min = 0; 1169 tm->tm_sec = 0; 1170 } 1171 tm->tm_hour = (hour % 12) + 12; 1172} 1173 1174static void date_am(struct tm *tm, struct tm *now UNUSED, int *num) 1175{ 1176 int hour, n = *num; 1177 *num = 0; 1178 1179 hour = tm->tm_hour; 1180 if (n) { 1181 hour = n; 1182 tm->tm_min = 0; 1183 tm->tm_sec = 0; 1184 } 1185 tm->tm_hour = (hour % 12); 1186} 1187 1188static void date_never(struct tm *tm, struct tm *now UNUSED, int *num) 1189{ 1190 time_t n = 0; 1191 localtime_r(&n, tm); 1192 *num = 0; 1193} 1194 1195static const struct special { 1196 const char *name; 1197 void (*fn)(struct tm *, struct tm *, int *); 1198} special[] = { 1199 { "yesterday", date_yesterday }, 1200 { "noon", date_noon }, 1201 { "midnight", date_midnight }, 1202 { "tea", date_tea }, 1203 { "PM", date_pm }, 1204 { "AM", date_am }, 1205 { "never", date_never }, 1206 { "now", date_now }, 1207 { NULL } 1208}; 1209 1210static const char *number_name[] = { 1211 "zero", "one", "two", "three", "four", 1212 "five", "six", "seven", "eight", "nine", "ten", 1213}; 1214 1215static const struct typelen { 1216 const char *type; 1217 int length; 1218} typelen[] = { 1219 { "seconds", 1 }, 1220 { "minutes", 60 }, 1221 { "hours", 60*60 }, 1222 { "days", 24*60*60 }, 1223 { "weeks", 7*24*60*60 }, 1224 { NULL } 1225}; 1226 1227static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num, int *touched) 1228{ 1229 const struct typelen *tl; 1230 const struct special *s; 1231 const char *end = date; 1232 int i; 1233 1234 while (isalpha(*++end)) 1235 ; 1236 1237 for (i = 0; i < 12; i++) { 1238 int match = match_string(date, month_names[i]); 1239 if (match >= 3) { 1240 tm->tm_mon = i; 1241 *touched = 1; 1242 return end; 1243 } 1244 } 1245 1246 for (s = special; s->name; s++) { 1247 size_t len = strlen(s->name); 1248 if (match_string(date, s->name) == len) { 1249 s->fn(tm, now, num); 1250 *touched = 1; 1251 return end; 1252 } 1253 } 1254 1255 if (!*num) { 1256 for (i = 1; i < 11; i++) { 1257 size_t len = strlen(number_name[i]); 1258 if (match_string(date, number_name[i]) == len) { 1259 *num = i; 1260 *touched = 1; 1261 return end; 1262 } 1263 } 1264 if (match_string(date, "last") == 4) { 1265 *num = 1; 1266 *touched = 1; 1267 } 1268 return end; 1269 } 1270 1271 tl = typelen; 1272 while (tl->type) { 1273 size_t len = strlen(tl->type); 1274 if (match_string(date, tl->type) >= len-1) { 1275 update_tm(tm, now, tl->length * *num); 1276 *num = 0; 1277 *touched = 1; 1278 return end; 1279 } 1280 tl++; 1281 } 1282 1283 for (i = 0; i < 7; i++) { 1284 int match = match_string(date, weekday_names[i]); 1285 if (match >= 3) { 1286 int diff, n = *num -1; 1287 *num = 0; 1288 1289 diff = tm->tm_wday - i; 1290 if (diff <= 0) 1291 n++; 1292 diff += 7*n; 1293 1294 update_tm(tm, now, diff * 24 * 60 * 60); 1295 *touched = 1; 1296 return end; 1297 } 1298 } 1299 1300 if (match_string(date, "months") >= 5) { 1301 int n; 1302 update_tm(tm, now, 0); /* fill in date fields if needed */ 1303 n = tm->tm_mon - *num; 1304 *num = 0; 1305 while (n < 0) { 1306 n += 12; 1307 tm->tm_year--; 1308 } 1309 tm->tm_mon = n; 1310 *touched = 1; 1311 return end; 1312 } 1313 1314 if (match_string(date, "years") >= 4) { 1315 update_tm(tm, now, 0); /* fill in date fields if needed */ 1316 tm->tm_year -= *num; 1317 *num = 0; 1318 *touched = 1; 1319 return end; 1320 } 1321 1322 return end; 1323} 1324 1325static const char *approxidate_digit(const char *date, struct tm *tm, int *num, 1326 time_t now) 1327{ 1328 char *end; 1329 timestamp_t number = parse_timestamp(date, &end, 10); 1330 1331 switch (*end) { 1332 case ':': 1333 case '.': 1334 case '/': 1335 case '-': 1336 if (isdigit(end[1])) { 1337 int match = match_multi_number(number, *end, date, end, 1338 tm, now); 1339 if (match) 1340 return date + match; 1341 } 1342 } 1343 1344 /* Accept zero-padding only for small numbers ("Dec 02", never "Dec 0002") */ 1345 if (date[0] != '0' || end - date <= 2) 1346 *num = number; 1347 return end; 1348} 1349 1350static timestamp_t approxidate_str(const char *date, 1351 const struct timeval *tv, 1352 int *error_ret) 1353{ 1354 int number = 0; 1355 int touched = 0; 1356 struct tm tm, now; 1357 time_t time_sec; 1358 1359 time_sec = tv->tv_sec; 1360 localtime_r(&time_sec, &tm); 1361 now = tm; 1362 1363 tm.tm_year = -1; 1364 tm.tm_mon = -1; 1365 tm.tm_mday = -1; 1366 1367 for (;;) { 1368 unsigned char c = *date; 1369 if (!c) 1370 break; 1371 date++; 1372 if (isdigit(c)) { 1373 pending_number(&tm, &number); 1374 date = approxidate_digit(date-1, &tm, &number, time_sec); 1375 touched = 1; 1376 continue; 1377 } 1378 if (isalpha(c)) 1379 date = approxidate_alpha(date-1, &tm, &now, &number, &touched); 1380 } 1381 pending_number(&tm, &number); 1382 if (!touched) 1383 *error_ret = 1; 1384 return (timestamp_t)update_tm(&tm, &now, 0); 1385} 1386 1387timestamp_t approxidate_careful(const char *date, int *error_ret) 1388{ 1389 struct timeval tv; 1390 timestamp_t timestamp; 1391 int offset; 1392 int dummy = 0; 1393 if (!error_ret) 1394 error_ret = &dummy; 1395 1396 if (!parse_date_basic(date, &timestamp, &offset)) { 1397 *error_ret = 0; 1398 return timestamp; 1399 } 1400 1401 get_time(&tv); 1402 return approxidate_str(date, &tv, error_ret); 1403} 1404 1405int date_overflows(timestamp_t t) 1406{ 1407 time_t sys; 1408 1409 /* If we overflowed our timestamp data type, that's bad... */ 1410 if ((uintmax_t)t >= TIME_MAX) 1411 return 1; 1412 1413 /* 1414 * ...but we also are going to feed the result to system 1415 * functions that expect time_t, which is often "signed long". 1416 * Make sure that we fit into time_t, as well. 1417 */ 1418 sys = t; 1419 return t != sys || (t < 1) != (sys < 1); 1420}