Adjust date/time functions so that they do a better job of keeping track
of whether the current time is UTC or localtime, and no-op the 'utc' and 'localtime' modifiers accordingly. See [forum:/info/e7a939e074|forum post e7a939e074]. Also add the datedebug() function, available only under -DSQLITE_DEBUG, for improved visibility of the DateTime object during debugging and testing. FossilOrigin-Name: dc569683748354a6db83438904422e802d3ea780775c48da85b474fff03ca8a1
This commit is contained in:
parent
8d25d07d50
commit
3f273db39b
16
manifest
16
manifest
@ -1,5 +1,5 @@
|
||||
C Fix\sassert()\sstatements\sin\sdate/time\scomputations:\sThe\smonth\sand\sday\snumbers\ncan\sbe\szero\sif\san\serror\shas\sbeen\sseen.
|
||||
D 2024-03-04T11:12:15.305
|
||||
C Adjust\sdate/time\sfunctions\sso\sthat\sthey\sdo\sa\sbetter\sjob\sof\skeeping\strack\nof\swhether\sthe\scurrent\stime\sis\sUTC\sor\slocaltime,\sand\sno-op\sthe\s'utc'\sand\n'localtime'\smodifiers\saccordingly.\sSee\n[forum:/info/e7a939e074|forum\spost\se7a939e074].\nAlso\sadd\sthe\sdatedebug()\sfunction,\savailable\nonly\sunder\s-DSQLITE_DEBUG,\sfor\simproved\svisibility\sof\sthe\sDateTime\sobject\nduring\sdebugging\sand\stesting.
|
||||
D 2024-03-04T13:58:09.237
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@ -696,7 +696,7 @@ F src/build.c 04f1bcee189f045ab086d84fee95db42cb49df82ff8e84af8136309ff3c8a75f
|
||||
F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
F src/ctime.c 23331529e654be40ca97d171cbbffe9b3d4c71cc53b78fe5501230675952da8b
|
||||
F src/date.c 29cecfe69287242a12f753ffc883e4231afde3af501fb1b4a7b721b159fc4bb2
|
||||
F src/date.c a9e4382961fb26156a308645f7363519ab7eb20e412e78b7c1fe16bbfbb1689a
|
||||
F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782
|
||||
F src/dbstat.c 3b677254d512fcafd4d0b341bf267b38b235ccfddbef24f9154e19360fa22e43
|
||||
F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500
|
||||
@ -1042,7 +1042,7 @@ F test/ctime.test 340f362f41f92972bbd71f44e10569a5cc694062b692231bd08aa6fe6c1c47
|
||||
F test/cursorhint.test 05cf0febe5c5f8a31f199401fd1c9322249e753950d55f26f9d5aca61408a270
|
||||
F test/cursorhint2.test 6f3aa9cb19e7418967a10ec6905209bcbb5968054da855fc36c8beee9ae9c42f
|
||||
F test/dataversion1.test 6e5e86ac681f0782e766ebcb56c019ae001522d114e0e111e5ebf68ccf2a7bb8
|
||||
F test/date.test 99bfd3a77a3f9ae54eebd374a4301af960f2b2e9a581cf63e26445bae830a435
|
||||
F test/date.test c8ff835023f2107b57ce7a45c92265d51c98a23fc93231e998f12d850831aad6
|
||||
F test/date2.test 7e12ec14aaf4d5e6294b4ba140445b0eca06ea50062a9c3a69c4ee13d0b6f8b1
|
||||
F test/date3.test a1b77abf05c6772fe5ca2337cac1398892f2a41e62bce7e6be0f4a08a0e64ae5
|
||||
F test/date4.test 75dc8401e8c0639a228cd26a6eaa4ff5ea8ccda912b9853d1c9462c476670e17
|
||||
@ -1749,7 +1749,7 @@ F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0
|
||||
F test/tkt-b72787b1.test a95e8cdad0b98af1853ac7f0afd4ab27b77bf5f3
|
||||
F test/tkt-b75a9ca6b0.test dc6a853c242f7d0326564ae32e9e5eb462b5e8d2bc5b01ea3b18fd24f8e5894b
|
||||
F test/tkt-ba7cbfaedc.test b4c0deccc12aeb55cfdb57935b16b5d67c5a9877
|
||||
F test/tkt-bd484a090c.test 60460bf946f79a79712b71f202eda501ca99b898
|
||||
F test/tkt-bd484a090c.test e6af3e3a4242cd8f1c91c736364f09075d8e33e3b86f6492a1ee36278ea71b61
|
||||
F test/tkt-bdc6bbbb38.test fc38bb09bdd440e3513a1f5f98fc60a075182d7d
|
||||
F test/tkt-c48d99d690.test ba61977d62ab612fc515b3c488a6fbd6464a2447
|
||||
F test/tkt-c694113d5.test 82c461924ada5c14866c47e85535b0b0923ba16a2e907e370061a5ca77f65d77
|
||||
@ -2176,8 +2176,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P f6e887203365b30cea6e9c145366345e48256a347388577edf2bada65e0655b7
|
||||
R 68522a41082e7767e1ccbf426313c9cf
|
||||
P fc773f6c76ec114be8b6e25b13885acb5adcc9d052dca0d8d0ff94e2a0743d64
|
||||
R 0f693fc7ce53368753332e9082e39c9c
|
||||
U drh
|
||||
Z cb9173c28803c9d207f1634b7853f7aa
|
||||
Z b0035812b357d7d3f0f59f737bf36230
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
@ -1 +1 @@
|
||||
fc773f6c76ec114be8b6e25b13885acb5adcc9d052dca0d8d0ff94e2a0743d64
|
||||
dc569683748354a6db83438904422e802d3ea780775c48da85b474fff03ca8a1
|
78
src/date.c
78
src/date.c
@ -73,12 +73,12 @@ struct DateTime {
|
||||
char validJD; /* True (1) if iJD is valid */
|
||||
char validYMD; /* True (1) if Y,M,D are valid */
|
||||
char validHMS; /* True (1) if h,m,s are valid */
|
||||
char validTZ; /* True (1) if tz is valid */
|
||||
char nFloor; /* Days to implement "floor" */
|
||||
unsigned rawS : 1; /* Raw numeric value stored in s */
|
||||
unsigned tzSet : 1; /* Timezone was set explicitly */
|
||||
unsigned isError : 1; /* An overflow has occurred */
|
||||
unsigned useSubsec : 1; /* Display subsecond precision */
|
||||
unsigned isUtc : 1; /* Time is known to be UTC */
|
||||
unsigned isLocal : 1; /* Time is known to be localtime */
|
||||
};
|
||||
|
||||
|
||||
@ -176,6 +176,8 @@ static int parseTimezone(const char *zDate, DateTime *p){
|
||||
sgn = +1;
|
||||
}else if( c=='Z' || c=='z' ){
|
||||
zDate++;
|
||||
p->isLocal = 0;
|
||||
p->isUtc = 1;
|
||||
goto zulu_time;
|
||||
}else{
|
||||
return c!=0;
|
||||
@ -188,7 +190,6 @@ static int parseTimezone(const char *zDate, DateTime *p){
|
||||
p->tz = sgn*(nMn + nHr*60);
|
||||
zulu_time:
|
||||
while( sqlite3Isspace(*zDate) ){ zDate++; }
|
||||
p->tzSet = 1;
|
||||
return *zDate!=0;
|
||||
}
|
||||
|
||||
@ -232,7 +233,6 @@ static int parseHhMmSs(const char *zDate, DateTime *p){
|
||||
p->m = m;
|
||||
p->s = s + ms;
|
||||
if( parseTimezone(zDate, p) ) return 1;
|
||||
p->validTZ = (p->tz!=0)?1:0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -279,11 +279,13 @@ static void computeJD(DateTime *p){
|
||||
p->validJD = 1;
|
||||
if( p->validHMS ){
|
||||
p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000 + 0.5);
|
||||
if( p->validTZ ){
|
||||
if( p->tz ){
|
||||
p->iJD -= p->tz*60000;
|
||||
p->validYMD = 0;
|
||||
p->validHMS = 0;
|
||||
p->validTZ = 0;
|
||||
p->tz = 0;
|
||||
p->isUtc = 1;
|
||||
p->isLocal = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -350,11 +352,14 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
|
||||
p->M = M;
|
||||
p->D = D;
|
||||
computeFloor(p);
|
||||
if( p->validTZ ){
|
||||
if( p->tz ){
|
||||
computeJD(p);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void clearYMD_HMS_TZ(DateTime *p); /* Forward declaration */
|
||||
|
||||
/*
|
||||
** Set the time to the current time reported by the VFS.
|
||||
@ -365,6 +370,9 @@ static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){
|
||||
p->iJD = sqlite3StmtCurrentTime(context);
|
||||
if( p->iJD>0 ){
|
||||
p->validJD = 1;
|
||||
p->isUtc = 1;
|
||||
p->isLocal = 0;
|
||||
clearYMD_HMS_TZ(p);
|
||||
return 0;
|
||||
}else{
|
||||
return 1;
|
||||
@ -503,7 +511,7 @@ static void computeYMD_HMS(DateTime *p){
|
||||
static void clearYMD_HMS_TZ(DateTime *p){
|
||||
p->validYMD = 0;
|
||||
p->validHMS = 0;
|
||||
p->validTZ = 0;
|
||||
p->tz = 0;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_LOCALTIME
|
||||
@ -635,7 +643,7 @@ static int toLocaltime(
|
||||
p->validHMS = 1;
|
||||
p->validJD = 0;
|
||||
p->rawS = 0;
|
||||
p->validTZ = 0;
|
||||
p->tz = 0;
|
||||
p->isError = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -793,7 +801,9 @@ static int parseModifier(
|
||||
** show local time.
|
||||
*/
|
||||
if( sqlite3_stricmp(z, "localtime")==0 && sqlite3NotPureFunc(pCtx) ){
|
||||
rc = toLocaltime(p, pCtx);
|
||||
rc = p->isLocal ? SQLITE_OK : toLocaltime(p, pCtx);
|
||||
p->isUtc = 0;
|
||||
p->isLocal = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -818,7 +828,7 @@ static int parseModifier(
|
||||
}
|
||||
#ifndef SQLITE_OMIT_LOCALTIME
|
||||
else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){
|
||||
if( p->tzSet==0 ){
|
||||
if( p->isUtc==0 ){
|
||||
i64 iOrigJD; /* Original localtime */
|
||||
i64 iGuess; /* Guess at the corresponding utc time */
|
||||
int cnt = 0; /* Safety to prevent infinite loop */
|
||||
@ -841,7 +851,8 @@ static int parseModifier(
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->iJD = iGuess;
|
||||
p->validJD = 1;
|
||||
p->tzSet = 1;
|
||||
p->isUtc = 1;
|
||||
p->isLocal = 0;
|
||||
}
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
@ -861,7 +872,7 @@ static int parseModifier(
|
||||
&& r>=0.0 && r<7.0 && (n=(int)r)==r ){
|
||||
sqlite3_int64 Z;
|
||||
computeYMD_HMS(p);
|
||||
p->validTZ = 0;
|
||||
p->tz = 0;
|
||||
p->validJD = 0;
|
||||
computeJD(p);
|
||||
Z = ((p->iJD + 129600000)/86400000) % 7;
|
||||
@ -901,7 +912,7 @@ static int parseModifier(
|
||||
p->h = p->m = 0;
|
||||
p->s = 0.0;
|
||||
p->rawS = 0;
|
||||
p->validTZ = 0;
|
||||
p->tz = 0;
|
||||
p->validJD = 0;
|
||||
if( sqlite3_stricmp(z,"month")==0 ){
|
||||
p->D = 1;
|
||||
@ -1674,9 +1685,7 @@ static void timediffFunc(
|
||||
d1.iJD = d2.iJD - d1.iJD;
|
||||
d1.iJD += (u64)1486995408 * (u64)100000;
|
||||
}
|
||||
d1.validYMD = 0;
|
||||
d1.validHMS = 0;
|
||||
d1.validTZ = 0;
|
||||
clearYMD_HMS_TZ(&d1);
|
||||
computeYMD_HMS(&d1);
|
||||
sqlite3StrAccumInit(&sRes, 0, 0, 0, 100);
|
||||
sqlite3_str_appendf(&sRes, "%c%04d-%02d-%02d %02d:%02d:%06.3f",
|
||||
@ -1745,6 +1754,36 @@ static void currentTimeFunc(
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(SQLITE_OMIT_DATETIME_FUNCS) && defined(SQLITE_DEBUG)
|
||||
/*
|
||||
** datedebug(...)
|
||||
**
|
||||
** This routine returns JSON that describes the internal DateTime object.
|
||||
** Used for debugging and testing only. Subject to change.
|
||||
*/
|
||||
static void datedebugFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
DateTime x;
|
||||
if( isDate(context, argc, argv, &x)==0 ){
|
||||
char *zJson;
|
||||
zJson = sqlite3_mprintf(
|
||||
"{iJD:%lld,Y:%d,M:%d,D:%d,h:%d,m:%d,tz:%d,"
|
||||
"s:%.3f,validJD:%d,validYMS:%d,validHMS:%d,"
|
||||
"nFloor:%d,rawS:%d,isError:%d,useSubsec:%d,"
|
||||
"isUtc:%d,isLocal:%d}",
|
||||
x.iJD, x.Y, x.M, x.D, x.h, x.m, x.tz,
|
||||
x.s, x.validJD, x.validYMD, x.validHMS,
|
||||
x.nFloor, x.rawS, x.isError, x.useSubsec,
|
||||
x.isUtc, x.isLocal);
|
||||
sqlite3_result_text(context, zJson, -1, sqlite3_free);
|
||||
}
|
||||
}
|
||||
#endif /* !SQLITE_OMIT_DATETIME_FUNCS && SQLITE_DEBUG */
|
||||
|
||||
|
||||
/*
|
||||
** This function registered all of the above C functions as SQL
|
||||
** functions. This should be the only routine in this file with
|
||||
@ -1760,6 +1799,9 @@ void sqlite3RegisterDateTimeFunctions(void){
|
||||
PURE_DATE(datetime, -1, 0, 0, datetimeFunc ),
|
||||
PURE_DATE(strftime, -1, 0, 0, strftimeFunc ),
|
||||
PURE_DATE(timediff, 2, 0, 0, timediffFunc ),
|
||||
#ifdef SQLITE_DEBUG
|
||||
PURE_DATE(datedebug, -1, 0, 0, datedebugFunc ),
|
||||
#endif
|
||||
DFUNCTION(current_time, 0, 0, 0, ctimeFunc ),
|
||||
DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc),
|
||||
DFUNCTION(current_date, 0, 0, 0, cdateFunc ),
|
||||
|
@ -262,7 +262,7 @@ datetest 5.15 {datetime('1994-04-16 14:00:00 +05:00 Z')} NULL
|
||||
# localtime->utc and utc->localtime conversions.
|
||||
#
|
||||
# Use SQLITE_TESTCTRL_LOCALTIME_FAULT=2 to set an alternative localtime_r()
|
||||
# implementation that is not locale-dependent. This testing localtime_r()
|
||||
# implementation that is not locale-dependent. The testing localtime_r()
|
||||
# operates as follows:
|
||||
#
|
||||
# (1) Localtime is 30 minutes earlier than (west of) UTC on
|
||||
@ -321,6 +321,38 @@ utc_to_local 6.22 {1800-10-29 12:30:00} {1800-10-29 12:00:00}
|
||||
local_to_utc 6.23 {3000-10-30 12:00:00} {3000-10-30 11:30:00}
|
||||
utc_to_local 6.24 {3000-10-30 11:30:00} {3000-10-30 12:00:00}
|
||||
|
||||
# If the time is specified to be ZULU, or if it has an explicit
|
||||
# timezone extension, then the time will already be UTC and subsequent
|
||||
# 'utc' modifiers are no-ops.
|
||||
#
|
||||
do_execsql_test date-6.25 {
|
||||
SELECT datetime('2000-10-29 12:00Z','utc','utc');
|
||||
} {{2000-10-29 12:00:00}}
|
||||
do_execsql_test date-6.26 {
|
||||
SELECT datetime('2000-10-29 12:00:00+05:00');
|
||||
} {{2000-10-29 07:00:00}}
|
||||
do_execsql_test date-6.27 {
|
||||
SELECT datetime('2000-10-29 12:00:00+05:00', 'utc');
|
||||
} {{2000-10-29 07:00:00}}
|
||||
|
||||
# Multiple back-and-forth UTC to LOCAL to UTC...
|
||||
do_execsql_test date-6.28 {
|
||||
SELECT datetime('2000-10-29 12:00:00Z', 'localtime');
|
||||
} {{2000-10-29 12:30:00}}
|
||||
do_execsql_test date-6.29 {
|
||||
SELECT datetime('2000-10-29 12:00:00Z', 'utc', 'localtime');
|
||||
} {{2000-10-29 12:30:00}}
|
||||
do_execsql_test date-6.30 {
|
||||
SELECT datetime('2000-10-29 12:00:00Z', 'utc', 'localtime', 'utc');
|
||||
} {{2000-10-29 12:00:00}}
|
||||
do_execsql_test date-6.31 {
|
||||
SELECT datetime('2000-10-29 12:00:00Z', 'utc','localtime','utc','localtime');
|
||||
} {{2000-10-29 12:30:00}}
|
||||
do_execsql_test date-6.32 {
|
||||
SELECT datetime('2000-10-29 12:00:00Z', 'localtime','localtime');
|
||||
} {{2000-10-29 12:30:00}}
|
||||
|
||||
|
||||
# Restore the use of the OS localtime_r() before going on...
|
||||
sqlite3_test_control SQLITE_TESTCTRL_LOCALTIME_FAULT 0
|
||||
|
||||
|
@ -30,7 +30,7 @@ do_test 2.1 {
|
||||
catchsql { SELECT datetime('now', 'localtime') }
|
||||
} {1 {local time unavailable}}
|
||||
do_test 2.2 {
|
||||
catchsql { SELECT datetime('now', 'utc') }
|
||||
catchsql { SELECT datetime('2000-01-01', 'utc') }
|
||||
} {1 {local time unavailable}}
|
||||
|
||||
sqlite3_test_control SQLITE_TESTCTRL_LOCALTIME_FAULT 0
|
||||
|
Loading…
Reference in New Issue
Block a user