mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
Revised date/time functions - now broken out into a separate source file.
See the DateAndTimeFunctions wiki page for additional information. (CVS 1116) FossilOrigin-Name: 68ef9b45bd3abdedf3721011ad0fb22e8735e721
This commit is contained in:
@ -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
|
||||
|
||||
|
6
main.mk
6
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
|
||||
|
||||
|
20
manifest
20
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
|
||||
|
@ -1 +1 @@
|
||||
181260c0aa7837feca9e415225ece0e9c4032c7a
|
||||
68ef9b45bd3abdedf3721011ad0fb22e8735e721
|
773
src/date.c
Normal file
773
src/date.c
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
400
src/func.c
400
src/func.c
@ -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);
|
||||
}
|
||||
|
@ -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*);
|
||||
|
123
test/date.test
Normal file
123
test/date.test
Normal file
@ -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
|
Reference in New Issue
Block a user