]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Revised date/time functions - now broken out into a separate source file.
authordrh <drh@noemail.net>
Sat, 1 Nov 2003 01:53:53 +0000 (01:53 +0000)
committerdrh <drh@noemail.net>
Sat, 1 Nov 2003 01:53:53 +0000 (01:53 +0000)
See the DateAndTimeFunctions wiki page for additional information. (CVS 1116)

FossilOrigin-Name: 68ef9b45bd3abdedf3721011ad0fb22e8735e721

Makefile.in
main.mk
manifest
manifest.uuid
src/date.c [new file with mode: 0644]
src/func.c
src/sqliteInt.h
test/date.test [new file with mode: 0644]

index 4c3e8e77849b9794b2b351c1d33c9f45844c262e..23e1c040cd5b7175a365895ee77a19dcac544046 100644 (file)
@@ -79,7 +79,7 @@ endif
 
 # Object files for the SQLite library.
 #
-LIBOBJ = attach.lo auth.lo btree.lo build.lo copy.lo \
+LIBOBJ = attach.lo auth.lo btree.lo build.lo copy.lo date.lo \
          delete.lo expr.lo func.lo hash.lo insert.lo \
          main.lo opcodes.lo os.lo pager.lo parse.lo pragma.lo \
          printf.lo random.lo select.lo table.lo tokenize.lo \
@@ -101,6 +101,7 @@ SRC = \
   $(TOP)/src/btree_rb.c \
   $(TOP)/src/build.c \
   $(TOP)/src/copy.c \
+  $(TOP)/src/date.c \
   $(TOP)/src/delete.c \
   $(TOP)/src/expr.c \
   $(TOP)/src/func.c \
@@ -288,6 +289,9 @@ where.lo:   $(TOP)/src/where.c $(HDR)
 copy.lo:       $(TOP)/src/copy.c $(HDR)
        $(LIBTOOL) $(TCC) -c $(TOP)/src/copy.c
 
+date.lo:       $(TOP)/src/date.c $(HDR)
+       $(LIBTOOL) $(TCC) -c $(TOP)/src/date.c
+
 delete.lo:     $(TOP)/src/delete.c $(HDR)
        $(LIBTOOL) $(TCC) -c $(TOP)/src/delete.c
 
diff --git a/main.mk b/main.mk
index 2aa68497cb18fdb18ce569bcb2e1c6ad23dd5622..3d8722dd5abcf6d374e985f40a4da51075d9a2cd 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -54,7 +54,7 @@ TCCX = $(TCC) $(OPTS) $(THREADSAFE) $(USLEEP) -I. -I$(TOP)/src
 
 # Object files for the SQLite library.
 #
-LIBOBJ = attach.o auth.o btree.o btree_rb.o build.o copy.o delete.o \
+LIBOBJ = attach.o auth.o btree.o btree_rb.o build.o copy.o date.o delete.o \
          expr.o func.o hash.o insert.o \
          main.o opcodes.o os.o pager.o parse.o pragma.o printf.o random.o \
          select.o table.o tokenize.o trigger.o update.o util.o \
@@ -70,6 +70,7 @@ SRC = \
   $(TOP)/src/btree_rb.c \
   $(TOP)/src/build.c \
   $(TOP)/src/copy.c \
+  $(TOP)/src/date.c \
   $(TOP)/src/delete.c \
   $(TOP)/src/expr.c \
   $(TOP)/src/func.c \
@@ -259,6 +260,9 @@ where.o:    $(TOP)/src/where.c $(HDR)
 copy.o:        $(TOP)/src/copy.c $(HDR)
        $(TCCX) -c $(TOP)/src/copy.c
 
+date.o:        $(TOP)/src/date.c $(HDR)
+       $(TCCX) -c $(TOP)/src/date.c
+
 delete.o:      $(TOP)/src/delete.c $(HDR)
        $(TCCX) -c $(TOP)/src/delete.c
 
index bcd5b56a379744f6cc31bbd1e4169d3e1dc8d218..bcc7d4ae2367c5156e3be79e6122941b74968d02 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,6 +1,6 @@
-C Fork\sthe\stree\sfor\sproject\s"shrike"\s(CVS\s1118)
-D 2003-10-30T07:00:00
-F Makefile.in ab585a91e34bc33928a1b6181fa2f6ebd4fb17e1
+C Revised\sdate/time\sfunctions\s-\snow\sbroken\sout\sinto\sa\sseparate\ssource\sfile.\nSee\sthe\sDateAndTimeFunctions\swiki\spage\sfor\sadditional\sinformation.\s(CVS\s1116)
+D 2003-11-01T01:53:54
+F Makefile.in 5cb273b7d0e945d47ee8b9ad1c2a04ce79927d2d
 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
 F VERSION 97d209249f825001288ff942df07b48e1083af5c
@@ -16,7 +16,7 @@ F doc/report1.txt a031aaf37b185e4fa540223cb516d3bccec7eeac
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
 F libtool bbbea7d79c23323e4100103836028e4fad0d9242
 F ltmain.sh abfb9387049fff6996afc6e325736597795baf11
-F main.mk 6af144bac62d83899b71919c738fdf442a4f1c16
+F main.mk 3e200c199e46c2b7c3106fd2c3bfa11cd0aa22c9
 F publish.sh 86b5e8535830a2588f62ce1d5d1ef00e1dede23a
 F spec.template a38492f1c1dd349fc24cb0565e08afc53045304b
 F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
@@ -28,10 +28,11 @@ F src/btree.h 9b7c09f1e64274d7bb74a57bbfc63778f67b1048
 F src/btree_rb.c 67d154ffb0fac27a4a7eab9118ece6eaafeb49c9
 F src/build.c 9def3a3b8fba59325ed686049b88c2e7aff9af12
 F src/copy.c 9e47975ea96751c658bcf1a0c4f0bb7c6ee61e73
+F src/date.c acb75ff7849ca923837a9d3ef6b2d3e111a32fb0
 F src/delete.c 0f81e6799c089487615d38e042a2de4d2d6192bc
 F src/encode.c 25ea901a9cefb3d93774afa4a06b57cb58acf544
 F src/expr.c d4d8eca6363a6e680361e5d2a934b78e5c7b7fa3
