]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Improvements to the timediff() function. Test cases added. Not all test
authordrh <>
Tue, 30 May 2023 02:16:33 +0000 (02:16 +0000)
committerdrh <>
Tue, 30 May 2023 02:16:33 +0000 (02:16 +0000)
cases are passing.

FossilOrigin-Name: 2cf6518140d61e2123dc20f39d7e97e7bce17e7641405df7fd63e1cdda444ae0

manifest
manifest.uuid
src/date.c
test/timediff1.test [new file with mode: 0644]

index a1547dded7a64878f6ef6c4581e7010fe9f48471..d7e2b18402e746cc0e2b8782805633309076a356 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C New\sdate/time\smodifier:\s\s(+|-)YYYY-MM-DD\sHH:MM(:SS.SSS)?
-D 2023-05-29T20:33:33.570
+C Improvements\sto\sthe\stimediff()\sfunction.\s\sTest\scases\sadded.\sNot\sall\stest\ncases\sare\spassing.
+D 2023-05-30T02:16:33.896
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -583,7 +583,7 @@ F src/build.c 5512d5a335334b48d116f1ecd051edef96a60add18ae48e0ea302a395f00f3d9
 F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490
 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
 F src/ctime.c 20507cc0b0a6c19cd882fcd0eaeda32ae6a4229fb4b024cfdf3183043d9b703d
-F src/date.c 3752bcd1a5b90147f28fff2a32877935afe10d79ac33a08f24ddf820c5d2e924
+F src/date.c 71c0484764e7d2c4a5f7a98c4e6d3403998eae554a81b3c02a90a1c836049902
 F src/dbpage.c f3eea5f7ec47e09ee7da40f42b25092ecbe961fc59566b8e5f705f34335b2387
 F src/dbstat.c ec92074baa61d883de58c945162d9e666c13cd7cf3a23bc38b4d1c4d0b2c2bef
 F src/delete.c 05e27e3a55dcfeadf2f7ca95a5c5e0928f182c04640ec1954ffa42f3d5c19341
@@ -1605,6 +1605,7 @@ F test/threadtest3.c 655bff6c0895ec03f014126aa65e808fac9aae8c5a7a7da58a510cbe8b4
 F test/threadtest4.c c1e67136ceb6c7ec8184e56ac61db28f96bd2925
 F test/threadtest5.c 9b4d782c58d8915d7e955ff8051f3d03628bda0d33b82971ea8c0f2f2808c421
 F test/time-wordcount.sh 8e0b0f8109367827ad5d58f5cc849705731e4b90
+F test/timediff1.test 701eea6ce725d71305d64292b44d9a0154b7c99ce09c916f9386de3e71689918
 F test/tkt-02a8e81d44.test 6c80d9c7514e2a42d4918bf87bf6bc54f379110c
 F test/tkt-18458b1a.test 6a62cb1ee50fa3c620da59e3a6f531eb38fceaf7e2166203816b724524e6f1d6
 F test/tkt-26ff0c2d1e.test c15bec890c4d226c0da2f35ff30f9e84c169cfef90e73a8cb5cec11d723dfa96
@@ -2071,8 +2072,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 054a195125a273bab026ada5f07cc7c32818007027a1fd028ca59d2f179276d4
-R b507ad1612c7c470e9ed65929342a32e
+P 3832b8a9ef84d9b53b48dfce9c1e724897cd7b3e05711e01675419655f894eac
+R 8ffc400398f351d057bce50fed0005bf
 U drh
-Z c8a8d9945ac3ddc516393bf70fb76d82
+Z 6414db32510ab8f356b11e69a29d6be4
 # Remove this line to create a well-formed Fossil manifest.
