]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add experimental date and time functions based on julian day number. (CVS 1069)
authordrh <drh@noemail.net>
Sat, 9 Aug 2003 21:32:28 +0000 (21:32 +0000)
committerdrh <drh@noemail.net>
Sat, 9 Aug 2003 21:32:28 +0000 (21:32 +0000)
FossilOrigin-Name: a6197e2075fdf9db862484255ac16b2855bbef0a

manifest
manifest.uuid
src/func.c
src/os.c
src/os.h
src/sqliteInt.h

index c640f49a8579554ca4379a7a76ffc15a3facd4f3..2ed24abefa90ea402503f0952caf999f477dd942 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Provide\sa\smore\sinformative\serror\smessage\swhen\sa\suniqueness\sconstraint\nfails.\s\sTicket\s#419.\s(CVS\s1068)
-D 2003-08-05T13:13:38
+C Add\sexperimental\sdate\sand\stime\sfunctions\sbased\son\sjulian\sday\snumber.\s(CVS\s1069)
+D 2003-08-09T21:32:28
 F Makefile.in 9ad23ed4ca97f9670c4496432e3fbd4b3760ebde
 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -29,14 +29,14 @@ F src/copy.c 9e47975ea96751c658bcf1a0c4f0bb7c6ee61e73
 F src/delete.c 0f81e6799c089487615d38e042a2de4d2d6192bc
 F src/encode.c 25ea901a9cefb3d93774afa4a06b57cb58acf544
 F src/expr.c 03c321ac66c1e998c2e0faf22184b5a808b559ca
-F src/func.c 6b23578d48a8be98a664db145a635c2fa9ddb57b
+F src/func.c 67b66803d8a8b33dfea64ef54835d8b1d43c9f20
 F src/hash.c 058f077c1f36f266581aa16f907a3903abf64aa3
 F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
 F src/insert.c dc200ae04a36bd36e575272a069e20c528b7fbdf
 F src/main.c 2500392bad5629b6d70b06ac5a076958acb49b92
 F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
-F src/os.c b0ae51da6e2ec7dd9f48f92ac88985d5fde8c1d5
-F src/os.h 8aed1c928449433acf19d30f76bc86d549041167
+F src/os.c acb42a6524adb04cf4b98dd130f1f64657180bba
+F src/os.h 4c51809f56daf21efbe26fc932998e865a5bc211
 F src/pager.c a4fd3a61d63879365f775875edfffaa8c6f3d7f8
 F src/pager.h 5da62c83443f26b1792cfd72c96c422f91aadd31
 F src/parse.y 16aed0e3ed05445fa7f6a4209cc054208c7083c0
@@ -47,7 +47,7 @@ F src/select.c 2fa83d6c972d3e3f379faee32e3621411490dedb
 F src/shell.c c2ba26c850874964f5ec1ebf6c43406f28e44c4a
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in dafa83571810b6932f089b589c783355ef7a54b5
-F src/sqliteInt.h a57cafea9ef34e73e4504ada4bc70417def67f1e
+F src/sqliteInt.h cdcfdb5dca98359e443e23384b822a53ebd610ac
 F src/table.c 4301926464d88d2c2c7cd21c3360aa75bf068b95
 F src/tclsqlite.c d6860dcd56348b9521726280b72c412d2a33ae97
 F src/test1.c b12b585bfb4763df3262975ed8d3f4f274b5eaed
@@ -168,7 +168,7 @@ F www/speed.tcl 2f6b1155b99d39adb185f900456d1d592c4832b3
 F www/sqlite.tcl 3c83b08cf9f18aa2d69453ff441a36c40e431604
 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
 F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1
-P da6273255471673841fdcadc688aeac80722e130
-R 1c0d64f5eac1f91d3d759569b9f6d88e
+P 086aa1c9922b7bf399b3ee8b73ba7353d126b119
+R b633d80bd522667187e3193065550fbf
 U drh