-F src/func.c fce558b4c1d895e81091d6d5e7d86a192fc8e84c
+F src/func.c 82a749b9a03ae9834a90854464f93e29f902799b
 F src/hash.c 058f077c1f36f266581aa16f907a3903abf64aa3
 F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
 F src/insert.c dc200ae04a36bd36e575272a069e20c528b7fbdf
@@ -49,7 +50,7 @@ F src/select.c d79ac60ba1595ff3c94b12892e87098329776482
 F src/shell.c c2ba26c850874964f5ec1ebf6c43406f28e44c4a
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in e6cfff01fafc8a82ce82cd8c932af421dc9adb54
-F src/sqliteInt.h 74dc7989c9f2b46b50485d0455a8ef8d4f178708
+F src/sqliteInt.h e9b2f6b3ff315d92ee240c998f9833b82c235a71
 F src/table.c 4301926464d88d2c2c7cd21c3360aa75bf068b95
 F src/tclsqlite.c 3efac6b5861ac149c41251d4d4c420c94be5ba6a
 F src/test1.c f9d5816610f7ec4168ab7b098d5207a5708712b6
@@ -82,6 +83,7 @@ F test/btree4rb.test ae6f0438512edcb45cf483471cd6070a765963a9
 F test/capi2.test ec96e0e235d87b53cbaef3d8e3e0f8ccf32c71ca
 F test/conflict.test 0911bb2f079046914a6e9c3341b36658c4e2103e
 F test/copy.test 88dabd4e811b17644b726aa81d404e73b7635c84
+F test/date.test 17619ff81d5b813092915927c50923e265e85bd8
 F test/delete.test 92256384f1801760180ded129f7427884cf28886
 F test/expr.test c4cc292d601019c2f2ce95093caaa5d10284b105
 F test/fkey1.test d65c824459916249bee501532d6154ddab0b5db7
@@ -174,7 +176,7 @@ F www/speed.tcl 2f6b1155b99d39adb185f900456d1d592c4832b3
 F www/sqlite.tcl 3c83b08cf9f18aa2d69453ff441a36c40e431604
 F www/tclsqlite.tcl b9271d44dcf147a93c98f8ecf28c927307abd6da
 F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1
-P c3a495026c7eafd576042a05a9a5f585ba8ba9b9
-R 836e4a87e08b64cd5a3c29c6f69b3de9
+P 181260c0aa7837feca9e415225ece0e9c4032c7a
+R 879a05717d19f1ac21f75967c496c2d9
 U drh