index 74271f635a644e86b07113ed5aea60d8f0f25230..7c9c1ef8776deabdf6ec801be3c8781fb6491805 100644 (file)
@@ -1 +1 @@
-3832b8a9ef84d9b53b48dfce9c1e724897cd7b3e05711e01675419655f894eac
\ No newline at end of file
+2cf6518140d61e2123dc20f39d7e97e7bce17e7641405df7fd63e1cdda444ae0
\ No newline at end of file
index 4106bd899c830b581982c5aac140b93b57368545..6c1acaa9ae203dfd85ad0b2007ce52197af9c24a 100644 (file)
@@ -871,6 +871,7 @@ static int parseModifier(
       double rRounder;
       int i;
       int Y,M,D,H,x;
+      const char *z2 = z;
       for(n=1; z[n]; n++){
         if( z[n]==':' ) break;
         if( sqlite3Isspace(z[n]) ) break;
@@ -907,16 +908,16 @@ static int parseModifier(
         p->validHMS = 0;
         p->validYMD = 0;
         p->iJD += (i64)D*86400000;
-        z = &z[12];
+        z2 = &z[12];
         n = 2;
       }
-      if( z[n]==':' ){
+      if( z2[n]==':' ){
         /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the
         ** specified number of hours, minutes, seconds, and fractional seconds
         ** to the time.  The ".FFF" may be omitted.  The ":SS.FFF" may be
         ** omitted.
         */
-        const char *z2 = z;
+
         DateTime tx;
         sqlite3_int64 day;
         if( !sqlite3Isdigit(*z2) ) z2++;
@@ -1370,12 +1371,15 @@ static void cdateFunc(
 /*
 ** timediff(DATE1, DATE2)
 **
-** Return the that amount of time that DATE1 is later than DATE2 in
-** this format:
+** Return the amount of time that must be added to DATE2 in order to
+** convert it into DATE2.  The time difference format is:
 **
 **     +YYYY-MM-DD HH:MM:SS.SSS
 **
-** The initial "+" becomes "-" if DATE1 occurs before DATE2.
+** The initial "+" becomes "-" if DATE1 occurs before DATE2.  For
+** date/time values A and B, the following invariant should hold:
+**
+**     datetime(A) == (datetime(B, timediff(A,B))
 */
 static void timediffFunc(
   sqlite3_context *context,
@@ -1384,8 +1388,9 @@ static void timediffFunc(
 ){
   DateTime d1, d2;
   sqlite3_str *pOut = 0;
-  char sign = '+';
+  char sign;
   int rc;
+  int Y, M;
   if( isDate(context, 1, argv, &d1)     ) return;
   if( isDate(context, 1, &argv[1], &d2) ) return;
   autoAdjustDate(&d1);
@@ -1397,20 +1402,85 @@ static void timediffFunc(
     sqlite3_result_error_nomem(context);
     return;
   }
-  if( d1.iJD<d2.iJD ){
+  computeYMD_HMS(&d1);
+  computeYMD_HMS(&d2);
+  if( d1.iJD>=d2.iJD ){
+    sign = '+';
+    Y = d1.Y - d2.Y;
+    if( Y ){
+      d2.Y = d1.Y;
+      d2.validJD = 0;
+      computeJD(&d2);
+    }
+    M = d1.M - d2.M;
+    if( M<0 ){
+      Y--;
+      d2.Y--;
+      M += 12;
+    }
+    if( M!=0 ){
+      d2.M = d1.M;
+      d2.validJD = 0;
+      computeJD(&d2);
+    }
+    if( d1.iJD<d2.iJD ){
+      M--;
+      if( M<0 ){
+        M = 11;
+        Y--;
+      }
+      d2.M--;
+      if( d2.M<0 ){
+        d2.M = 12;
+        d2.Y--;
+      }
+      d2.validJD = 0;
+      computeJD(&d2);
+    }
+    d1.iJD -= d2.iJD;
+    d1.iJD += 148699540800000;
+  }else{
     sign = '-';
-    DateTime x = d1;
-    d1 = d2;
-    d2 = x;
+    Y = d2.Y - d1.Y;
+    if( Y ){
+      d2.Y = d1.Y;
+      d2.validJD = 0;
+      computeJD(&d2);
+    }
+    M = d2.M - d1.M;
+    if( M<0 ){
+      Y--;
+      d2.Y--;
+      M += 12;
+    }
+    if( M!=0 ){
+      d2.M = d1.M;
+      d2.validJD = 0;
+      computeJD(&d2);
+    }
+    if( d1.iJD>d2.iJD ){
+      M--;
+      if( M<0 ){
+        M = 11;
+        Y--;
+      }
+      d2.M++;
+      if( d2.M>12 ){
+        d2.M = 1;
+        d2.Y++;
+      }
+      d2.validJD = 0;
+      computeJD(&d2);
+    }
+    d1.iJD = d2.iJD - d1.iJD;
+    d1.iJD += 148699540800000;
   }
   d1.validYMD = 0;
   d1.validHMS = 0;
   d1.validTZ = 0;
-  d1.iJD -= d2.iJD;
-  d1.iJD += 148699540800000;
   computeYMD_HMS(&d1);
-  sqlite3_str_appendf(pOut, "%c%04d-%02d-%02d %02d:%02d:%07.3f",
-     sign, d1.Y, d1.M-1, d1.D-1, d1.h, d1.m, d1.s);
+  sqlite3_str_appendf(pOut, "%c%04d-%02d-%02d %02d:%02d:%06.3f",
+       sign, Y, M, d1.D-1, d1.h, d1.m, d1.s);
   rc = sqlite3_str_errcode(pOut);
   if( rc ){
     sqlite3_free(sqlite3_str_finish(pOut));
diff --git a/test/timediff1.test b/test/timediff1.test
new file mode 100644 (file)
index 0000000..b9a38b9
--- /dev/null
@@ -0,0 +1,111 @@
+# 2023-05-30
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.  The
+# focus of this file is testing date and time functions.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Skip this whole file if date and time functions are omitted
+# at compile-time
+#
+ifcapable {!datetime} {
+  finish_test
+  return
+}
+
+proc datetest {tnum expr result} {
+  do_test timediff-$tnum [subst {
+    execsql "SELECT coalesce($expr,'NULL')"
+  }] [list $result]
+}
+set tcl_precision 15
+
+
+# February overflow on a leap year
+datetest 1.1 {datetime('2000-01-31','+1 month')} {2000-03-02 00:00:00}
+datetest 1.2 {datetime('2004-01-29','+1 month')} {2004-02-29 00:00:00}
+datetest 1.3 {datetime('2000-03-01','-1 day')}   {2000-02-29 00:00:00}
+datetest 1.4 {datetime('2000-03-31','-1 month')} {2000-03-02 00:00:00}
+datetest 1.5 {datetime('2000-03-30','-1 month')} {2000-03-01 00:00:00}
+datetest 1.6 {datetime('2000-03-29','-1 month')} {2000-02-29 00:00:00}
+datetest 1.7 {datetime('2000-03-28','-1 month')} {2000-02-28 00:00:00}
+datetest 1.8 {datetime('2000-02-29','+1 year')}  {2001-03-01 00:00:00}
+datetest 1.9 {datetime('2000-02-29','+4 years')} {2004-02-29 00:00:00}
+
+datetest 1.10 {datetime('1998-11-10','+0001-03-19 12:34:56')} \
+                                                 {2000-02-29 12:34:56}
+datetest 1.11 {datetime('2000-01-31','+0004-01-00 12:34:56')} \
+                                                 {2004-03-02 12:34:56}
+datetest 1.12 {datetime('2000-01-29','+0008-01-00 12:34:56')} \
+                                                 {2008-02-29 12:34:56}
+datetest 1.13 {datetime('2001-03-31','-0001-01-00 06:10')} \
+                                                 {2000-03-01 17:50:00}
+
+
+# February overflow on a non-leap year
+datetest 2.1 {datetime('2001-01-31','+1 month')} {2001-03-03 00:00:00}
+datetest 2.2 {datetime('2005-01-29','+1 month')} {2005-03-01 00:00:00}
+datetest 2.3 {datetime('2001-03-01','-1 day')}   {2001-02-28 00:00:00}
+datetest 2.4 {datetime('2001-03-31','-1 month')} {2001-03-03 00:00:00}
+datetest 2.5 {datetime('2001-03-30','-1 month')} {2001-03-02 00:00:00}
+datetest 2.6 {datetime('2001-03-29','-1 month')} {2001-03-01 00:00:00}
+datetest 2.7 {datetime('2001-03-28','-1 month')} {2001-02-28 00:00:00}
+
+datetest 2.10 {datetime('1999-11-10','+0001-03-19 12:34:56')} \
+                                                 {2001-03-01 12:34:56}
+datetest 2.11 {datetime('2000-01-31','+0005-01-00 12:34:56')} \
+                                                 {2005-03-03 12:34:56}
+datetest 2.12 {datetime('2000-01-29','+0009-01-00 12:34:56')} \
+                                                 {2009-03-01 12:34:56}
+datetest 2.13 {datetime('2002-03-31','-0001-01-00 06:10')} \
+                                                 {2001-03-02 17:50:00}
+
+# timediff
+datetest 3.1 {timediff('2000-03-02','2000-01-31')} {+0000-01-00 00:00:00.000}
+datetest 3.2 {timediff('2000-01-31','2000-03-02')} {-0000-01-02 00:00:00.000}
+datetest 3.3 {timediff('2000-03-02','1999-01-31')} {+0001-01-00 00:00:00.000}
+datetest 3.4 {timediff('1999-01-31','2000-03-02')} {-0001-01-02 00:00:00.000}
+
+unset -nocomplain p1
+unset -nocomplain p2
+set p1 {
+  1   {0000-01-01 12:34:56}
+  2   {1776-07-04 13:00:00}
+  3   {-275529600}
+  4   {2440587.5}
+  5   {2000-05-29 14:26}
+  6   {2023-05-29 18:11}
+  7   {2050-05-29 14:26}
+}
+set p2 {
+  A   {1900-02-28 11:00}
+  B   {1900-03-01 12:00}
+  C   {2000-02-29 13:00}
+  D   {2005-04-30 23:59:59}
+  E   {2005-05-01}
+}
+
+foreach {x1 d1} $p1 {
+  foreach {x2 d2} $p2 {
+    set r1 [db one {SELECT datetime($d1,'auto')}]
+    do_execsql_test timediff-4-$x1$x2 {
+      SELECT datetime($d2, timediff($d1,$d2));
+    } [list $r1]
+    set r2 [db one {SELECT datetime($d2,'auto')}]
+    do_execsql_test timediff-4-$x2$x1 {
+      SELECT datetime($d1, timediff($d2,$d1));
+    } [list $r2]
+  }
+}
+
+finish_test