# 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 \
$(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 \
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
# 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 \
$(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 \
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
-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
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
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
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
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
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
-181260c0aa7837feca9e415225ece0e9c4032c7a
\ No newline at end of file
+68ef9b45bd3abdedf3721011ad0fb22e8735e721
\ No newline at end of file
--- /dev/null
+/*
+** 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);
+ }
+ }
+}
** 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>
}
}
-/****************************************************************************
-** 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
{ "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
aAggs[i].nArg, aAggs[i].xStep, aAggs[i].xFinalize, 0);
sqlite_function_type(db, aAggs[i].zName, aAggs[i].dataType);
}
+ sqliteRegisterDateTimeFunctions(db);
}
*************************************************************************
** 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"
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*);
--- /dev/null
+# 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