-Z de7377f7ee1c2dc37770413072c96044
+Z 7096cda608fb24dfdfaba056b6dd6a59
index 05db6a5027d44048e182f718da480d7f9449d40a..f15d8d3f5f0f2915a88b4470a84d0277bc751e54 100644 (file)
@@ -1 +1 @@
-181260c0aa7837feca9e415225ece0e9c4032c7a
\ No newline at end of file
+68ef9b45bd3abdedf3721011ad0fb22e8735e721
\ No newline at end of file
diff --git a/src/date.c b/src/date.c
new file mode 100644 (file)
index 0000000..a870359
--- /dev/null
@@ -0,0 +1,773 @@
+/*
+** 2003 October 31
+**
+** 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 contains the C functions that implement date and time
+** functions for SQLite.  
+**
+** There is only one exported symbol in this file - the function
+** sqliteRegisterDateTimeFunctions() found at the bottom of the file.
+** All other code has file scope.
+**
+** $Id: date.c,v 1.1 2003/11/01 01:53:54 drh Exp $
+**
+** NOTES:
+**
+** 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 November 24, 4714 B.C. according to the Gregorian
+** calendar system.
+**
+** 1970-01-01 00:00:00 is JD 2440587.5
+** 2000-01-01 00:00:00 is JD 2451544.5
+**
+** This implemention 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 usually
+** 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
+#include <ctype.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "sqliteInt.h"
+#include "os.h"
+
+/*
+** A structure for holding a single date and time.
+*/
+typedef struct DateTime DateTime;
+struct DateTime {
+  double rJD;      /* The julian day number */
+  int Y, M, D;     /* Year, month, and day */
+  int h, m;        /* Hour and minutes */
+  int tz;          /* Timezone offset in minutes */
+  double s;        /* Seconds */
+  char validYMD;   /* True if Y,M,D are valid */
+  char validHMS;   /* True if h,m,s are valid */
+  char validJD;    /* True if rJD is valid */
+  char validTZ;    /* True if tz is valid */
+};
+
+
+/*
+** 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;
+}
+
+/*
+** Read text from z[] and convert into a floating point number.  Return
+** the number of digits converted.
+*/
+static int getValue(const char *z, double *pR){
+  double r = 0.0;
+  double rDivide = 1.0;
+  int isNeg = 0;
+  int nChar = 0;
+  if( *z=='+' ){
+    z++;
+    nChar++;
+  }else if( *z=='-' ){
+    z++;
+    isNeg = 1;
+    nChar++;
+  }
+  if( !isdigit(*z) ) return 0;
+  while( isdigit(*z) ){
+    r = r*10.0 + *z - '0';
+    nChar++;
+    z++;
+  }
+  if( *z=='.' && isdigit(z[1]) ){
+    z++;
+    nChar++;
+    while( isdigit(*z) ){
+      r = r*10.0 + *z - '0';
+      rDivide *= 10.0;
+      nChar++;
+      z++;
+    }
+    r /= rDivide;
+  }
+  if( *z!=0 && !isspace(*z) ) return 0;
+  *pR = isNeg ? -r : r;
+  return nChar;
+}
+
+/*
+** Parse a timezone extension on the end of a date-time.
+** The extension is of the form:
+**
+**        (+/-)HH:MM
+**
+** If the parse is successful, write the number of minutes
+** of change in *pnMin and return 0.  If a parser error occurs,
+** return 0.
+**
+** A missing specifier is not considered an error.
+*/
+static int parseTimezone(const char *zDate, DateTime *p){
+  int sgn = 0;
+  int nHr, nMn;
+  while( isspace(*zDate) ){ zDate++; }
+  p->tz = 0;
+  if( *zDate=='-' ){
+    sgn = -1;
+  }else if( *zDate=='+' ){
+    sgn = +1;
+  }else{
+    return *zDate!=0;
+  }
+  zDate++;
+  nHr = getDigits(zDate, 2);
+  if( nHr<0 || nHr>14 ) return 1;
+  zDate += 2;
+  if( zDate[0]!=':' ) return 1;
+  zDate++;
+  nMn = getDigits(zDate, 2);
+  if( nMn<0 || nMn>59 ) return 1;
+  zDate += 2;
+  p->tz = sgn*(nMn + nHr*60);
+  while( isspace(*zDate) ){ zDate++; }
+  return *zDate!=0;
+}
+
+/*
+** Parse times of the form HH:MM or HH:MM:SS or HH:MM:SS.FFFF.
+** The HH, MM, and SS must each be exactly 2 digits.  The
+** fractional seconds FFFF can be one or more digits.
+**
+** Return 1 if there is a parsing error and 0 on success.
+*/
+static int parseHhMmSs(const char *zDate, DateTime *p){
+  int h, m, s;
+  double ms = 0.0;
+  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;
+    if( *zDate=='.' && isdigit(zDate[1]) ){
+      double rScale = 1.0;
+      zDate++;
+      while( isdigit(*zDate) ){
+        ms = ms*10.0 + *zDate - '0';
+        rScale *= 10.0;
+        zDate++;
+      }
+      ms /= rScale;
+    }
+  }else{
+    s = 0;
+  }
+  p->validJD = 0;
+  p->validHMS = 1;
+  p->h = h;
+  p->m = m;
+  p->s = s + ms;
+  if( parseTimezone(zDate, p) ) return 1;
+  p->validTZ = p->tz!=0;
+  return 0;
+}
+
+/*
+** Convert from YYYY-MM-DD HH:MM:SS to julian day.  We always assume
+** that the YYYY-MM-DD is according to the Gregorian calendar.
+**
+** Reference:  Meeus page 61
+*/
+static void computeJD(DateTime *p){
+  int Y, M, D, A, B, X1, X2;
+
+  if( p->validJD ) return;
+  if( p->validYMD ){
+    Y = p->Y;
+    M = p->M;
+    D = p->D;
+  }else{
+    Y = 2000;
+    M = 1;
+    D = 1;
+  }
+  if( M<=2 ){
+    Y--;
+    M += 12;
+  }
+  A = Y/100;
+  B = 2 - A + (A/4);
+  X1 = 365.25*(Y+4716);
+  X2 = 30.6001*(M+1);
+  p->rJD = X1 + X2 + D + B - 1524.5;
+  p->validJD = 1;
+  p->validYMD = 0;
+  if( p->validHMS ){
+    p->rJD += (p->h*3600.0 + p->m*60.0 + p->s)/86400.0;
+    if( p->validTZ ){
+      p->rJD += p->tz*60/86400.0;
+      p->validHMS = 0;
+      p->validTZ = 0;
+    }
+  }
+}
+
+/*
+** Parse dates of the form
+**
+**     YYYY-MM-DD HH:MM:SS.FFF
+**     YYYY-MM-DD HH:MM:SS
+**     YYYY-MM-DD HH:MM
+**     YYYY-MM-DD
+**
+** Write the result into the DateTime structure and return 0
+** on success and 1 if the input string is not a well-formed
+** date.
+*/
+static int parseYyyyMmDd(const char *zDate, DateTime *p){
+  int Y, M, D;
+
+  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, p) ) return 1;
+  }else if( *zDate==0 ){
+    p->validHMS = 0;
+  }else{
+    return 1;
+  }
+  p->validJD = 0;
+  p->validYMD = 1;
+  p->Y = Y;
+  p->M = M;
+  p->D = D;
+  if( p->validTZ ){
+    computeJD(p);
+  }
+  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 HH:MM:SS.FFF  +/-HH:MM
+**      DDDD.DD 
+**      now
+**
+** In the first form, the +/-HH:MM is always optional.  The fractional
+** seconds extension (the ".FFF") is optional.  The seconds portion
+** (":SS.FFF") is option.  The year and date can be omitted as long
+** as there is a time string.  The time string can be omitted as long
+** as there is a year and date.
+*/
+static int parseDateOrTime(const char *zDate, DateTime *p){
+  int i;
+  memset(p, 0, sizeof(*p));
+  for(i=0; isdigit(zDate[i]); i++){}
+  if( i==4 && zDate[i]=='-' ){
+    return parseYyyyMmDd(zDate, p);
+  }else if( i==2 && zDate[i]==':' ){
+    return parseHhMmSs(zDate, p);
+    return 0;
+  }else if( i==0 && sqliteStrICmp(zDate,"now")==0 ){
+    double r;
+    if( sqliteOsCurrentTime(&r)==0 ){
+      p->rJD = r;
+      p->validJD = 1;
+      return 0;
+    }
+    return 1;
+  }else if( sqliteIsNumber(zDate) ){
+    p->rJD = atof(zDate);
+    p->validJD = 1;
+    return 0;
+  }
+  return 1;
+}
+
+/*
+** Compute the Year, Month, and Day from the julian day number.
+*/
+static void computeYMD(DateTime *p){
+  int Z, A, B, C, D, E, X1;
+  if( p->validYMD ) return;
+  Z = p->rJD + 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;
+  p->D = B - D - X1;
+  p->M = E<14 ? E-1 : E-13;
+  p->Y = p->M>2 ? C - 4716 : C - 4715;
+  p->validYMD = 1;
+}
+
+/*
+** Compute the Hour, Minute, and Seconds from the julian day number.
+*/
+static void computeHMS(DateTime *p){
+  int Z, s;
+  if( p->validHMS ) return;
+  Z = p->rJD + 0.5;
+  s = (p->rJD + 0.5 - Z)*86400000.0 + 0.5;
+  p->s = 0.001*s;
+  s = p->s;
+  p->s -= s;
+  p->h = s/3600;
+  s -= p->h*3600;
+  p->m = s/60;
+  p->s += s - p->m*60;
+  p->validHMS = 1;
+}
+
+/*
+** Process a modifier to a date-time stamp.  The modifiers are
+** as follows:
+**
+**     NNN days
+**     NNN hours
+**     NNN minutes
+**     NNN.NNNN seconds
+**     NNN months
+**     NNN years
+**     start of month
+**     start of year
+**     start of week
+**     start of day
+**     weekday N
+**     unixepoch
+**
+** Return 0 on success and 1 if there is any kind of error.
+*/
+static int parseModifier(const char *zMod, DateTime *p){
+  int rc = 1;
+  int n;
+  double r;
+  char z[30];
+  for(n=0; n<sizeof(z)-1; n++){
+    z[n] = tolower(zMod[n]);
+  }
+  z[n] = 0;
+  switch( z[0] ){
+    case 'u': {
+      /*
+      **    unixepoch
+      **
+      ** Treat the current value of p->rJD as the number of
+      ** seconds since 1970.  Convert to a real julian day number.
+      */
+      if( strcmp(z, "unixepoch")==0 && p->validJD ){
+        p->rJD = p->rJD/86400.0 + 2440587.5;
+        p->validYMD = 0;
+        p->validHMS = 0;
+        p->validTZ = 0;
+        rc = 0;
+      }
+      break;
+    }
+    case 'w': {
+      /*
+      **    weekday N
+      **
+      ** Move the date to the beginning of the next occurrance of
+      ** weekday N where 0==Sunday, 1==Monday, and so forth.  If the
+      ** date is already on the appropriate weekday, this is equivalent
+      ** to "start of day".
+      */
+      if( strncmp(z, "weekday ", 8)==0 && getValue(&z[8],&r)>0
+                 && (n=r)==r && n>=0 && r<7 ){
+        int Z;
+        computeYMD(p);
+        p->validHMS = 0;
+        p->validTZ = 0;
+        p->validJD = 0;
+        computeJD(p);
+        Z = p->rJD + 1.5;
+        Z %= 7;
+        if( Z>n ) Z -= 7;
+        p->rJD += n - Z;
+        p->validYMD = 0;
+        p->validHMS = 0;
+        rc = 0;
+      }
+      break;
+    }
+    case 's': {
+      /*
+      **    start of TTTTT
+      **
+      ** Move the date backwards to the beginning of the current day,
+      ** or month or year.
+      */
+      if( strncmp(z, "start of ", 9)!=0 ) break;
+      zMod = &z[9];
+      computeYMD(p);
+      p->validHMS = 1;
+      p->h = p->m = 0;
+      p->s = 0.0;
+      p->validTZ = 0;
+      p->validJD = 0;
+      if( strcmp(zMod,"month")==0 ){
+        p->D = 1;
+        rc = 0;
+      }else if( strcmp(zMod,"year")==0 ){
+        computeYMD(p);
+        p->M = 1;
+        p->D = 1;
+        rc = 0;
+      }else if( strcmp(zMod,"day")==0 ){
+        rc = 0;
+      }
+      break;
+    }
+    case '+':
+    case '-':
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9': {
+      n = getValue(z, &r);
+      if( n<=0 ) break;
+      zMod = &z[n];
+      while( isspace(zMod[0]) ) zMod++;
+      n = strlen(zMod);
+      if( n>10 || n<3 ) break;
+      strcpy(z, zMod);
+      if( z[n-1]=='s' ){ z[n-1] = 0; n--; }
+      computeJD(p);
+      rc = 0;
+      if( n==3 && strcmp(z,"day")==0 ){
+        p->rJD += r;
+      }else if( n==4 && strcmp(z,"hour")==0 ){
+        computeJD(p);
+        p->rJD += r/24.0;
+      }else if( n==6 && strcmp(z,"minute")==0 ){
+        computeJD(p);
+        p->rJD += r/(24.0*60.0);
+      }else if( n==6 && strcmp(z,"second")==0 ){
+        computeJD(p);
+        p->rJD += r/(24.0*60.0*60.0);
+      }else if( n==5 && strcmp(z,"month")==0 ){
+        int x, y;
+        computeYMD(p);
+        p->M += 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 = r;
+        if( y!=r ){
+          p->rJD += (r - y)*30.0;
+        }
+      }else if( n==4 && strcmp(z,"year")==0 ){
+        computeYMD(p);
+        p->Y += r;
+        p->validJD = 0;
+        computeJD(p);
+      }else{
+        rc = 1;
+      }
+      p->validYMD = 0;
+      p->validHMS = 0;
+      p->validTZ = 0;
+      break;
+    }
+    default: {
+      break;
+    }
+  }
+  return rc;
+}
+
+/*
+** Process time function arguments.  argv[0] is a date-time stamp.
+** argv[1] and following are modifiers.  Parse them all and write
+** the resulting time into the DateTime structure p.  Return 0
+** on success and 1 if there are any errors.
+*/
+static int isDate(int argc, const char **argv, DateTime *p){
+  int i;
+  if( argc==0 ) return 1;
+  if( parseDateOrTime(argv[0], p) ) return 1;
+  for(i=1; i<argc; i++){
+    if( parseModifier(argv[i], p) ) return 1;
+  }
+  return 0;
+}
+
+
+/*
+** The following routines implement the various date and time functions
+** of SQLite.
+*/
+
+/*
+**    julianday( TIMESTRING, MOD, MOD, ...)
+**
+** Return the julian day number of the date specified in the arguments
+*/
+static void juliandayFunc(sqlite_func *context, int argc, const char **argv){
+  DateTime x;
+  if( isDate(argc, argv, &x)==0 ){
+    computeJD(&x);
+    sqlite_set_result_double(context, x.rJD);
+  }
+}
+
+/*
+**    datetime( TIMESTRING, MOD, MOD, ...)
+**
+** Return YYYY-MM-DD HH:MM:SS
+*/
+static void datetimeFunc(sqlite_func *context, int argc, const char **argv){
+  DateTime x;
+  if( isDate(argc, argv, &x)==0 ){
+    char zBuf[100];
+    computeYMD(&x);
+    computeHMS(&x);
+    sprintf(zBuf, "%04d-%02d-%02d %02d:%02d:%02d",x.Y, x.M, x.D, x.h, x.m,
+           (int)(x.s));
+    sqlite_set_result_string(context, zBuf, -1);
+  }
+}
+
+/*
+**    time( TIMESTRING, MOD, MOD, ...)
+**
+** Return HH:MM:SS
+*/
+static void timeFunc(sqlite_func *context, int argc, const char **argv){
+  DateTime x;
+  if( isDate(argc, argv, &x)==0 ){
+    char zBuf[100];
+    computeHMS(&x);
+    sprintf(zBuf, "%02d:%02d:%02d", x.h, x.m, (int)x.s);
+    sqlite_set_result_string(context, zBuf, -1);
+  }
+}
+
+/*
+**    date( TIMESTRING, MOD, MOD, ...)
+**
+** Return YYYY-MM-DD
+*/
+static void dateFunc(sqlite_func *context, int argc, const char **argv){
+  DateTime x;
+  if( isDate(argc, argv, &x)==0 ){
+    char zBuf[100];
+    computeYMD(&x);
+    sprintf(zBuf, "%04d-%02d-%02d", x.Y, x.M, x.D);
+    sqlite_set_result_string(context, zBuf, -1);
+  }
+}
+
+/*
+**    strftime( FORMAT, TIMESTRING, MOD, MOD, ...)
+**
+** Return a string described by FORMAT.  Conversions as follows:
+**
+**   %d  day of month
+**   %f  ** fractional seconds  SS.SSS
+**   %H  hour 00-24
+**   %j  day of year 000-366
+**   %J  ** Julian day number
+**   %m  month 01-12
+**   %M  minute 00-59
+**   %s  seconds since 1970-01-01
+**   %S  seconds 00-59
+**   %w  day of week 0-6  sunday==0
+**   %W  week of year 00-53
+**   %Y  year 0000-9999
+**   %%  %
+*/
+static void strftimeFunc(sqlite_func *context, int argc, const char **argv){
+  DateTime x;
+  int n, i, j;
+  char *z;
+  const char *zFmt = argv[0];
+  char zBuf[100];
+  if( isDate(argc-1, argv+1, &x) ) return;
+  for(i=0, n=1; zFmt[i]; i++, n++){
+    if( zFmt[i]=='%' ){
+      switch( zFmt[i+1] ){
+        case 'd':
+        case 'H':
+        case 'm':
+        case 'M':
+        case 'S':
+        case 'W':
+          n++;
+          /* fall thru */
+        case 'w':
+        case '%':
+          break;
+        case 'f':
+          n += 8;
+          break;
+        case 'j':
+          n += 3;
+          break;
+        case 'Y':
+          n += 8;
+          break;
+        case 's':
+        case 'J':
+          n += 50;
+          break;
+        default:
+          return;  /* ERROR.  return a NULL */
+      }
+      i++;
+    }
+  }
+  if( n<sizeof(zBuf) ){
+    z = zBuf;
+  }else{
+    z = sqliteMalloc( n );
+    if( z==0 ) return;
+  }
+  computeJD(&x);
+  computeYMD(&x);
+  computeHMS(&x);
+  for(i=j=0; zFmt[i]; i++){
+    if( zFmt[i]!='%' ){
+      z[j++] = zFmt[i];
+    }else{
+      i++;
+      switch( zFmt[i] ){
+        case 'd':  sprintf(&z[j],"%02d",x.D); j+=2; break;
+        case 'f': {
+          int s = x.s;
+          int ms = (x.s - s)*1000.0;
+          sprintf(&z[j],"%02d.%03d",s,ms);
+          j += strlen(&z[j]);
+          break;
+        }
+        case 'H':  sprintf(&z[j],"%02d",x.h); j+=2; break;
+        case 'W': /* Fall thru */
+        case 'j': {
+          int n;
+          DateTime y = x;
+          y.validJD = 0;
+          y.M = 1;
+          y.D = 1;
+          computeJD(&y);
+          n = x.rJD - y.rJD + 1;
+          if( zFmt[i]=='W' ){
+            sprintf(&z[j],"%02d",(n+6)/7);
+            j += 2;
+          }else{
+            sprintf(&z[j],"%03d",n);
+            j += 3;
+          }
+          break;
+        }
+        case 'J':  sprintf(&z[j],"%.16g",x.rJD); j+=strlen(&z[j]); break;
+        case 'm':  sprintf(&z[j],"%02d",x.M); j+=2; break;
+        case 'M':  sprintf(&z[j],"%02d",x.m); j+=2; break;
+        case 's': {
+          sprintf(&z[j],"%d",(int)((x.rJD-2440587.5)*86400.0));
+          j += strlen(&z[j]);
+          break;
+        }
+        case 'S':  sprintf(&z[j],"%02d",(int)x.s); j+=2; break;
+        case 'w':  z[j++] = (((int)(x.rJD+1.5)) % 7) + '0'; break;
+        case 'Y':  sprintf(&z[j],"%04d",x.Y); j+=strlen(&z[j]); break;
+        case '%':  z[j++] = '%'; break;
+      }
+    }
+  }
+  z[j] = 0;
+  sqlite_set_result_string(context, z, -1);
+  if( z!=zBuf ){
+    sqliteFree(z);
+  }
+}
+
+
+#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
+** external linkage.
+*/
+void sqliteRegisterDateTimeFunctions(sqlite *db){
+  static struct {
+     char *zName;
+     int nArg;
+     int dataType;
+     void (*xFunc)(sqlite_func*,int,const char**);
+  } aFuncs[] = {
+#ifndef SQLITE_OMIT_DATETIME_FUNCS
+    { "julianday", -1, SQLITE_NUMERIC, juliandayFunc   },
+    { "date",      -1, SQLITE_TEXT,    dateFunc        },
+    { "time",       1, SQLITE_TEXT,    timeFunc        },
+    { "datetime",  -1, SQLITE_TEXT,    datetimeFunc    },
+    { "strftime",  -1, SQLITE_TEXT,    strftimeFunc    },
+#endif
+  };
+  int i;
+
+  for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
+    sqlite_create_function(db, aFuncs[i].zName,
+           aFuncs[i].nArg, aFuncs[i].xFunc, 0);
+    if( aFuncs[i].xFunc ){
+      sqlite_function_type(db, aFuncs[i].zName, aFuncs[i].dataType);
+    }
+  }
+}
index f3c4d1d853e027f907abcf8845f69c2acb1c237b..c33434e96901f403e02a9cba5875c205e88e746e 100644 (file)
@@ -16,7 +16,7 @@
 ** sqliteRegisterBuildinFunctions() found at the bottom of the file.
 ** All other code has file scope.
 **
