Fossil SCM
Accept pure numeric ISO8601 date/time strings (without punctuation) if there are not conflicts with other identifiers.
Commit
437f39b87c3340cb50b91cacfc6186fe037694013a85ad40c516a7f307490e22
Parent
1aab3f31781ddb2…
2 files changed
+83
-1
+7
+83
-1
| --- src/name.c | ||
| +++ src/name.c | ||
| @@ -38,10 +38,77 @@ | ||
| 38 | 38 | if( z[7]!='-') return 0; |
| 39 | 39 | if( !fossil_isdigit(z[8]) ) return 0; |
| 40 | 40 | if( !fossil_isdigit(z[9]) ) return 0; |
| 41 | 41 | return 1; |
| 42 | 42 | } |
| 43 | + | |
| 44 | +/* | |
| 45 | +** Check to see if the string might be a compact date/time that omits | |
| 46 | +** the punctuation. Example: "20190327084549" instead of | |
| 47 | +** "2019-03-27 08:45:49". If the string is of the appropriate form, | |
| 48 | +** then return an alternative string (in static space) that is the same | |
| 49 | +** string with punctuation inserted. | |
| 50 | +** | |
| 51 | +** If the bVerifyNotAHash flag is true, then a check is made to see if | |
| 52 | +** the string is a hash prefix and NULL is returned if it is. If the | |
| 53 | +** bVerifyNotAHash flag is false, then the result is determined by syntax | |
| 54 | +** of the input string only, without reference to the artifact table. | |
| 55 | +*/ | |
| 56 | +char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){ | |
| 57 | + static char zEDate[20]; | |
| 58 | + static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' }; | |
| 59 | + int n = (int)strlen(zIn); | |
| 60 | + int i, j; | |
| 61 | + | |
| 62 | + /* Only three forms allowed: | |
| 63 | + ** (1) YYYYMMDD | |
| 64 | + ** (2) YYYYMMDDHHMM | |
| 65 | + ** (3) YYYYMMDDHHMMSS | |
| 66 | + */ | |
| 67 | + if( n!=8 && n!=12 && n!=14 ) return 0; | |
| 68 | + | |
| 69 | + /* Every character must be a digit */ | |
| 70 | + for(i=0; fossil_isdigit(zIn[i]); i++){} | |
| 71 | + if( i!=n ) return 0; | |
| 72 | + | |
| 73 | + /* Expand the date */ | |
| 74 | + for(i=j=0; zIn[i]; i++){ | |
| 75 | + if( i>=4 && (i%2)==0 ){ | |
| 76 | + zEDate[j++] = aPunct[i/2]; | |
| 77 | + } | |
| 78 | + zEDate[j++] = zIn[i]; | |
| 79 | + } | |
| 80 | + zEDate[j] = 0; | |
| 81 | + | |
| 82 | + /* Check for reasonable date values. | |
| 83 | + ** Offset references: | |
| 84 | + ** YYYY-MM-DD HH:MM:SS | |
| 85 | + ** 0123456789 12345678 | |
| 86 | + */ | |
| 87 | + | |
| 88 | + i = atoi(zEDate); | |
| 89 | + if( i<1970 || i>2100 ) return 0; | |
| 90 | + i = atoi(zEDate+5); | |
| 91 | + if( i<1 || i>12 ) return 0; | |
| 92 | + i = atoi(zEDate+8); | |
| 93 | + if( i<1 || i>31 ) return 0; | |
| 94 | + if( n>8 ){ | |
| 95 | + i = atoi(zEDate+11); | |
| 96 | + if( i>24 ) return 0; | |
| 97 | + i = atoi(zEDate+14); | |
| 98 | + if( i>60 ) return 0; | |
| 99 | + if( n==14 && atoi(zEDate+17)>60 ) return 0; | |
| 100 | + } | |
| 101 | + | |
| 102 | + /* The string is not also a hash prefix */ | |
| 103 | + if( bVerifyNotAHash ){ | |
| 104 | + if( db_exists("SELECT 1 FROM blob WHERE uuid GLOB '%q*'",zIn) ) return 0; | |
| 105 | + } | |
| 106 | + | |
| 107 | + /* It looks like this may be a date. Return it with punctuation added. */ | |
| 108 | + return zEDate; | |
| 109 | +} | |
| 43 | 110 | |
| 44 | 111 | /* |
| 45 | 112 | ** Return the RID that is the "root" of the branch that contains |
| 46 | 113 | ** check-in "rid" if inBranch==0 or the first check-in in the branch |
| 47 | 114 | ** if inBranch==1. |
| @@ -114,10 +181,11 @@ | ||
| 114 | 181 | int nTag; |
| 115 | 182 | int i; |
| 116 | 183 | int startOfBranch = 0; |
| 117 | 184 | const char *zXTag; /* zTag with optional [...] removed */ |
| 118 | 185 | int nXTag; /* Size of zXTag */ |
| 186 | + const char *zDate; /* Expanded date-time string */ | |
| 119 | 187 | |
| 120 | 188 | if( zType==0 || zType[0]==0 ){ |
| 121 | 189 | zType = "*"; |
| 122 | 190 | }else if( zType[0]=='b' ){ |
| 123 | 191 | zType = "ci"; |
| @@ -150,15 +218,17 @@ | ||
| 150 | 218 | if( rid ) return rid; |
| 151 | 219 | } |
| 152 | 220 | |
| 153 | 221 | /* Date and times */ |
| 154 | 222 | if( memcmp(zTag, "date:", 5)==0 ){ |
| 223 | + zDate = fossil_expand_datetime(&zTag[5],0); | |
| 224 | + if( zDate==0 ) zDate = &zTag[5]; | |
| 155 | 225 | rid = db_int(0, |
| 156 | 226 | "SELECT objid FROM event" |
| 157 | 227 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 158 | 228 | " ORDER BY mtime DESC LIMIT 1", |
| 159 | - &zTag[5], zType); | |
| 229 | + zDate, zType); | |
| 160 | 230 | return rid; |
| 161 | 231 | } |
| 162 | 232 | if( fossil_isdate(zTag) ){ |
| 163 | 233 | rid = db_int(0, |
| 164 | 234 | "SELECT objid FROM event" |
| @@ -283,10 +353,22 @@ | ||
| 283 | 353 | ); |
| 284 | 354 | if( rid>0 ){ |
| 285 | 355 | if( startOfBranch ) rid = start_of_branch(rid,1); |
| 286 | 356 | return rid; |
| 287 | 357 | } |
| 358 | + | |
| 359 | + /* Pure numeric date/time */ | |
| 360 | + zDate = fossil_expand_datetime(zTag, 0); | |
| 361 | + if( zDate ){ | |
| 362 | + rid = db_int(0, | |
| 363 | + "SELECT objid FROM event" | |
| 364 | + " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" | |
| 365 | + " ORDER BY mtime DESC LIMIT 1", | |
| 366 | + zDate, zType); | |
| 367 | + if( rid) return rid; | |
| 368 | + } | |
| 369 | + | |
| 288 | 370 | |
| 289 | 371 | /* Undocumented: numeric tags get translated directly into the RID */ |
| 290 | 372 | if( memcmp(zTag, "rid:", 4)==0 ){ |
| 291 | 373 | zTag += 4; |
| 292 | 374 | for(i=0; fossil_isdigit(zTag[i]); i++){} |
| 293 | 375 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -38,10 +38,77 @@ | |
| 38 | if( z[7]!='-') return 0; |
| 39 | if( !fossil_isdigit(z[8]) ) return 0; |
| 40 | if( !fossil_isdigit(z[9]) ) return 0; |
| 41 | return 1; |
| 42 | } |
| 43 | |
| 44 | /* |
| 45 | ** Return the RID that is the "root" of the branch that contains |
| 46 | ** check-in "rid" if inBranch==0 or the first check-in in the branch |
| 47 | ** if inBranch==1. |
| @@ -114,10 +181,11 @@ | |
| 114 | int nTag; |
| 115 | int i; |
| 116 | int startOfBranch = 0; |
| 117 | const char *zXTag; /* zTag with optional [...] removed */ |
| 118 | int nXTag; /* Size of zXTag */ |
| 119 | |
| 120 | if( zType==0 || zType[0]==0 ){ |
| 121 | zType = "*"; |
| 122 | }else if( zType[0]=='b' ){ |
| 123 | zType = "ci"; |
| @@ -150,15 +218,17 @@ | |
| 150 | if( rid ) return rid; |
| 151 | } |
| 152 | |
| 153 | /* Date and times */ |
| 154 | if( memcmp(zTag, "date:", 5)==0 ){ |
| 155 | rid = db_int(0, |
| 156 | "SELECT objid FROM event" |
| 157 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 158 | " ORDER BY mtime DESC LIMIT 1", |
| 159 | &zTag[5], zType); |
| 160 | return rid; |
| 161 | } |
| 162 | if( fossil_isdate(zTag) ){ |
| 163 | rid = db_int(0, |
| 164 | "SELECT objid FROM event" |
| @@ -283,10 +353,22 @@ | |
| 283 | ); |
| 284 | if( rid>0 ){ |
| 285 | if( startOfBranch ) rid = start_of_branch(rid,1); |
| 286 | return rid; |
| 287 | } |
| 288 | |
| 289 | /* Undocumented: numeric tags get translated directly into the RID */ |
| 290 | if( memcmp(zTag, "rid:", 4)==0 ){ |
| 291 | zTag += 4; |
| 292 | for(i=0; fossil_isdigit(zTag[i]); i++){} |
| 293 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -38,10 +38,77 @@ | |
| 38 | if( z[7]!='-') return 0; |
| 39 | if( !fossil_isdigit(z[8]) ) return 0; |
| 40 | if( !fossil_isdigit(z[9]) ) return 0; |
| 41 | return 1; |
| 42 | } |
| 43 | |
| 44 | /* |
| 45 | ** Check to see if the string might be a compact date/time that omits |
| 46 | ** the punctuation. Example: "20190327084549" instead of |
| 47 | ** "2019-03-27 08:45:49". If the string is of the appropriate form, |
| 48 | ** then return an alternative string (in static space) that is the same |
| 49 | ** string with punctuation inserted. |
| 50 | ** |
| 51 | ** If the bVerifyNotAHash flag is true, then a check is made to see if |
| 52 | ** the string is a hash prefix and NULL is returned if it is. If the |
| 53 | ** bVerifyNotAHash flag is false, then the result is determined by syntax |
| 54 | ** of the input string only, without reference to the artifact table. |
| 55 | */ |
| 56 | char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){ |
| 57 | static char zEDate[20]; |
| 58 | static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' }; |
| 59 | int n = (int)strlen(zIn); |
| 60 | int i, j; |
| 61 | |
| 62 | /* Only three forms allowed: |
| 63 | ** (1) YYYYMMDD |
| 64 | ** (2) YYYYMMDDHHMM |
| 65 | ** (3) YYYYMMDDHHMMSS |
| 66 | */ |
| 67 | if( n!=8 && n!=12 && n!=14 ) return 0; |
| 68 | |
| 69 | /* Every character must be a digit */ |
| 70 | for(i=0; fossil_isdigit(zIn[i]); i++){} |
| 71 | if( i!=n ) return 0; |
| 72 | |
| 73 | /* Expand the date */ |
| 74 | for(i=j=0; zIn[i]; i++){ |
| 75 | if( i>=4 && (i%2)==0 ){ |
| 76 | zEDate[j++] = aPunct[i/2]; |
| 77 | } |
| 78 | zEDate[j++] = zIn[i]; |
| 79 | } |
| 80 | zEDate[j] = 0; |
| 81 | |
| 82 | /* Check for reasonable date values. |
| 83 | ** Offset references: |
| 84 | ** YYYY-MM-DD HH:MM:SS |
| 85 | ** 0123456789 12345678 |
| 86 | */ |
| 87 | |
| 88 | i = atoi(zEDate); |
| 89 | if( i<1970 || i>2100 ) return 0; |
| 90 | i = atoi(zEDate+5); |
| 91 | if( i<1 || i>12 ) return 0; |
| 92 | i = atoi(zEDate+8); |
| 93 | if( i<1 || i>31 ) return 0; |
| 94 | if( n>8 ){ |
| 95 | i = atoi(zEDate+11); |
| 96 | if( i>24 ) return 0; |
| 97 | i = atoi(zEDate+14); |
| 98 | if( i>60 ) return 0; |
| 99 | if( n==14 && atoi(zEDate+17)>60 ) return 0; |
| 100 | } |
| 101 | |
| 102 | /* The string is not also a hash prefix */ |
| 103 | if( bVerifyNotAHash ){ |
| 104 | if( db_exists("SELECT 1 FROM blob WHERE uuid GLOB '%q*'",zIn) ) return 0; |
| 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" if inBranch==0 or the first check-in in the branch |
| 114 | ** if inBranch==1. |
| @@ -114,10 +181,11 @@ | |
| 181 | int nTag; |
| 182 | int i; |
| 183 | int startOfBranch = 0; |
| 184 | const char *zXTag; /* zTag with optional [...] removed */ |
| 185 | int nXTag; /* Size of zXTag */ |
| 186 | const char *zDate; /* Expanded date-time string */ |
| 187 | |
| 188 | if( zType==0 || zType[0]==0 ){ |
| 189 | zType = "*"; |
| 190 | }else if( zType[0]=='b' ){ |
| 191 | zType = "ci"; |
| @@ -150,15 +218,17 @@ | |
| 218 | if( rid ) return rid; |
| 219 | } |
| 220 | |
| 221 | /* Date and times */ |
| 222 | if( memcmp(zTag, "date:", 5)==0 ){ |
| 223 | zDate = fossil_expand_datetime(&zTag[5],0); |
| 224 | if( zDate==0 ) zDate = &zTag[5]; |
| 225 | rid = db_int(0, |
| 226 | "SELECT objid FROM event" |
| 227 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 228 | " ORDER BY mtime DESC LIMIT 1", |
| 229 | zDate, zType); |
| 230 | return rid; |
| 231 | } |
| 232 | if( fossil_isdate(zTag) ){ |
| 233 | rid = db_int(0, |
| 234 | "SELECT objid FROM event" |
| @@ -283,10 +353,22 @@ | |
| 353 | ); |
| 354 | if( rid>0 ){ |
| 355 | if( startOfBranch ) rid = start_of_branch(rid,1); |
| 356 | return rid; |
| 357 | } |
| 358 | |
| 359 | /* Pure numeric date/time */ |
| 360 | zDate = fossil_expand_datetime(zTag, 0); |
| 361 | if( zDate ){ |
| 362 | rid = db_int(0, |
| 363 | "SELECT objid FROM event" |
| 364 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 365 | " ORDER BY mtime DESC LIMIT 1", |
| 366 | zDate, zType); |
| 367 | if( rid) return rid; |
| 368 | } |
| 369 | |
| 370 | |
| 371 | /* Undocumented: numeric tags get translated directly into the RID */ |
| 372 | if( memcmp(zTag, "rid:", 4)==0 ){ |
| 373 | zTag += 4; |
| 374 | for(i=0; fossil_isdigit(zTag[i]); i++){} |
| 375 |
+7
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -1027,14 +1027,21 @@ | ||
| 1027 | 1027 | ** query parameters of timeline into a julianday mtime value. |
| 1028 | 1028 | */ |
| 1029 | 1029 | double symbolic_name_to_mtime(const char *z){ |
| 1030 | 1030 | double mtime; |
| 1031 | 1031 | int rid; |
| 1032 | + const char *zDate; | |
| 1032 | 1033 | if( z==0 ) return -1.0; |
| 1033 | 1034 | if( fossil_isdate(z) ){ |
| 1034 | 1035 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z); |
| 1035 | 1036 | if( mtime>0.0 ) return mtime; |
| 1037 | + } | |
| 1038 | + zDate = fossil_expand_datetime(z, 1); | |
| 1039 | + if( zDate!=0 | |
| 1040 | + && (mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", zDate))>0.0 | |
| 1041 | + ){ | |
| 1042 | + return mtime; | |
| 1036 | 1043 | } |
| 1037 | 1044 | rid = symbolic_name_to_rid(z, "*"); |
| 1038 | 1045 | if( rid ){ |
| 1039 | 1046 | mtime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 1040 | 1047 | }else{ |
| 1041 | 1048 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1027,14 +1027,21 @@ | |
| 1027 | ** query parameters of timeline into a julianday mtime value. |
| 1028 | */ |
| 1029 | double symbolic_name_to_mtime(const char *z){ |
| 1030 | double mtime; |
| 1031 | int rid; |
| 1032 | if( z==0 ) return -1.0; |
| 1033 | if( fossil_isdate(z) ){ |
| 1034 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z); |
| 1035 | if( mtime>0.0 ) return mtime; |
| 1036 | } |
| 1037 | rid = symbolic_name_to_rid(z, "*"); |
| 1038 | if( rid ){ |
| 1039 | mtime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 1040 | }else{ |
| 1041 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1027,14 +1027,21 @@ | |
| 1027 | ** query parameters of timeline into a julianday mtime value. |
| 1028 | */ |
| 1029 | double symbolic_name_to_mtime(const char *z){ |
| 1030 | double mtime; |
| 1031 | int rid; |
| 1032 | const char *zDate; |
| 1033 | if( z==0 ) return -1.0; |
| 1034 | if( fossil_isdate(z) ){ |
| 1035 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z); |
| 1036 | if( mtime>0.0 ) return mtime; |
| 1037 | } |
| 1038 | zDate = fossil_expand_datetime(z, 1); |
| 1039 | if( zDate!=0 |
| 1040 | && (mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", zDate))>0.0 |
| 1041 | ){ |
| 1042 | return mtime; |
| 1043 | } |
| 1044 | rid = symbolic_name_to_rid(z, "*"); |
| 1045 | if( rid ){ |
| 1046 | mtime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 1047 | }else{ |
| 1048 |