]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Adjust date/time functions so that they do a better job of keeping track
authordrh <>
Mon, 4 Mar 2024 13:58:09 +0000 (13:58 +0000)
committerdrh <>
Mon, 4 Mar 2024 13:58:09 +0000 (13:58 +0000)
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

manifest
manifest.uuid
src/date.c
test/date.test
test/tkt-bd484a090c.test

index f23cc045a5816f97b34bf5d62baa406f2df0f301..e7d61781ccb497c36323679ecbd008d1ea7576bb 100644 (file)
--- a/manifest
+++ b/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.
index 9788f68f52001b06557f0121e0bf65f6183d853b..7e9ada2541ac67c5442baa499109cb9e42b871a8 100644 (file)
@@ -1 +1 @@
-fc773f6c76ec114be8b6e25b13885acb5adcc9d052dca0d8d0ff94e2a0743d64
\ No newline at end of file
+dc569683748354a6db83438904422e802d3ea780775c48da85b474fff03ca8a1
\ No newline at end of file
index 239a772ca29dfab68273951207e206759350b2eb..3bb4a8c714bf073dadaa55f3ccb0479621a5dd94 100644 (file)
@@ -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     ),
index 2a6e93df40c063039cee6fe41fad693b57af3b73..d22b652b474a3f070ba0a24f054c1611cf343567 100644 (file)
@@ -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
 
index 3d2b59995857c9a7f61b6860c381c51b23cdf036..7867c8dc97c29ade95d1047313c9cbed483fd57b 100644 (file)
@@ -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