1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-05 15:55:57 +03:00

Simplify the date/time function logic for improved rebustness and also to

decrease the size of the binary.

FossilOrigin-Name: 6e144735ed0cd3d4461ae6a4d8034264563e3165
This commit is contained in:
drh
2016-12-02 19:07:03 +00:00
parent 861a56821b
commit 6d4e9c3d92
3 changed files with 85 additions and 64 deletions

View File

@@ -414,10 +414,8 @@ static void computeYMD(DateTime *p){
p->Y = 2000;
p->M = 1;
p->D = 1;
}else if( !validJulianDay(p->iJD) ){
datetimeError(p);
return;
}else{
assert( validJulianDay(p->iJD) );
Z = (int)((p->iJD + 43200000)/86400000);
A = (int)((Z - 1867216.25)/36524.25);
A = Z + 1 + A - (A/4);
@@ -597,6 +595,29 @@ static sqlite3_int64 localtimeOffset(
}
#endif /* SQLITE_OMIT_LOCALTIME */
/*
** The following table defines various date transformations of the form
**
** 'NNN days'
**
** Where NNN is an arbitrary floating-point number and "days" can be one
** of several units of time.
*/
static const struct {
u8 eType; /* Transformation type code */
u8 nName; /* Length of th name */
char *zName; /* Name of the transformation */
double rLimit; /* Maximum NNN value for this transform */
double rXform; /* Constant used for this transform */
} aXformType[] = {
{ 0, 6, "second", 464269060800.0, 86400000.0/(24.0*60.0*60.0) },
{ 0, 6, "minute", 7737817680.0, 86400000.0/(24.0*60.0) },
{ 0, 4, "hour", 128963628.0, 86400000.0/24.0 },
{ 0, 3, "day", 5373485.0, 86400000.0 },
{ 1, 5, "month", 176546.0, 30.0*86400000.0 },
{ 2, 4, "year", 14713.0, 365.0*86400000.0 },
};
/*
** Process a modifier to a date-time stamp. The modifiers are
** as follows:
@@ -621,17 +642,15 @@ static sqlite3_int64 localtimeOffset(
** to context pCtx. If the error is an unrecognized modifier, no error is
** written to pCtx.
*/
static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
static int parseModifier(
sqlite3_context *pCtx, /* Function context */
const char *z, /* The text of the modifier */
int n, /* Length of zMod in bytes */
DateTime *p /* The date/time value to be modified */
){
int rc = 1;
int n;
double r;
char *z, zBuf[30];
z = zBuf;
for(n=0; n<ArraySize(zBuf)-1 && zMod[n]; n++){
z[n] = (char)sqlite3UpperToLower[(u8)zMod[n]];
}
z[n] = 0;
switch( z[0] ){
switch(sqlite3UpperToLower[(u8)z[0]] ){
#ifndef SQLITE_OMIT_LOCALTIME
case 'l': {
/* localtime
@@ -639,7 +658,7 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
** Assuming the current time value is UTC (a.k.a. GMT), shift it to
** show local time.
*/
if( strcmp(z, "localtime")==0 ){
if( sqlite3_stricmp(z, "localtime")==0 ){
computeJD(p);
p->iJD += localtimeOffset(p, pCtx, &rc);
clearYMD_HMS_TZ(p);
@@ -654,7 +673,7 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
** Treat the current value of p->s as the number of
** seconds since 1970. Convert to a real julian day number.
*/
if( strcmp(z, "unixepoch")==0 && p->rawS ){
if( sqlite3_stricmp(z, "unixepoch")==0 && p->rawS ){
double r = p->s*1000.0 + 210866760000000.0;
if( r>=0.0 && r<464269060800000.0 ){
clearYMD_HMS_TZ(p);
@@ -665,7 +684,7 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
}
}
#ifndef SQLITE_OMIT_LOCALTIME
else if( strcmp(z, "utc")==0 ){
else if( sqlite3_stricmp(z, "utc")==0 ){
if( p->tzSet==0 ){
sqlite3_int64 c1;
computeJD(p);
@@ -691,7 +710,7 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
** weekday N where 0==Sunday, 1==Monday, and so forth. If the
** date is already on the appropriate weekday, this is a no-op.
*/
if( strncmp(z, "weekday ", 8)==0
if( sqlite3_strnicmp(z, "weekday ", 8)==0
&& sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8)
&& (n=(int)r)==r && n>=0 && r<7 ){
sqlite3_int64 Z;
@@ -714,7 +733,7 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
** Move the date backwards to the beginning of the current day,
** or month or year.
*/
if( strncmp(z, "start of ", 9)!=0 ) break;
if( sqlite3_strnicmp(z, "start of ", 9)!=0 ) break;
z += 9;
computeYMD(p);
p->validHMS = 1;
@@ -722,15 +741,15 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
p->s = 0.0;
p->validTZ = 0;
p->validJD = 0;
if( strcmp(z,"month")==0 ){
if( sqlite3_stricmp(z,"month")==0 ){
p->D = 1;
rc = 0;
}else if( strcmp(z,"year")==0 ){
}else if( sqlite3_stricmp(z,"year")==0 ){
computeYMD(p);
p->M = 1;
p->D = 1;
rc = 0;
}else if( strcmp(z,"day")==0 ){
}else if( sqlite3_stricmp(z,"day")==0 ){
rc = 0;
}
break;
@@ -748,7 +767,7 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
case '8':
case '9': {
double rRounder;
double rAbs;
int i;
for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){}
if( !sqlite3AtoF(z, &r, n, SQLITE_UTF8) ){
rc = 1;
@@ -777,47 +796,48 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
rc = 0;
break;
}
/* If control reaches this point, it means the transformation is
** one of the forms like "+NNN days". */
z += n;
while( sqlite3Isspace(*z) ) z++;
n = sqlite3Strlen30(z);
if( n>10 || n<3 ) break;
if( z[n-1]=='s' ){ z[n-1] = 0; n--; }
if( sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--;
computeJD(p);
rc = 0;
rc = 1;
rRounder = r<0 ? -0.5 : +0.5;
rAbs = r<0 ? -r : r;
if( n==3 && strcmp(z,"day")==0 && rAbs<5373485.0 ){
p->iJD += (sqlite3_int64)(r*86400000.0 + rRounder);
}else if( n==4 && strcmp(z,"hour")==0 && rAbs<128963628.0 ){
p->iJD += (sqlite3_int64)(r*(86400000.0/24.0) + rRounder);
}else if( n==6 && strcmp(z,"minute")==0 && rAbs<7737817680.0 ){
p->iJD += (sqlite3_int64)(r*(86400000.0/(24.0*60.0)) + rRounder);
}else if( n==6 && strcmp(z,"second")==0 && rAbs<464269060800.0 ){
p->iJD += (sqlite3_int64)(r*(86400000.0/(24.0*60.0*60.0)) + rRounder);
}else if( n==5 && strcmp(z,"month")==0 && rAbs<176546.0 ){
int x, y;
computeYMD_HMS(p);
p->M += (int)r;
x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12;
p->Y += x;
p->M -= x*12;
p->validJD = 0;
computeJD(p);
y = (int)r;
if( y!=r ){
p->iJD += (sqlite3_int64)((r - y)*30.0*86400000.0 + rRounder);
for(i=0; i<ArraySize(aXformType); i++){
if( aXformType[i].nName==n
&& sqlite3_strnicmp(aXformType[i].zName, z, n)==0
&& r>-aXformType[i].rLimit && r<aXformType[i].rLimit
){
switch( aXformType[i].eType ){
case 1: { /* Special processing to add months */
int x;
computeYMD_HMS(p);
p->M += (int)r;
x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12;
p->Y += x;
p->M -= x*12;
p->validJD = 0;
r -= (int)r;
break;
}
case 2: { /* Special processing to add years */
int y = (int)r;
computeYMD_HMS(p);
p->Y += y;
p->validJD = 0;
r -= (int)r;
break;
}
}
computeJD(p);
p->iJD += (sqlite3_int64)(r*aXformType[i].rXform + rRounder);
rc = 0;
break;
}
}else if( n==4 && strcmp(z,"year")==0 && rAbs<14713.0 ){
int y = (int)r;
computeYMD_HMS(p);
p->Y += y;
p->validJD = 0;
computeJD(p);
if( y!=r ){
p->iJD += (sqlite3_int64)((r - y)*365.0*86400000.0 + rRounder);
}
}else{
rc = 1;
}
clearYMD_HMS_TZ(p);
break;
@@ -844,7 +864,7 @@ static int isDate(
sqlite3_value **argv,
DateTime *p
){
int i;
int i, n;
const unsigned char *z;
int eType;
memset(p, 0, sizeof(*p));
@@ -862,7 +882,8 @@ static int isDate(
}
for(i=1; i<argc; i++){
z = sqlite3_value_text(argv[i]);
if( z==0 || parseModifier(context, (char*)z, p) ) return 1;
n = sqlite3_value_bytes(argv[i]);
if( z==0 || parseModifier(context, (char*)z, n, p) ) return 1;
}
computeJD(p);
if( p->isError || !validJulianDay(p->iJD) ) return 1;