-** $Id: func.c,v 1.32 2003/10/10 02:09:57 drh Exp $
+** $Id: func.c,v 1.33 2003/11/01 01:53:54 drh Exp $
 */
 #include <ctype.h>
 #include <math.h>
@@ -539,388 +539,6 @@ static void minMaxFinalize(sqlite_func *context){
   }
 }
 
-/****************************************************************************
-** Time and date functions.
-**
-** 1970-01-01 00:00:00 is JD 2440587.5.
-** 2000-01-01 00:00:00 is JD 2451544.5
-**
-** 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 November 24, 4714 B.C. according to the Gregorian
-** calendar system.
-**
-** 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 usually
-** 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 a timezone extension on the end of a datetime stamp.
-** The extension is of the form:
-**
-**        (+/-)HH:MM
-**
-** If the parse is successful, write the number of minutes
-** of change in *pnMin and return 0.  If a parser error occurs,
-** return 0.
-**
-** A missing specifier is not considered an error.
-*/
-static int parseTimezone(const char *zDate, int *pnMin){
-  int sgn = 0;
-  int nHr, nMn;
-  while( isspace(*zDate) ){ zDate++; }
-  *pnMin = 0;
-  if( *zDate=='-' ){
-    sgn = -1;
-  }else if( *zDate=='+' ){
-    sgn = +1;
-  }else{
-    return *zDate!=0;
-  }
-  zDate++;
-  nHr = getDigits(zDate, 2);
-  if( nHr<0 || nHr>14 ) return 1;
-  zDate += 2;
-  if( zDate[0]!=':' ) return 1;
-  zDate++;
-  nMn = getDigits(zDate, 2);
-  if( nMn<0 || nMn>59 ) return 1;
-  zDate += 2;
-  *pnMin = sgn*(nMn + nHr*60);
-  while( isspace(*zDate) ){ *zDate++; }
-  return *zDate!=0;
-}
-
-/*
-** Parse times of the form HH:MM or HH:MM:SS or HH:MM:SS.FFFF.
-** The HH, MM, and SS must each be exactly 2 digits.  The
-** fractional seconds FFFF can be one or more digits.
-**
-** The time string can be followed by an optional timezone specifier
-** of the following form:  (+/-)HH:MM.
-**
-** Whatever the format, the string is converted into a julian
-** day number and stored 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, tz;
-  double ms = 0.0;
-  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;
-    if( *zDate=='.' && isdigit(zDate[1]) ){
-      double rScale = 1.0/864000.0;
-      zDate++;
-      while( isdigit(*zDate) ){
-        ms += rScale * (*zDate - '0');
-        rScale *= 0.1;
-        zDate++;
-      }
-    }
-  }else{
-    s = 0;
-  }
-  if( parseTimezone(zDate, &tz) ) return 1;
-  *prJD = (h*3600.0 + (m+tz)*60.0 + s)/86400.0 + ms;
-  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 HH:MM:SS.FFF  +/-HH:MM
-**      DDDD.DD 
-**      now
-**
-** In the first form, the +/-HH:MM is always optional.  The fractional
-** seconds extension (the ".FFF") is optional.  The seconds portion
-** (":SS.FFF") is option.  The year and date can be omitted as long
-** as there is a time string.  The time string can be omitted as long
-** as there is a year and date.
-**
-** If the bRelative flag is set and the format is HH:MM or HH:MM:SS then
-** make the result is relative to midnight instead of noon.  In other words,
-** if bRelative is true, "00:00:00" parses to 0.0 but if bRelative is
-** false, "00:00:00" parses to 0.5.
-*/
-static int parseDateOrTime(const char *zDate, int bRelative, 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]==':' ){
-    if( parseHhMmSs(zDate, prJD) ) return 1;
-    if( !bRelative ) *prJD += 2451544.5;
-    return 0;
-  }else if( i==0 && sqliteStrICmp(zDate,"now")==0 ){
-    return sqliteOsCurrentTime(prJD);
-  }else if( sqliteIsNumber(zDate) ){
-    *prJD = atof(zDate);
-    return 0;
-  }
-  return 1;
-}
-
-/*
-** A structure for holding date and time.
-*/
-typedef struct DateTime DateTime;
-struct DateTime {
-  double rJD;    /* The julian day number */
-  int Y, M, D;   /* Year, month, and day */
-  int h, m;      /* Hour and minutes */
-  double s;      /* Seconds */
-};
-
-/*
-** Break up a julian day number into year, month, day, hour, minute, second.
-** This function assume the Gregorian calendar - even for dates prior
-** to the invention of the Gregorian calendar in 1582.
-**
-** See Meeus page 63.
-**
-** If mode==1 only the year, month, and day are computed.  If mode==2
-** then only the hour, minute, and second are computed.  If mode==3 then
-** everything is computed.  If mode==0, this routine is a no-op.
-*/
-static void decomposeDate(DateTime *p, int mode){
-  int Z;
-  Z = p->rJD + 0.5;
-  if( mode & 1 ){
-    int A, B, C, D, E, X1;
-    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;
-    p->D = B - D - X1;
-    p->M = E<14 ? E-1 : E-13;
-    p->Y = p->M>2 ? C - 4716 : C - 4715;
-  }
-  if( mode & 2 ){
-    int s = (p->rJD + 0.5 - Z)*86400000.0 + 0.5;
-    p->s = 0.001*s;
-    s = p->s;
-    p->s -= s;
-    p->h = s/3600;
-    s -= p->h*3600;
-    p->m = s/60;
-    p->s += s - p->m*60;
-  }
-}
-
-/*
-** Check to see that all arguments are valid date strings.  If any 
-** argument is not a valid date string, return 0.  If all arguments
-** are valid, return 1 and write into *p->rJD the sum of the julian day
-** numbers for all date strings.
-**
-** A "valid" date string is one that is accepted by parseDateOrTime().
-**
-** The mode argument is passed through to decomposeDate() in order to
-** fill in the year, month, day, hour, minute, and second of the *p
-** structure, if desired.
-*/
-static int isDate(int argc, const char **argv, DateTime *p, int mode){
-  double r;
-  int i;
-  p->rJD = 0.0;
-  for(i=0; i<argc; i++){
-    if( argv[i]==0 ) return 0;
-    if( parseDateOrTime(argv[i], i, &r) ) return 0;
-    p->rJD += r;
-  }
-  decomposeDate(p, mode);
-  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){
-  DateTime x;
-  if( isDate(argc, argv, &x, 0) ){
-    sqlite_set_result_double(context, x.rJD);
-  }
-}
-static void timestampFunc(sqlite_func *context, int argc, const char **argv){
-  DateTime x;
-  if( isDate(argc, argv, &x, 3) ){
-    char zBuf[100];
-    sprintf(zBuf, "%04d-%02d-%02d %02d:%02d:%02d",x.Y, x.M, x.D, x.h, x.m,
-           (int)(x.s+0.5));
-    sqlite_set_result_string(context, zBuf, -1);
-  }
-}
-static void timeFunc(sqlite_func *context, int argc, const char **argv){
-  DateTime x;
-  if( isDate(argc, argv, &x, 2) ){
-    char zBuf[100];
-    sprintf(zBuf, "%02d:%02d:%02d", x.h, x.m, (int)(x.s+0.5));
-    sqlite_set_result_string(context, zBuf, -1);
-  }
-}
-static void dateFunc(sqlite_func *context, int argc, const char **argv){
-  DateTime x;
-  if( isDate(argc, argv, &x, 1) ){
-    char zBuf[100];
-    sprintf(zBuf, "%04d-%02d-%02d", x.Y, x.M, x.D);
-    sqlite_set_result_string(context, zBuf, -1);
-  }
-}
-static void yearFunc(sqlite_func *context, int argc, const char **argv){
-  DateTime x;
-  if( isDate(argc, argv, &x, 1) ){
-    sqlite_set_result_int(context, x.Y);
-  }
-}
-static void monthFunc(sqlite_func *context, int argc, const char **argv){
-  DateTime x;
-  if( isDate(argc, argv, &x, 1) ){
-    sqlite_set_result_int(context, x.M);
-  }
-}
-static void dayofweekFunc(sqlite_func *context, int argc, const char **argv){
-  DateTime x;
-  if( isDate(argc, argv, &x, 0) ){
-    int Z = x.rJD + 1.5;
-    sqlite_set_result_int(context, Z % 7);
-  }
-}
-static void dayofmonthFunc(sqlite_func *context, int argc, const char **argv){
-  DateTime x;
-  if( isDate(argc, argv, &x, 1) ){
-    sqlite_set_result_int(context, x.D);
-  }
-}
-static void secondFunc(sqlite_func *context, int argc, const char **argv){
-  DateTime x;
-  if( isDate(argc, argv, &x, 2) ){
-    sqlite_set_result_double(context, x.s);
-  }
-}
-static void minuteFunc(sqlite_func *context, int argc, const char **argv){
-  DateTime x;
-  if( isDate(argc, argv, &x, 2) ){
-    sqlite_set_result_int(context, x.m);
-  }
-}
-static void hourFunc(sqlite_func *context, int argc, const char **argv){
-  DateTime x;
-  if( isDate(argc, argv, &x, 2) ){
-    sqlite_set_result_int(context, x.h);
-  }
-}
-static void unixToJdFunc(sqlite_func *context, int argc, const char **argv){
-  sqlite_set_result_double(context, atof(argv[0])/(24.0*3600.0)+2440587.5);
-}
-static void unixtimeFunc(sqlite_func *context, int argc, const char **argv){
-  DateTime x;
-  if( isDate(argc, argv, &x, 0) ){
-    sqlite_set_result_double(context, (x.rJD-2440587.5)*24.0*3600.0);
-  }
-}
-
-#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
@@ -954,21 +572,6 @@ void sqliteRegisterBuiltinFunctions(sqlite *db){
     { "nullif",     2, SQLITE_ARGS,    nullifFunc },
     { "sqlite_version",0,SQLITE_TEXT,  versionFunc},
     { "quote",      1, SQLITE_ARGS,    quoteFunc  },
-#ifndef SQLITE_OMIT_DATETIME_FUNCS
-    { "julianday", -1, SQLITE_NUMERIC, juliandayFunc   },
-    { "unixtime",  -1, SQLITE_NUMERIC, unixtimeFunc    },
-    { "unix_to_jd", 1, SQLITE_NUMERIC, unixToJdFunc    },
-    { "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
@@ -1010,4 +613,5 @@ void sqliteRegisterBuiltinFunctions(sqlite *db){
            aAggs[i].nArg, aAggs[i].xStep, aAggs[i].xFinalize, 0);
     sqlite_function_type(db, aAggs[i].zName, aAggs[i].dataType);
   }
+  sqliteRegisterDateTimeFunctions(db);
 }
