github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/sqlite3/date.patch (about) 1 # Backport from 3.46. 2 # https://sqlite.org/draft/releaselog/current.html 3 --- sqlite3.c.orig 4 +++ sqlite3.c 5 @@ -71,13 +71,14 @@ struct DateTime { 6 int tz; /* Timezone offset in minutes */ 7 double s; /* Seconds */ 8 char validJD; /* True (1) if iJD is valid */ 9 - char rawS; /* Raw numeric value stored in s */ 10 char validYMD; /* True (1) if Y,M,D are valid */ 11 char validHMS; /* True (1) if h,m,s are valid */ 12 - char validTZ; /* True (1) if tz is valid */ 13 - char tzSet; /* Timezone was set explicitly */ 14 - char isError; /* An overflow has occurred */ 15 - char useSubsec; /* Display subsecond precision */ 16 + char nFloor; /* Days to implement "floor" */ 17 + unsigned rawS : 1; /* Raw numeric value stored in s */ 18 + unsigned isError : 1; /* An overflow has occurred */ 19 + unsigned useSubsec : 1; /* Display subsecond precision */ 20 + unsigned isUtc : 1; /* Time is known to be UTC */ 21 + unsigned isLocal : 1; /* Time is known to be localtime */ 22 }; 23 24 25 @@ -175,6 +176,8 @@ static int parseTimezone(const char *zDate, DateTime *p){ 26 sgn = +1; 27 }else if( c=='Z' || c=='z' ){ 28 zDate++; 29 + p->isLocal = 0; 30 + p->isUtc = 1; 31 goto zulu_time; 32 }else{ 33 return c!=0; 34 @@ -187,7 +190,6 @@ static int parseTimezone(const char *zDate, DateTime *p){ 35 p->tz = sgn*(nMn + nHr*60); 36 zulu_time: 37 while( sqlite3Isspace(*zDate) ){ zDate++; } 38 - p->tzSet = 1; 39 return *zDate!=0; 40 } 41 42 @@ -231,7 +233,6 @@ static int parseHhMmSs(const char *zDate, DateTime *p){ 43 p->m = m; 44 p->s = s + ms; 45 if( parseTimezone(zDate, p) ) return 1; 46 - p->validTZ = (p->tz!=0)?1:0; 47 return 0; 48 } 49 50 @@ -278,15 +279,40 @@ static void computeJD(DateTime *p){ 51 p->validJD = 1; 52 if( p->validHMS ){ 53 p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000 + 0.5); 54 - if( p->validTZ ){ 55 + if( p->tz ){ 56 p->iJD -= p->tz*60000; 57 p->validYMD = 0; 58 p->validHMS = 0; 59 - p->validTZ = 0; 60 + p->tz = 0; 61 + p->isUtc = 1; 62 + p->isLocal = 0; 63 } 64 } 65 } 66 67 +/* 68 +** Given the YYYY-MM-DD information current in p, determine if there 69 +** is day-of-month overflow and set nFloor to the number of days that 70 +** would need to be subtracted from the date in order to bring the 71 +** date back to the end of the month. 72 +*/ 73 +static void computeFloor(DateTime *p){ 74 + assert( p->validYMD || p->isError ); 75 + assert( p->D>=0 && p->D<=31 ); 76 + assert( p->M>=0 && p->M<=12 ); 77 + if( p->D<=28 ){ 78 + p->nFloor = 0; 79 + }else if( (1<<p->M) & 0x15aa ){ 80 + p->nFloor = 0; 81 + }else if( p->M!=2 ){ 82 + p->nFloor = (p->D==31); 83 + }else if( p->Y%4!=0 || (p->Y%100==0 && p->Y%400!=0) ){ 84 + p->nFloor = p->D - 28; 85 + }else{ 86 + p->nFloor = p->D - 29; 87 + } 88 +} 89 + 90 /* 91 ** Parse dates of the form 92 ** 93 @@ -325,12 +351,16 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){ 94 p->Y = neg ? -Y : Y; 95 p->M = M; 96 p->D = D; 97 - if( p->validTZ ){ 98 + computeFloor(p); 99 + if( p->tz ){ 100 computeJD(p); 101 } 102 return 0; 103 } 104 105 + 106 +static void clearYMD_HMS_TZ(DateTime *p); /* Forward declaration */ 107 + 108 /* 109 ** Set the time to the current time reported by the VFS. 110 ** 111 @@ -340,6 +370,9 @@ static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){ 112 p->iJD = sqlite3StmtCurrentTime(context); 113 if( p->iJD>0 ){ 114 p->validJD = 1; 115 + p->isUtc = 1; 116 + p->isLocal = 0; 117 + clearYMD_HMS_TZ(p); 118 return 0; 119 }else{ 120 return 1; 121 @@ -478,7 +511,7 @@ static void computeYMD_HMS(DateTime *p){ 122 static void clearYMD_HMS_TZ(DateTime *p){ 123 p->validYMD = 0; 124 p->validHMS = 0; 125 - p->validTZ = 0; 126 + p->tz = 0; 127 } 128 129 #ifndef SQLITE_OMIT_LOCALTIME 130 @@ -610,7 +643,7 @@ static int toLocaltime( 131 p->validHMS = 1; 132 p->validJD = 0; 133 p->rawS = 0; 134 - p->validTZ = 0; 135 + p->tz = 0; 136 p->isError = 0; 137 return SQLITE_OK; 138 } 139 @@ -630,12 +663,12 @@ static const struct { 140 float rLimit; /* Maximum NNN value for this transform */ 141 float rXform; /* Constant used for this transform */ 142 } aXformType[] = { 143 - { 6, "second", 4.6427e+14, 1.0 }, 144 - { 6, "minute", 7.7379e+12, 60.0 }, 145 - { 4, "hour", 1.2897e+11, 3600.0 }, 146 - { 3, "day", 5373485.0, 86400.0 }, 147 - { 5, "month", 176546.0, 2592000.0 }, 148 - { 4, "year", 14713.0, 31536000.0 }, 149 + /* 0 */ { 6, "second", 4.6427e+14, 1.0 }, 150 + /* 1 */ { 6, "minute", 7.7379e+12, 60.0 }, 151 + /* 2 */ { 4, "hour", 1.2897e+11, 3600.0 }, 152 + /* 3 */ { 3, "day", 5373485.0, 86400.0 }, 153 + /* 4 */ { 5, "month", 176546.0, 30.0*86400.0 }, 154 + /* 5 */ { 4, "year", 14713.0, 365.0*86400.0 }, 155 }; 156 157 /* 158 @@ -667,14 +700,20 @@ static void autoAdjustDate(DateTime *p){ 159 ** NNN.NNNN seconds 160 ** NNN months 161 ** NNN years 162 +** +/-YYYY-MM-DD HH:MM:SS.SSS 163 +** ceiling 164 +** floor 165 ** start of month 166 ** start of year 167 ** start of week 168 ** start of day 169 ** weekday N 170 ** unixepoch 171 +** auto 172 ** localtime 173 ** utc 174 +** subsec 175 +** subsecond 176 ** 177 ** Return 0 on success and 1 if there is any kind of error. If the error 178 ** is in a system call (i.e. localtime()), then an error message is written 179 @@ -705,6 +744,37 @@ static int parseModifier( 180 } 181 break; 182 } 183 + case 'c': { 184 + /* 185 + ** ceiling 186 + ** 187 + ** Resolve day-of-month overflow by rolling forward into the next 188 + ** month. As this is the default action, this modifier is really 189 + ** a no-op that is only included for symmetry. See "floor". 190 + */ 191 + if( sqlite3_stricmp(z, "ceiling")==0 ){ 192 + computeJD(p); 193 + clearYMD_HMS_TZ(p); 194 + rc = 0; 195 + p->nFloor = 0; 196 + } 197 + break; 198 + } 199 + case 'f': { 200 + /* 201 + ** floor 202 + ** 203 + ** Resolve day-of-month overflow by rolling back to the end of the 204 + ** previous month. 205 + */ 206 + if( sqlite3_stricmp(z, "floor")==0 ){ 207 + computeJD(p); 208 + p->iJD -= p->nFloor*86400000; 209 + clearYMD_HMS_TZ(p); 210 + rc = 0; 211 + } 212 + break; 213 + } 214 case 'j': { 215 /* 216 ** julianday 217 @@ -731,7 +801,9 @@ static int parseModifier( 218 ** show local time. 219 */ 220 if( sqlite3_stricmp(z, "localtime")==0 && sqlite3NotPureFunc(pCtx) ){ 221 - rc = toLocaltime(p, pCtx); 222 + rc = p->isLocal ? SQLITE_OK : toLocaltime(p, pCtx); 223 + p->isUtc = 0; 224 + p->isLocal = 1; 225 } 226 break; 227 } 228 @@ -756,7 +828,7 @@ static int parseModifier( 229 } 230 #ifndef SQLITE_OMIT_LOCALTIME 231 else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){ 232 - if( p->tzSet==0 ){ 233 + if( p->isUtc==0 ){ 234 i64 iOrigJD; /* Original localtime */ 235 i64 iGuess; /* Guess at the corresponding utc time */ 236 int cnt = 0; /* Safety to prevent infinite loop */ 237 @@ -779,7 +851,8 @@ static int parseModifier( 238 memset(p, 0, sizeof(*p)); 239 p->iJD = iGuess; 240 p->validJD = 1; 241 - p->tzSet = 1; 242 + p->isUtc = 1; 243 + p->isLocal = 0; 244 } 245 rc = SQLITE_OK; 246 } 247 @@ -799,7 +872,7 @@ static int parseModifier( 248 && r>=0.0 && r<7.0 && (n=(int)r)==r ){ 249 sqlite3_int64 Z; 250 computeYMD_HMS(p); 251 - p->validTZ = 0; 252 + p->tz = 0; 253 p->validJD = 0; 254 computeJD(p); 255 Z = ((p->iJD + 129600000)/86400000) % 7; 256 @@ -839,7 +912,7 @@ static int parseModifier( 257 p->h = p->m = 0; 258 p->s = 0.0; 259 p->rawS = 0; 260 - p->validTZ = 0; 261 + p->tz = 0; 262 p->validJD = 0; 263 if( sqlite3_stricmp(z,"month")==0 ){ 264 p->D = 1; 265 @@ -910,6 +983,7 @@ static int parseModifier( 266 x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; 267 p->Y += x; 268 p->M -= x*12; 269 + computeFloor(p); 270 computeJD(p); 271 p->validHMS = 0; 272 p->validYMD = 0; 273 @@ -956,11 +1030,12 @@ static int parseModifier( 274 z += n; 275 while( sqlite3Isspace(*z) ) z++; 276 n = sqlite3Strlen30(z); 277 - if( n>10 || n<3 ) break; 278 + if( n<3 || n>10 ) break; 279 if( sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--; 280 computeJD(p); 281 assert( rc==1 ); 282 rRounder = r<0 ? -0.5 : +0.5; 283 + p->nFloor = 0; 284 for(i=0; i<ArraySize(aXformType); i++){ 285 if( aXformType[i].nName==n 286 && sqlite3_strnicmp(aXformType[i].zName, z, n)==0 287 @@ -968,21 +1043,24 @@ static int parseModifier( 288 ){ 289 switch( i ){ 290 case 4: { /* Special processing to add months */ 291 - assert( strcmp(aXformType[i].zName,"month")==0 ); 292 + assert( strcmp(aXformType[4].zName,"month")==0 ); 293 computeYMD_HMS(p); 294 p->M += (int)r; 295 x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; 296 p->Y += x; 297 p->M -= x*12; 298 + computeFloor(p); 299 p->validJD = 0; 300 r -= (int)r; 301 break; 302 } 303 case 5: { /* Special processing to add years */ 304 int y = (int)r; 305 - assert( strcmp(aXformType[i].zName,"year")==0 ); 306 + assert( strcmp(aXformType[5].zName,"year")==0 ); 307 computeYMD_HMS(p); 308 + assert( p->M>=0 && p->M<=12 ); 309 p->Y += y; 310 + computeFloor(p); 311 p->validJD = 0; 312 r -= (int)r; 313 break; 314 @@ -1236,22 +1314,83 @@ static void dateFunc( 315 } 316 } 317 318 +/* 319 +** Compute the number of days after the most recent January 1. 320 +** 321 +** In other words, compute the zero-based day number for the 322 +** current year: 323 +** 324 +** Jan01 = 0, Jan02 = 1, ..., Jan31 = 30, Feb01 = 31, ... 325 +** Dec31 = 364 or 365. 326 +*/ 327 +static int daysAfterJan01(DateTime *pDate){ 328 + DateTime jan01 = *pDate; 329 + assert( jan01.validYMD ); 330 + assert( jan01.validHMS ); 331 + assert( pDate->validJD ); 332 + jan01.validJD = 0; 333 + jan01.M = 1; 334 + jan01.D = 1; 335 + computeJD(&jan01); 336 + return (int)((pDate->iJD-jan01.iJD+43200000)/86400000); 337 +} 338 + 339 +/* 340 +** Return the number of days after the most recent Monday. 341 +** 342 +** In other words, return the day of the week according 343 +** to this code: 344 +** 345 +** 0=Monday, 1=Tuesday, 2=Wednesday, ..., 6=Sunday. 346 +*/ 347 +static int daysAfterMonday(DateTime *pDate){ 348 + assert( pDate->validJD ); 349 + return (int)((pDate->iJD+43200000)/86400000) % 7; 350 +} 351 + 352 +/* 353 +** Return the number of days after the most recent Sunday. 354 +** 355 +** In other words, return the day of the week according 356 +** to this code: 357 +** 358 +** 0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday 359 +*/ 360 +static int daysAfterSunday(DateTime *pDate){ 361 + assert( pDate->validJD ); 362 + return (int)((pDate->iJD+129600000)/86400000) % 7; 363 +} 364 + 365 /* 366 ** strftime( FORMAT, TIMESTRING, MOD, MOD, ...) 367 ** 368 ** Return a string described by FORMAT. Conversions as follows: 369 ** 370 -** %d day of month 371 +** %d day of month 01-31 372 +** %e day of month 1-31 373 ** %f ** fractional seconds SS.SSS 374 +** %F ISO date. YYYY-MM-DD 375 +** %G ISO year corresponding to %V 0000-9999. 376 +** %g 2-digit ISO year corresponding to %V 00-99 377 ** %H hour 00-24 378 -** %j day of year 000-366 379 +** %k hour 0-24 (leading zero converted to space) 380 +** %I hour 01-12 381 +** %j day of year 001-366 382 ** %J ** julian day number 383 +** %l hour 1-12 (leading zero converted to space) 384 ** %m month 01-12 385 ** %M minute 00-59 386 +** %p "am" or "pm" 387 +** %P "AM" or "PM" 388 +** %R time as HH:MM 389 ** %s seconds since 1970-01-01 390 ** %S seconds 00-59 391 -** %w day of week 0-6 Sunday==0 392 -** %W week of year 00-53 393 +** %T time as HH:MM:SS 394 +** %u day of week 1-7 Monday==1, Sunday==7 395 +** %w day of week 0-6 Sunday==0, Monday==1 396 +** %U week of year 00-53 (First Sunday is start of week 01) 397 +** %V week of year 01-53 (First week containing Thursday is week 01) 398 +** %W week of year 00-53 (First Monday is start of week 01) 399 ** %Y year 0000-9999 400 ** %% % 401 */ 402 @@ -1288,7 +1427,7 @@ static void strftimeFunc( 403 sqlite3_str_appendf(&sRes, cf=='d' ? "%02d" : "%2d", x.D); 404 break; 405 } 406 - case 'f': { 407 + case 'f': { /* Fractional seconds. (Non-standard) */ 408 double s = x.s; 409 if( s>59.999 ) s = 59.999; 410 sqlite3_str_appendf(&sRes, "%06.3f", s); 411 @@ -1298,6 +1437,21 @@ static void strftimeFunc( 412 sqlite3_str_appendf(&sRes, "%04d-%02d-%02d", x.Y, x.M, x.D); 413 break; 414 } 415 + case 'G': /* Fall thru */ 416 + case 'g': { 417 + DateTime y = x; 418 + assert( y.validJD ); 419 + /* Move y so that it is the Thursday in the same week as x */ 420 + y.iJD += (3 - daysAfterMonday(&x))*86400000; 421 + y.validYMD = 0; 422 + computeYMD(&y); 423 + if( cf=='g' ){ 424 + sqlite3_str_appendf(&sRes, "%02d", y.Y%100); 425 + }else{ 426 + sqlite3_str_appendf(&sRes, "%04d", y.Y); 427 + } 428 + break; 429 + } 430 case 'H': 431 case 'k': { 432 sqlite3_str_appendf(&sRes, cf=='H' ? "%02d" : "%2d", x.h); 433 @@ -1311,25 +1465,11 @@ static void strftimeFunc( 434 sqlite3_str_appendf(&sRes, cf=='I' ? "%02d" : "%2d", h); 435 break; 436 } 437 - case 'W': /* Fall thru */ 438 - case 'j': { 439 - int nDay; /* Number of days since 1st day of year */ 440 - DateTime y = x; 441 - y.validJD = 0; 442 - y.M = 1; 443 - y.D = 1; 444 - computeJD(&y); 445 - nDay = (int)((x.iJD-y.iJD+43200000)/86400000); 446 - if( cf=='W' ){ 447 - int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */ 448 - wd = (int)(((x.iJD+43200000)/86400000)%7); 449 - sqlite3_str_appendf(&sRes,"%02d",(nDay+7-wd)/7); 450 - }else{ 451 - sqlite3_str_appendf(&sRes,"%03d",nDay+1); 452 - } 453 + case 'j': { /* Day of year. Jan01==1, Jan02==2, and so forth */ 454 + sqlite3_str_appendf(&sRes,"%03d",daysAfterJan01(&x)+1); 455 break; 456 } 457 - case 'J': { 458 + case 'J': { /* Julian day number. (Non-standard) */ 459 sqlite3_str_appendf(&sRes,"%.16g",x.iJD/86400000.0); 460 break; 461 } 462 @@ -1372,13 +1512,33 @@ static void strftimeFunc( 463 sqlite3_str_appendf(&sRes,"%02d:%02d:%02d", x.h, x.m, (int)x.s); 464 break; 465 } 466 - case 'u': /* Fall thru */ 467 - case 'w': { 468 - char c = (char)(((x.iJD+129600000)/86400000) % 7) + '0'; 469 + case 'u': /* Day of week. 1 to 7. Monday==1, Sunday==7 */ 470 + case 'w': { /* Day of week. 0 to 6. Sunday==0, Monday==1 */ 471 + char c = (char)daysAfterSunday(&x) + '0'; 472 if( c=='0' && cf=='u' ) c = '7'; 473 sqlite3_str_appendchar(&sRes, 1, c); 474 break; 475 } 476 + case 'U': { /* Week num. 00-53. First Sun of the year is week 01 */ 477 + sqlite3_str_appendf(&sRes,"%02d", 478 + (daysAfterJan01(&x)-daysAfterSunday(&x)+7)/7); 479 + break; 480 + } 481 + case 'V': { /* Week num. 01-53. First week with a Thur is week 01 */ 482 + DateTime y = x; 483 + /* Adjust y so that is the Thursday in the same week as x */ 484 + assert( y.validJD ); 485 + y.iJD += (3 - daysAfterMonday(&x))*86400000; 486 + y.validYMD = 0; 487 + computeYMD(&y); 488 + sqlite3_str_appendf(&sRes,"%02d", daysAfterJan01(&y)/7+1); 489 + break; 490 + } 491 + case 'W': { /* Week num. 00-53. First Mon of the year is week 01 */ 492 + sqlite3_str_appendf(&sRes,"%02d", 493 + (daysAfterJan01(&x)-daysAfterMonday(&x)+7)/7); 494 + break; 495 + } 496 case 'Y': { 497 sqlite3_str_appendf(&sRes,"%04d",x.Y); 498 break; 499 @@ -1525,9 +1685,7 @@ static void timediffFunc( 500 d1.iJD = d2.iJD - d1.iJD; 501 d1.iJD += (u64)1486995408 * (u64)100000; 502 } 503 - d1.validYMD = 0; 504 - d1.validHMS = 0; 505 - d1.validTZ = 0; 506 + clearYMD_HMS_TZ(&d1); 507 computeYMD_HMS(&d1); 508 sqlite3StrAccumInit(&sRes, 0, 0, 0, 100); 509 sqlite3_str_appendf(&sRes, "%c%04d-%02d-%02d %02d:%02d:%06.3f", 510 @@ -1596,6 +1754,36 @@ static void currentTimeFunc( 511 } 512 #endif 513 514 +#if !defined(SQLITE_OMIT_DATETIME_FUNCS) && defined(SQLITE_DEBUG) 515 +/* 516 +** datedebug(...) 517 +** 518 +** This routine returns JSON that describes the internal DateTime object. 519 +** Used for debugging and testing only. Subject to change. 520 +*/ 521 +static void datedebugFunc( 522 + sqlite3_context *context, 523 + int argc, 524 + sqlite3_value **argv 525 +){ 526 + DateTime x; 527 + if( isDate(context, argc, argv, &x)==0 ){ 528 + char *zJson; 529 + zJson = sqlite3_mprintf( 530 + "{iJD:%lld,Y:%d,M:%d,D:%d,h:%d,m:%d,tz:%d," 531 + "s:%.3f,validJD:%d,validYMS:%d,validHMS:%d," 532 + "nFloor:%d,rawS:%d,isError:%d,useSubsec:%d," 533 + "isUtc:%d,isLocal:%d}", 534 + x.iJD, x.Y, x.M, x.D, x.h, x.m, x.tz, 535 + x.s, x.validJD, x.validYMD, x.validHMS, 536 + x.nFloor, x.rawS, x.isError, x.useSubsec, 537 + x.isUtc, x.isLocal); 538 + sqlite3_result_text(context, zJson, -1, sqlite3_free); 539 + } 540 +} 541 +#endif /* !SQLITE_OMIT_DATETIME_FUNCS && SQLITE_DEBUG */ 542 + 543 + 544 /* 545 ** This function registered all of the above C functions as SQL 546 ** functions. This should be the only routine in this file with 547 @@ -1611,6 +1799,9 @@ void sqlite3RegisterDateTimeFunctions(void){ 548 PURE_DATE(datetime, -1, 0, 0, datetimeFunc ), 549 PURE_DATE(strftime, -1, 0, 0, strftimeFunc ), 550 PURE_DATE(timediff, 2, 0, 0, timediffFunc ), 551 +#ifdef SQLITE_DEBUG 552 + PURE_DATE(datedebug, -1, 0, 0, datedebugFunc ), 553 +#endif 554 DFUNCTION(current_time, 0, 0, 0, ctimeFunc ), 555 DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc), 556 DFUNCTION(current_date, 0, 0, 0, cdateFunc ),