-Z cf08853c7690373b766f29a37d61c34c
+Z 4776791846cfff06b30527d77321657f
index 038e22f1331c25b707a4099c4585bd865c3cea43..eaf7f93b2fb30c4eb4cfa67f9ad194d387695c51 100644 (file)
@@ -1 +1 @@
-086aa1c9922b7bf399b3ee8b73ba7353d126b119
\ No newline at end of file
+a6197e2075fdf9db862484255ac16b2855bbef0a
\ No newline at end of file
index 701403bd5f0cddc5fd2fbbc2996ef1ba4eb69a86..10f0e089051cd5fe38b8825feee5c5728341afea 100644 (file)
 ** sqliteRegisterBuildinFunctions() found at the bottom of the file.
 ** All other code has file scope.
 **
-** $Id: func.c,v 1.26 2003/06/28 16:20:23 drh Exp $
+** $Id: func.c,v 1.27 2003/08/09 21:32:28 drh Exp $
 */
 #include <ctype.h>
 #include <math.h>
 #include <stdlib.h>
 #include <assert.h>
 #include "sqliteInt.h"
+#include "os.h"
 
 /*
 ** Implementation of the non-aggregate min() and max() functions
@@ -497,6 +498,312 @@ static void minMaxFinalize(sqlite_func *context){
   }
 }
 
+/****************************************************************************
+** Time and date functions.
+**
+** SQLite processes all times and dates as Julian Day numbers.  The
+** dates and times are stored as the number of days since noon
+** in Greenwich on January 01, 4713 B.C.  (a.k.a -4713-01-01 12:00:00)
+** This implement requires years to be expressed as a 4-digit number
+** which means that only dates between 0000-01-01 and 9999-12-31 can
+** be represented, even though julian day numbers allow a much wider
+** range of dates.
+**
+** The Gregorian calendar system is used for all dates and times,
+** even those that predate the Gregorian calendar.  Historians often
+** use the Julian calendar for dates prior to 1582-10-15 and for some
+** dates afterwards, depending on locale.  Beware of this difference.
+**
+** The conversion algorithms are implemented based on descriptions
+** in the following text:
+**
+**      Jean Meeus
+**      Astronomical Algorithms, 2nd Edition, 1998
+**      ISBM 0-943396-61-1
+**      Willmann-Bell, Inc
+**      Richmond, Virginia (USA)
+*/
+#ifndef SQLITE_OMIT_DATETIME_FUNCS
+
+/*
+** Convert N digits from zDate into an integer.  Return
+** -1 if zDate does not begin with N digits.
+*/
+static int getDigits(const char *zDate, int N){
+  int val = 0;
+  while( N-- ){
+    if( !isdigit(*zDate) ) return -1;
+    val = val*10 + *zDate - '0';
+    zDate++;
+  }
+  return val;
+}
+
+/*
+** Parse dates of the form HH:MM:SS or HH:MM.  Store the
+** result (in days) in *prJD.
+**
+** Return 1 if there is a parsing error and 0 on success.
+*/
+static int parseHhMmSs(const char *zDate, double *prJD){
+  int h, m, s;
+  h = getDigits(zDate, 2);
+  if( h<0 || zDate[2]!=':' ) return 1;
+  zDate += 3;
+  m = getDigits(zDate, 2);
+  if( m<0 || m>59 ) return 1;
+  zDate += 2;
+  if( *zDate==':' ){
+    s = getDigits(&zDate[1], 2);
+    if( s<0 || s>59 ) return 1;
+    zDate += 3;
+  }else{
+    s = 0;
+  }
+  while( isspace(*zDate) ){ zDate++; }
+  *prJD = (h*3600.0 + m*60.0 + s)/86400.0;
+  return 0;
+}
+
+/*
+** Parse dates of the form
+**
+**     YYYY-MM-DD HH:MM:SS
+**     YYYY-MM-DD HH:MM
+**     YYYY-MM-DD
+**
+** Write the result as a julian day number in *prJD.  Return 0
+** on success and 1 if the input string is not a well-formed
+** date.
+*/
+static int parseYyyyMmDd(const char *zDate, double *prJD){
+  int Y, M, D;
+  double rTime;
+  int A, B, X1, X2;
+
+  Y = getDigits(zDate, 4);
+  if( Y<0 || zDate[4]!='-' ) return 1;
+  zDate += 5;
+  M = getDigits(zDate, 2);
+  if( M<=0 || M>12 || zDate[2]!='-' ) return 1;
+  zDate += 3;
+  D = getDigits(zDate, 2);
+  if( D<=0 || D>31 ) return 1;
+  zDate += 2;
+  while( isspace(*zDate) ){ zDate++; }
+  if( isdigit(*zDate) ){
+    if( parseHhMmSs(zDate, &rTime) ) return 1;
+  }else if( *zDate==0 ){
+    rTime = 0.0;
+  }else{ 
+    return 1;
+  }
+
+  /* The year, month, and day are now stored in Y, M, and D.  Convert
+  ** these into the Julian Day number.  See Meeus page 61.
+  */
+  if( M<=2 ){
+    Y--;
+    M += 12;
+  }
+  A = Y/100;
+  B = 2 - A + (A/4);
+  X1 = 365.25*(Y+4716);
+  X2 = 30.6001*(M+1);
+  *prJD = X1 + X2 + D + B - 1524.5 + rTime;
+  return 0;
+}
+
+/*
+** Attempt to parse the given string into a Julian Day Number.  Return
+** the number of errors.
+**
+** The following are acceptable forms for the input string:
+**
+**      YYYY-MM-DD
+**      YYYY-MM-DD HH:MM
+**      YYYY-MM-DD HH:MM:SS
+**      HH:MM
+**      HH:MM:SS
+**      DDDD.DD 
+**      now
+*/
+static int parseDateOrTime(const char *zDate, double *prJD){
+  int i;
+  for(i=0; isdigit(zDate[i]); i++){}
+  if( i==4 && zDate[i]=='-' ){
+    return parseYyyyMmDd(zDate, prJD);
+  }else if( i==2 && zDate[i]==':' ){
+    return parseHhMmSs(zDate, prJD);
+  }else if( i==0 && sqliteStrICmp(zDate,"now")==0 ){
+    return sqliteOsCurrentTime(prJD);
+  }else if( sqliteIsNumber(zDate) ){
+    *prJD = atof(zDate);
+    return 0;
+  }
+  return 1;
+}
+
+/*
+** Break up a julian day number into year, month, day, and seconds.
+** This function assume the Gregorian calendar - even for dates prior
+** to the invention of the Gregorian calendar in 1582.
+**
+** See Meeus page 63.
+*/
+static void decomposeDate(double JD, int *pY, int *pM, int *pD, int *pS){
+  int Z, A, B, C, D, E, X1;
+  Z = JD + 0.5;
+  A = (Z - 1867216.25)/36524.25;
+  A = Z + 1 + A - (A/4);
+  B = A + 1524;
+  C = (B - 122.1)/365.25;
+  D = 365.25*C;
+  E = (B-D)/30.6001;
+  X1 = 30.6001*E;
+  *pD = B - D - X1;
+  *pM = E<14 ? E-1 : E-13;
+  *pY = *pD>2 ? C - 4716 : C - 4715;
+  *pS = (JD + 0.5 - Z)*86400.0;
+}
+
+/*
+** Check to see that all arguments are valid date strings.  If any is
+** not a valid date string, return 0.  If all are valid, return 1.
+** Write into *prJD the sum of the julian day numbers for all date
+** strings.
+*/
+static int isDate(
+  sqlite_func *context,
+  int argc,
+  const char **argv,
+  double *prJD
+){
+  double r;
+  int i;
+  *prJD = 0.0;
+  for(i=0; i<argc; i++){
+    if( argv[i]==0 ) return 0;
+    if( parseDateOrTime(argv[i], &r) ) return 0;
+    *prJD += r;
+  }
+  return 1;
+}
+
+/*
+** The following routines implement the various date and time functions
+** of SQLite.
+*/
+static void juliandayFunc(sqlite_func *context, int argc, const char **argv){
+  double JD;
+  if( isDate(context, argc, argv, &JD) ){
+    sqlite_set_result_double(context, JD);
+  }
+}
+static void timestampFunc(sqlite_func *context, int argc, const char **argv){
+  double JD;
+  if( isDate(context, argc, argv, &JD) ){
+    int Y, M, D, h, m, s;
+    char zBuf[100];
+    decomposeDate(JD, &Y, &M, &D, &s);
+    h = s/3600;
+    s -= h*3600;
+    m = s/60;
+    s -= m*60;
+    sprintf(zBuf, "%04d-%02d-%02d %02d:%02d:%02d", Y, M, D, h, m, s);
+    sqlite_set_result_string(context, zBuf, -1);
+  }
+}
+static void timeFunc(sqlite_func *context, int argc, const char **argv){
+  double JD;
+  if( isDate(context, argc, argv, &JD) ){
+    int Y, M, D, h, m, s;
+    char zBuf[100];
+    decomposeDate(JD, &Y, &M, &D, &s);
+    h = s/3600;
+    s -= h*3600;
+    m = s/60;
+    s -= m*60;
+    sprintf(zBuf, "%02d:%02d:%02d", h, m, s);
+    sqlite_set_result_string(context, zBuf, -1);
+  }
+}
+static void dateFunc(sqlite_func *context, int argc, const char **argv){
+  double JD;
+  if( isDate(context, argc, argv, &JD) ){
+    int Y, M, D, s;
+    char zBuf[100];
+    decomposeDate(JD, &Y, &M, &D, &s);
+    sprintf(zBuf, "%04d-%02d-%02d", Y, M, D);
+    sqlite_set_result_string(context, zBuf, -1);
+  }
+}
+static void yearFunc(sqlite_func *context, int argc, const char **argv){
+  double JD;
+  if( isDate(context, argc, argv, &JD) ){
+    int Y, M, D, s;
+    decomposeDate(JD, &Y, &M, &D, &s);
+    sqlite_set_result_int(context, Y);
+  }
+}
+static void monthFunc(sqlite_func *context, int argc, const char **argv){
+  double JD;
+  if( isDate(context, argc, argv, &JD) ){
+    int Y, M, D, s;
+    decomposeDate(JD, &Y, &M, &D, &s);
+    sqlite_set_result_int(context, M);
+  }
+}
+static void dayofweekFunc(sqlite_func *context, int argc, const char **argv){
+  double JD;
+  if( isDate(context, argc, argv, &JD) ){
+    int Z = JD + 1.5;
+    sqlite_set_result_int(context, Z % 7);
+  }
+}
+static void dayofmonthFunc(sqlite_func *context, int argc, const char **argv){
+  double JD;
+  if( isDate(context, argc, argv, &JD) ){
+    int Y, M, D, s;
+    decomposeDate(JD, &Y, &M, &D, &s);
+    sqlite_set_result_int(context, D);
+  }
+}
+static void secondFunc(sqlite_func *context, int argc, const char **argv){
+  double JD;
+  if( isDate(context, argc, argv, &JD) ){
+    int Y, M, D, h, m, s;
+    decomposeDate(JD, &Y, &M, &D, &s);
+    h = s/3600;
+    s -= h*3600;
+    m = s/60;
+    s -= m*60;
+    sqlite_set_result_int(context, s);
+  }
+}
+static void minuteFunc(sqlite_func *context, int argc, const char **argv){
+  double JD;
+  if( isDate(context, argc, argv, &JD) ){
+    int Y, M, D, h, m, s;
+    decomposeDate(JD, &Y, &M, &D, &s);
+    h = s/3600;
+    s -= h*3600;
+    m = s/60;
+    sqlite_set_result_int(context, m);
+  }
+}
+static void hourFunc(sqlite_func *context, int argc, const char **argv){
+  double JD;
+  if( isDate(context, argc, argv, &JD) ){
+    int Y, M, D, h, s;
+    decomposeDate(JD, &Y, &M, &D, &s);
+    h = s/3600;
+    sqlite_set_result_int(context, h);
+  }
+}
+#endif /* !defined(SQLITE_OMIT_DATETIME_FUNCS) */
+/***************************************************************************/
+
 /*
 ** This function registered all of the above C functions as SQL
 ** functions.  This should be the only routine in this file with
@@ -529,6 +836,19 @@ void sqliteRegisterBuiltinFunctions(sqlite *db){
     { "glob",       2, SQLITE_NUMERIC, globFunc   },
     { "nullif",     2, SQLITE_ARGS,    nullifFunc },
     { "sqlite_version",0,SQLITE_TEXT,  versionFunc},
+#ifndef SQLITE_OMIT_DATETIME_FUNCS
+    { "julianday", -1, SQLITE_NUMERIC, juliandayFunc   },
+    { "timestamp", -1, SQLITE_TEXT,    timestampFunc   },
+    { "time",      -1, SQLITE_TEXT,    timeFunc        },
+    { "date",      -1, SQLITE_TEXT,    dateFunc        },
+    { "year",      -1, SQLITE_NUMERIC, yearFunc        },
+    { "month",     -1, SQLITE_NUMERIC, monthFunc       },
+    { "dayofmonth",-1, SQLITE_NUMERIC, dayofmonthFunc  },
+    { "dayofweek", -1, SQLITE_NUMERIC, dayofweekFunc   },
+    { "hour",      -1, SQLITE_NUMERIC, hourFunc        },
+    { "minute",    -1, SQLITE_NUMERIC, minuteFunc      },
+    { "second",    -1, SQLITE_NUMERIC, secondFunc      },
+#endif
 #ifdef SQLITE_SOUNDEX
     { "soundex",    1, SQLITE_TEXT,    soundexFunc},
 #endif
index 2e3fdddbc6a57a6df507902880fa3bb64a24fea0..543f50e9178515bf447189865015e6111476a2d8 100644 (file)
--- a/src/os.c
+++ b/src/os.c
@@ -1597,3 +1597,18 @@ char *sqliteOsFullPathname(const char *zRelative){
   return zFull;
 #endif
 }
+
+/*
+** Find the current time (in Universal Coordinated Time).  Write the
+** current time and date as a Julian Day number into *prNow and
+** return 0.  Return 1 if the time and date cannot be found.
+*/
+int sqliteOsCurrentTime(double *prNow){
+#if OS_UNIX
+  time_t t;
+  time(&t);
+  *prNow = t/86400.0 + 2440587.5;
+  return 0;
+#endif
+  return 1;
+}
index f47046ce13a706424179b004556bb63b172a3ccd..1fdf92538e500f7578b851e9b5e4f389da2edca2 100644 (file)
--- a/src/os.h
+++ b/src/os.h
@@ -171,6 +171,7 @@ int sqliteOsWriteLock(OsFile*);
 int sqliteOsUnlock(OsFile*);
 int sqliteOsRandomSeed(char*);
 int sqliteOsSleep(int ms);
+int sqliteOsCurrentTime(double*);
 void sqliteOsEnterMutex(void);
 void sqliteOsLeaveMutex(void);
 char *sqliteOsFullPathname(const char*);
index 63604d9ea1ff31990f979b8ce6351c3ced9da403..8b6b8bbea883043f27477566dabe2a73f6286b04 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.195 2003/07/30 12:34:12 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.196 2003/08/09 21:32:28 drh Exp $
 */
 #include "config.h"
 #include "sqlite.h"
@@ -87,6 +87,7 @@
 /* #define SQLITE_OMIT_AUTHORIZATION  1 */
 /* #define SQLITE_OMIT_INMEMORYDB     1 */
 /* #define SQLITE_OMIT_VACUUM         1 */
+/* #define SQLITE_OMIT_TIMEDATE_FUNCS 1 */
 
 /*
 ** Integers of known sizes.  These typedefs might change for architectures