index 11d14cc2110af13d21f5c625799871f505ee9f9d..b14eb28efadd4fd21550463f29ac4dc1fa0bcfb8 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.200 2003/10/18 09:37:26 danielk1977 Exp $
+** @(#) $Id: sqliteInt.h,v 1.201 2003/11/01 01:53:54 drh Exp $
 */
 #include "config.h"
 #include "sqlite.h"
@@ -1159,6 +1159,7 @@ IdList *sqliteIdListDup(IdList*);
 Select *sqliteSelectDup(Select*);
 FuncDef *sqliteFindFunction(sqlite*,const char*,int,int,int);
 void sqliteRegisterBuiltinFunctions(sqlite*);
+void sqliteRegisterDateTimeFunctions(sqlite*);
 int sqliteSafetyOn(sqlite*);
 int sqliteSafetyOff(sqlite*);
 int sqliteSafetyCheck(sqlite*);
diff --git a/test/date.test b/test/date.test
new file mode 100644 (file)
index 0000000..e1cc160
--- /dev/null
@@ -0,0 +1,123 @@
+# 2003 October 31
+#
+# 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.
+#
+# $Id: date.test,v 1.1 2003/11/01 01:53:54 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+proc datetest {tnum expr result} {
+  do_test date-$tnum [subst {
+    execsql "SELECT coalesce($expr,'NULL')"
+  }] [list $result]
+}
+
+datetest 1.1 julianday('2000-01-01') 2451544.5
+datetest 1.2 julianday('1970-01-01') 2440587.5
+datetest 1.3 julianday('1910-04-20') 2418781.5
+datetest 1.4 julianday('1986-02-09') 2446470.5
+datetest 1.5 julianday('12:00:00') 2451545
+datetest 1.6 {julianday('2000-01-01 12:00:00')} 2451545
+datetest 1.7 {julianday('2000-01-01 12:00')} 2451545
+datetest 1.8 julianday('bogus') NULL
+datetest 1.9 julianday('1999-12-31') 2451543.5
+datetest 1.10 julianday('1999-12-32') NULL
+datetest 1.11 julianday('1999-13-01') NULL
+datetest 1.12 julianday('2003-02-31') 2452701.5
+datetest 1.13 julianday('2003-03-03') 2452701.5
+datetest 1.14 julianday('+2000-01-01') NULL
+datetest 1.15 julianday('200-01-01') NULL
+datetest 1.16 julianday('2000-1-01') NULL
+datetest 1.17 julianday('2000-01-1') NULL
+datetest 1.18 {julianday('2000-01-01     12:00:00')} 2451545
+datetest 1.19 {julianday('2000-01-01 12:00:00.1')}   2451545.00000116
+datetest 1.20 {julianday('2000-01-01 12:00:00.01')}  2451545.00000012
+datetest 1.21 {julianday('2000-01-01 12:00:00.001')} 2451545.00000001
+datetest 1.22 {julianday('2000-01-01 12:00:00.')} NULL
+datetest 1.23 julianday(12345.6) 12345.6
+datetest 1.24 {julianday('2001-01-01 12:00:00 bogus')} NULL
+datetest 1.25 {julianday('2001-01-01 bogus')} NULL
+
+datetest 2.1 datetime(0,'unixepoch') {1970-01-01 00:00:00}
+datetest 2.2 datetime(946684800,'unixepoch') {2000-01-01 00:00:00}
+datetest 2.3 {date('2003-10-22','weekday 0')} 2003-10-26
+datetest 2.4 {date('2003-10-22','weekday 1')} 2003-10-27
+datetest 2.5 {date('2003-10-22','weekday 2')} 2003-10-28
+datetest 2.6 {date('2003-10-22','weekday 3')} 2003-10-22
+datetest 2.7 {date('2003-10-22','weekday 4')} 2003-10-23
+datetest 2.8 {date('2003-10-22','weekday 5')} 2003-10-24
+datetest 2.9 {date('2003-10-22','weekday 6')} 2003-10-25
+datetest 2.10 {date('2003-10-22','weekday 7')} NULL
+datetest 2.11 {date('2003-10-22','weekday 5.5')} NULL
+datetest 2.12 {datetime('2003-10-22 12:34','weekday 0')} {2003-10-26 00:00:00}
+datetest 2.13 {datetime('2003-10-22 12:34','start of month')} \
+   {2003-10-01 00:00:00}
+datetest 2.14 {datetime('2003-10-22 12:34','start of year')} \
+   {2003-01-01 00:00:00}
+datetest 2.15 {datetime('2003-10-22 12:34','start of day')} \
+   {2003-10-22 00:00:00}
+datetest 2.16 time('12:34:56.43') 12:34:56
+datetest 2.17 {datetime('2003-10-22 12:34','1 day')} {2003-10-23 12:34:00}
+datetest 2.18 {datetime('2003-10-22 12:34','+1 day')} {2003-10-23 12:34:00}
+datetest 2.19 {datetime('2003-10-22 12:34','+1.25 day')} {2003-10-23 18:34:00}
+datetest 2.20 {datetime('2003-10-22 12:34','-1.0 day')} {2003-10-21 12:34:00}
+datetest 2.21 {datetime('2003-10-22 12:34','1 month')} {2003-11-22 12:34:00}
+datetest 2.22 {datetime('2003-10-22 12:34','11 month')} {2004-09-22 12:34:00}
+datetest 2.23 {datetime('2003-10-22 12:34','-13 month')} {2002-09-22 12:34:00}
+datetest 2.24 {datetime('2003-10-22 12:34','1.5 months')} {2003-12-07 12:34:00}
+datetest 2.25 {datetime('2003-10-22 12:34','-5 years')} {1998-10-22 12:34:00}
+datetest 2.26 {datetime('2003-10-22 12:34','+10.5 minutes')} \
+  {2003-10-22 12:44:30}
+datetest 2.27 {datetime('2003-10-22 12:34','-1.25 hours')} \
+  {2003-10-22 11:19:00}
+datetest 2.28 {datetime('2003-10-22 12:34','11.25 seconds')} \
+  {2003-10-22 12:34:11}
+datetest 2.29 {datetime('2003-10-22 12:24','+5 bogus')} NULL
+
+
+datetest 3.1 {strftime('%d','2003-10-31 12:34:56.432')} 31
+datetest 3.2 {strftime('%f','2003-10-31 12:34:56.432')} 56.432
+datetest 3.3 {strftime('%H','2003-10-31 12:34:56.432')} 12
+datetest 3.4 {strftime('%j','2003-10-31 12:34:56.432')} 304
+datetest 3.5 {strftime('%J','2003-10-31 12:34:56.432')} 2452944.024264259
+datetest 3.6 {strftime('%m','2003-10-31 12:34:56.432')} 10
+datetest 3.7 {strftime('%M','2003-10-31 12:34:56.432')} 34
+datetest 3.8 {strftime('%s','2003-10-31 12:34:56.432')} 1067603696
+datetest 3.9 {strftime('%S','2003-10-31 12:34:56.432')} 56
+datetest 3.10 {strftime('%w','2003-10-31 12:34:56.432')} 5
+datetest 3.11 {strftime('%W','2003-10-31 12:34:56.432')} 44
+datetest 3.12 {strftime('%Y','2003-10-31 12:34:56.432')} 2003
+datetest 3.13 {strftime('%%','2003-10-31 12:34:56.432')} %
+datetest 3.14 {strftime('%_','2003-10-31 12:34:56.432')} NULL
+datetest 3.15 {strftime('%Y-%m-%d','2003-10-31')} 2003-10-31
+proc repeat {n txt} {
+  set x {} 
+  while {$n>0} {
+    append x $txt
+    incr n -1
+  }
+  return $x
+}
+datetest 3.16 "strftime('[repeat 200 %Y]','2003-10-31')" [repeat 200 2003]
+datetest 3.17 "strftime('[repeat 200 abc%m123]','2003-10-31')" \
+    [repeat 200 abc10123]
+
+set now [clock format [clock seconds] -format "%Y-%m-%d" -gmt 1]
+datetest 4.1 {date('now')} $now
+
+datetest 5.1 {datetime('1994-04-16 14:00:00 -05:00')} {1994-04-16 09:00:00}
+datetest 5.2 {datetime('1994-04-16 14:00:00 +05:15')} {1994-04-16 19:15:00}
+datetest 5.3 {datetime('1994-04-16 05:00:00 -08:30')} {1994-04-15 20:30:00}
+datetest 5.4 {datetime('1994-04-16 14:00:00 +11:55')} {1994-04-17 01:55:00}
+
+finish_test