Fossil SCM
Improved processing of timestamp strings used as check-in identifiers. The ISO8601 date without punctuation (digits only) is now accept on the tag:date format. Also, incomplete ISO8601 date/time values are rounding up instead of rounded down, such that the match on 20200101 actually finds a check-in on 2020-01-01 (if any) rather than the last check-in on 2019-12-31.
Commit
a5f3103111ee0866fd3a3768933a171949c38a5a47cc9409d2804ed7e971930a
Parent
53d1f053ad92716…
2 files changed
+48
-8
+7
-5
+48
-8
| --- src/name.c | ||
| +++ src/name.c | ||
| @@ -105,10 +105,43 @@ | ||
| 105 | 105 | } |
| 106 | 106 | |
| 107 | 107 | /* It looks like this may be a date. Return it with punctuation added. */ |
| 108 | 108 | return zEDate; |
| 109 | 109 | } |
| 110 | + | |
| 111 | +/* | |
| 112 | +** The data-time string in the argument is going to be used as an | |
| 113 | +** upper bound like this: mtime<=julianday(zDate,'localtime'). | |
| 114 | +** But if the zDate parameter omits the fractional seconds or the | |
| 115 | +** seconds, or the time, that might mess up the == part of the | |
| 116 | +** comparison. So add in missing factional seconds or seconds or time. | |
| 117 | +** | |
| 118 | +** The returned string is held in a static buffer that is overwritten | |
| 119 | +** with each call, or else is just a copy of its input if there are | |
| 120 | +** no changes. | |
| 121 | +*/ | |
| 122 | +const char *fossil_roundup_date(const char *zDate){ | |
| 123 | + static char zUp[24]; | |
| 124 | + int n = (int)strlen(zDate); | |
| 125 | + if( n==19 ){ /* YYYY-MM-DD HH:MM:SS */ | |
| 126 | + memcpy(zUp, zDate, 19); | |
| 127 | + memcpy(zUp+19, ".999", 5); | |
| 128 | + return zUp; | |
| 129 | + } | |
| 130 | + if( n==16 ){ /* YYYY-MM-DD HH:MM */ | |
| 131 | + memcpy(zUp, zDate, 16); | |
| 132 | + memcpy(zUp+16, ":59.999", 8); | |
| 133 | + return zUp; | |
| 134 | + } | |
| 135 | + if( n==10 ){ /* YYYY-MM-DD */ | |
| 136 | + memcpy(zUp, zDate, 10); | |
| 137 | + memcpy(zUp+10, " 23:59:59.999", 14); | |
| 138 | + return zUp; | |
| 139 | + } | |
| 140 | + return zDate; | |
| 141 | +} | |
| 142 | + | |
| 110 | 143 | |
| 111 | 144 | /* |
| 112 | 145 | ** Return the RID that is the "root" of the branch that contains |
| 113 | 146 | ** check-in "rid". Details depending on eType: |
| 114 | 147 | ** |
| @@ -235,19 +268,19 @@ | ||
| 235 | 268 | if( zDate==0 ) zDate = &zTag[5]; |
| 236 | 269 | rid = db_int(0, |
| 237 | 270 | "SELECT objid FROM event" |
| 238 | 271 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 239 | 272 | " ORDER BY mtime DESC LIMIT 1", |
| 240 | - zDate, zType); | |
| 273 | + fossil_roundup_date(zDate), zType); | |
| 241 | 274 | return rid; |
| 242 | 275 | } |
| 243 | 276 | if( fossil_isdate(zTag) ){ |
| 244 | 277 | rid = db_int(0, |
| 245 | 278 | "SELECT objid FROM event" |
| 246 | 279 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 247 | 280 | " ORDER BY mtime DESC LIMIT 1", |
| 248 | - zTag, zType); | |
| 281 | + fossil_roundup_date(zTag), zType); | |
| 249 | 282 | if( rid) return rid; |
| 250 | 283 | } |
| 251 | 284 | |
| 252 | 285 | /* Deprecated date & time formats: "local:" + date-time and |
| 253 | 286 | ** "utc:" + date-time */ |
| @@ -262,11 +295,11 @@ | ||
| 262 | 295 | if( memcmp(zTag, "utc:", 4)==0 ){ |
| 263 | 296 | rid = db_int(0, |
| 264 | 297 | "SELECT objid FROM event" |
| 265 | 298 | " WHERE mtime<=julianday('%qz') AND type GLOB '%q'" |
| 266 | 299 | " ORDER BY mtime DESC LIMIT 1", |
| 267 | - &zTag[4], zType); | |
| 300 | + fossil_roundup_date(&zTag[4]), zType); | |
| 268 | 301 | return rid; |
| 269 | 302 | } |
| 270 | 303 | |
| 271 | 304 | /* "tag:" + symbolic-name */ |
| 272 | 305 | if( memcmp(zTag, "tag:", 4)==0 ){ |
| @@ -294,29 +327,36 @@ | ||
| 294 | 327 | return start_of_branch(rid, 2); |
| 295 | 328 | } |
| 296 | 329 | |
| 297 | 330 | /* symbolic-name ":" date-time */ |
| 298 | 331 | nTag = strlen(zTag); |
| 299 | - for(i=0; i<nTag-10 && zTag[i]!=':'; i++){} | |
| 300 | - if( zTag[i]==':' && fossil_isdate(&zTag[i+1]) ){ | |
| 332 | + for(i=0; i<nTag-8 && zTag[i]!=':'; i++){} | |
| 333 | + if( zTag[i]==':' | |
| 334 | + && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0)!=0) | |
| 335 | + ){ | |
| 301 | 336 | char *zDate = mprintf("%s", &zTag[i+1]); |
| 302 | 337 | char *zTagBase = mprintf("%.*s", i, zTag); |
| 338 | + char *zXDate; | |
| 303 | 339 | int nDate = strlen(zDate); |
| 304 | 340 | if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ |
| 305 | 341 | zDate[nDate-3] = 'z'; |
| 306 | 342 | zDate[nDate-2] = 0; |
| 307 | 343 | } |
| 344 | + zXDate = fossil_expand_datetime(zDate,0); | |
| 345 | + if( zXDate==0 ) zXDate = zDate; | |
| 308 | 346 | rid = db_int(0, |
| 309 | 347 | "SELECT event.objid, max(event.mtime)" |
| 310 | 348 | " FROM tag, tagxref, event" |
| 311 | 349 | " WHERE tag.tagname='sym-%q' " |
| 312 | 350 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 313 | 351 | " AND event.objid=tagxref.rid " |
| 314 | - " AND event.mtime<=julianday(%Q)" | |
| 352 | + " AND event.mtime<=julianday(%Q,fromLocal())" | |
| 315 | 353 | " AND event.type GLOB '%q'", |
| 316 | - zTagBase, zDate, zType | |
| 354 | + zTagBase, fossil_roundup_date(zXDate), zType | |
| 317 | 355 | ); |
| 356 | + fossil_free(zDate); | |
| 357 | + fossil_free(zTagBase); | |
| 318 | 358 | return rid; |
| 319 | 359 | } |
| 320 | 360 | |
| 321 | 361 | /* Remove optional [...] */ |
| 322 | 362 | zXTag = zTag; |
| @@ -381,11 +421,11 @@ | ||
| 381 | 421 | if( zDate ){ |
| 382 | 422 | rid = db_int(0, |
| 383 | 423 | "SELECT objid FROM event" |
| 384 | 424 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 385 | 425 | " ORDER BY mtime DESC LIMIT 1", |
| 386 | - zDate, zType); | |
| 426 | + fossil_roundup_date(zDate), zType); | |
| 387 | 427 | if( rid) return rid; |
| 388 | 428 | } |
| 389 | 429 | |
| 390 | 430 | |
| 391 | 431 | /* Undocumented: numeric tags get translated directly into the RID */ |
| 392 | 432 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -105,10 +105,43 @@ | |
| 105 | } |
| 106 | |
| 107 | /* It looks like this may be a date. Return it with punctuation added. */ |
| 108 | return zEDate; |
| 109 | } |
| 110 | |
| 111 | /* |
| 112 | ** Return the RID that is the "root" of the branch that contains |
| 113 | ** check-in "rid". Details depending on eType: |
| 114 | ** |
| @@ -235,19 +268,19 @@ | |
| 235 | if( zDate==0 ) zDate = &zTag[5]; |
| 236 | rid = db_int(0, |
| 237 | "SELECT objid FROM event" |
| 238 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 239 | " ORDER BY mtime DESC LIMIT 1", |
| 240 | zDate, zType); |
| 241 | return rid; |
| 242 | } |
| 243 | if( fossil_isdate(zTag) ){ |
| 244 | rid = db_int(0, |
| 245 | "SELECT objid FROM event" |
| 246 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 247 | " ORDER BY mtime DESC LIMIT 1", |
| 248 | zTag, zType); |
| 249 | if( rid) return rid; |
| 250 | } |
| 251 | |
| 252 | /* Deprecated date & time formats: "local:" + date-time and |
| 253 | ** "utc:" + date-time */ |
| @@ -262,11 +295,11 @@ | |
| 262 | if( memcmp(zTag, "utc:", 4)==0 ){ |
| 263 | rid = db_int(0, |
| 264 | "SELECT objid FROM event" |
| 265 | " WHERE mtime<=julianday('%qz') AND type GLOB '%q'" |
| 266 | " ORDER BY mtime DESC LIMIT 1", |
| 267 | &zTag[4], zType); |
| 268 | return rid; |
| 269 | } |
| 270 | |
| 271 | /* "tag:" + symbolic-name */ |
| 272 | if( memcmp(zTag, "tag:", 4)==0 ){ |
| @@ -294,29 +327,36 @@ | |
| 294 | return start_of_branch(rid, 2); |
| 295 | } |
| 296 | |
| 297 | /* symbolic-name ":" date-time */ |
| 298 | nTag = strlen(zTag); |
| 299 | for(i=0; i<nTag-10 && zTag[i]!=':'; i++){} |
| 300 | if( zTag[i]==':' && fossil_isdate(&zTag[i+1]) ){ |
| 301 | char *zDate = mprintf("%s", &zTag[i+1]); |
| 302 | char *zTagBase = mprintf("%.*s", i, zTag); |
| 303 | int nDate = strlen(zDate); |
| 304 | if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ |
| 305 | zDate[nDate-3] = 'z'; |
| 306 | zDate[nDate-2] = 0; |
| 307 | } |
| 308 | rid = db_int(0, |
| 309 | "SELECT event.objid, max(event.mtime)" |
| 310 | " FROM tag, tagxref, event" |
| 311 | " WHERE tag.tagname='sym-%q' " |
| 312 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 313 | " AND event.objid=tagxref.rid " |
| 314 | " AND event.mtime<=julianday(%Q)" |
| 315 | " AND event.type GLOB '%q'", |
| 316 | zTagBase, zDate, zType |
| 317 | ); |
| 318 | return rid; |
| 319 | } |
| 320 | |
| 321 | /* Remove optional [...] */ |
| 322 | zXTag = zTag; |
| @@ -381,11 +421,11 @@ | |
| 381 | if( zDate ){ |
| 382 | rid = db_int(0, |
| 383 | "SELECT objid FROM event" |
| 384 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 385 | " ORDER BY mtime DESC LIMIT 1", |
| 386 | zDate, zType); |
| 387 | if( rid) return rid; |
| 388 | } |
| 389 | |
| 390 | |
| 391 | /* Undocumented: numeric tags get translated directly into the RID */ |
| 392 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -105,10 +105,43 @@ | |
| 105 | } |
| 106 | |
| 107 | /* It looks like this may be a date. Return it with punctuation added. */ |
| 108 | return zEDate; |
| 109 | } |
| 110 | |
| 111 | /* |
| 112 | ** The data-time string in the argument is going to be used as an |
| 113 | ** upper bound like this: mtime<=julianday(zDate,'localtime'). |
| 114 | ** But if the zDate parameter omits the fractional seconds or the |
| 115 | ** seconds, or the time, that might mess up the == part of the |
| 116 | ** comparison. So add in missing factional seconds or seconds or time. |
| 117 | ** |
| 118 | ** The returned string is held in a static buffer that is overwritten |
| 119 | ** with each call, or else is just a copy of its input if there are |
| 120 | ** no changes. |
| 121 | */ |
| 122 | const char *fossil_roundup_date(const char *zDate){ |
| 123 | static char zUp[24]; |
| 124 | int n = (int)strlen(zDate); |
| 125 | if( n==19 ){ /* YYYY-MM-DD HH:MM:SS */ |
| 126 | memcpy(zUp, zDate, 19); |
| 127 | memcpy(zUp+19, ".999", 5); |
| 128 | return zUp; |
| 129 | } |
| 130 | if( n==16 ){ /* YYYY-MM-DD HH:MM */ |
| 131 | memcpy(zUp, zDate, 16); |
| 132 | memcpy(zUp+16, ":59.999", 8); |
| 133 | return zUp; |
| 134 | } |
| 135 | if( n==10 ){ /* YYYY-MM-DD */ |
| 136 | memcpy(zUp, zDate, 10); |
| 137 | memcpy(zUp+10, " 23:59:59.999", 14); |
| 138 | return zUp; |
| 139 | } |
| 140 | return zDate; |
| 141 | } |
| 142 | |
| 143 | |
| 144 | /* |
| 145 | ** Return the RID that is the "root" of the branch that contains |
| 146 | ** check-in "rid". Details depending on eType: |
| 147 | ** |
| @@ -235,19 +268,19 @@ | |
| 268 | if( zDate==0 ) zDate = &zTag[5]; |
| 269 | rid = db_int(0, |
| 270 | "SELECT objid FROM event" |
| 271 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 272 | " ORDER BY mtime DESC LIMIT 1", |
| 273 | fossil_roundup_date(zDate), zType); |
| 274 | return rid; |
| 275 | } |
| 276 | if( fossil_isdate(zTag) ){ |
| 277 | rid = db_int(0, |
| 278 | "SELECT objid FROM event" |
| 279 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 280 | " ORDER BY mtime DESC LIMIT 1", |
| 281 | fossil_roundup_date(zTag), zType); |
| 282 | if( rid) return rid; |
| 283 | } |
| 284 | |
| 285 | /* Deprecated date & time formats: "local:" + date-time and |
| 286 | ** "utc:" + date-time */ |
| @@ -262,11 +295,11 @@ | |
| 295 | if( memcmp(zTag, "utc:", 4)==0 ){ |
| 296 | rid = db_int(0, |
| 297 | "SELECT objid FROM event" |
| 298 | " WHERE mtime<=julianday('%qz') AND type GLOB '%q'" |
| 299 | " ORDER BY mtime DESC LIMIT 1", |
| 300 | fossil_roundup_date(&zTag[4]), zType); |
| 301 | return rid; |
| 302 | } |
| 303 | |
| 304 | /* "tag:" + symbolic-name */ |
| 305 | if( memcmp(zTag, "tag:", 4)==0 ){ |
| @@ -294,29 +327,36 @@ | |
| 327 | return start_of_branch(rid, 2); |
| 328 | } |
| 329 | |
| 330 | /* symbolic-name ":" date-time */ |
| 331 | nTag = strlen(zTag); |
| 332 | for(i=0; i<nTag-8 && zTag[i]!=':'; i++){} |
| 333 | if( zTag[i]==':' |
| 334 | && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0)!=0) |
| 335 | ){ |
| 336 | char *zDate = mprintf("%s", &zTag[i+1]); |
| 337 | char *zTagBase = mprintf("%.*s", i, zTag); |
| 338 | char *zXDate; |
| 339 | int nDate = strlen(zDate); |
| 340 | if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ |
| 341 | zDate[nDate-3] = 'z'; |
| 342 | zDate[nDate-2] = 0; |
| 343 | } |
| 344 | zXDate = fossil_expand_datetime(zDate,0); |
| 345 | if( zXDate==0 ) zXDate = zDate; |
| 346 | rid = db_int(0, |
| 347 | "SELECT event.objid, max(event.mtime)" |
| 348 | " FROM tag, tagxref, event" |
| 349 | " WHERE tag.tagname='sym-%q' " |
| 350 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 351 | " AND event.objid=tagxref.rid " |
| 352 | " AND event.mtime<=julianday(%Q,fromLocal())" |
| 353 | " AND event.type GLOB '%q'", |
| 354 | zTagBase, fossil_roundup_date(zXDate), zType |
| 355 | ); |
| 356 | fossil_free(zDate); |
| 357 | fossil_free(zTagBase); |
| 358 | return rid; |
| 359 | } |
| 360 | |
| 361 | /* Remove optional [...] */ |
| 362 | zXTag = zTag; |
| @@ -381,11 +421,11 @@ | |
| 421 | if( zDate ){ |
| 422 | rid = db_int(0, |
| 423 | "SELECT objid FROM event" |
| 424 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 425 | " ORDER BY mtime DESC LIMIT 1", |
| 426 | fossil_roundup_date(zDate), zType); |
| 427 | if( rid) return rid; |
| 428 | } |
| 429 | |
| 430 | |
| 431 | /* Undocumented: numeric tags get translated directly into the RID */ |
| 432 |
+7
-5
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -1113,15 +1113,17 @@ | ||
| 1113 | 1113 | if( fossil_isdate(z) ){ |
| 1114 | 1114 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z); |
| 1115 | 1115 | if( mtime>0.0 ) return mtime; |
| 1116 | 1116 | } |
| 1117 | 1117 | zDate = fossil_expand_datetime(z, 1); |
| 1118 | - if( zDate!=0 | |
| 1119 | - && (mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", zDate))>0.0 | |
| 1120 | - ){ | |
| 1121 | - if( pzDisplay ) *pzDisplay = fossil_strdup(zDate); | |
| 1122 | - return mtime; | |
| 1118 | + if( zDate!=0 ){ | |
| 1119 | + mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", | |
| 1120 | + fossil_roundup_date(zDate)); | |
| 1121 | + if( mtime>0.0 ){ | |
| 1122 | + if( pzDisplay ) *pzDisplay = fossil_strdup(zDate); | |
| 1123 | + return mtime; | |
| 1124 | + } | |
| 1123 | 1125 | } |
| 1124 | 1126 | rid = symbolic_name_to_rid(z, "*"); |
| 1125 | 1127 | if( rid ){ |
| 1126 | 1128 | mtime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 1127 | 1129 | }else{ |
| 1128 | 1130 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1113,15 +1113,17 @@ | |
| 1113 | if( fossil_isdate(z) ){ |
| 1114 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z); |
| 1115 | if( mtime>0.0 ) return mtime; |
| 1116 | } |
| 1117 | zDate = fossil_expand_datetime(z, 1); |
| 1118 | if( zDate!=0 |
| 1119 | && (mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", zDate))>0.0 |
| 1120 | ){ |
| 1121 | if( pzDisplay ) *pzDisplay = fossil_strdup(zDate); |
| 1122 | return mtime; |
| 1123 | } |
| 1124 | rid = symbolic_name_to_rid(z, "*"); |
| 1125 | if( rid ){ |
| 1126 | mtime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 1127 | }else{ |
| 1128 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1113,15 +1113,17 @@ | |
| 1113 | if( fossil_isdate(z) ){ |
| 1114 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z); |
| 1115 | if( mtime>0.0 ) return mtime; |
| 1116 | } |
| 1117 | zDate = fossil_expand_datetime(z, 1); |
| 1118 | if( zDate!=0 ){ |
| 1119 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", |
| 1120 | fossil_roundup_date(zDate)); |
| 1121 | if( mtime>0.0 ){ |
| 1122 | if( pzDisplay ) *pzDisplay = fossil_strdup(zDate); |
| 1123 | return mtime; |
| 1124 | } |
| 1125 | } |
| 1126 | rid = symbolic_name_to_rid(z, "*"); |
| 1127 | if( rid ){ |
| 1128 | mtime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 1129 | }else{ |
| 1130 |