Skip to content

Commit ec3bb1d

Browse files
committed
Add support for datetime fields
We add support for FoxPro datetime fields, which store an exact timestamp in two binary dwords, representing the Julian day number and the number of milliseconds since midnight, respectively. These are represented in userland as strings with the format "YYYYMMDDhhmmss.uuu", which conforms to ISO-8601. According to the current handling of date fields, the validity of input and output is not checked, i.e. it is possible to store invalid datetime values in the database. If necessary, respective checks have to be made by the programmer. To avoid reinventing the wheel, but also not to rely on ext/calendar, we add the respective functionality and adapt the names to avoid build conflicts. git-svn-id: http://svn.php.net/repository/pecl/dbase/trunk@340796 c90b9560-bf6c-de11-be94-00142212c4b1
1 parent 5f334ce commit ec3bb1d

30 files changed

+551
-54
lines changed

config.m4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ PHP_ARG_ENABLE(dbase,whether to enable dbase support,
77

88
if test "$PHP_DBASE" = "yes"; then
99
AC_DEFINE(DBASE,1,[ ])
10-
PHP_NEW_EXTENSION(dbase, dbf_head.c dbf_rec.c dbf_misc.c dbf_ndx.c dbase.c, $ext_shared)
10+
PHP_NEW_EXTENSION(dbase, dbf_head.c dbf_rec.c dbf_misc.c dbf_ndx.c dbase.c gregor.c, $ext_shared)
1111
fi

config.w32

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
ARG_ENABLE("dbase", "Enable the bundled dbase library", "no");
55

66
if (PHP_DBASE != "no") {
7-
EXTENSION("dbase", "dbase.c dbf_head.c dbf_misc.c dbf_ndx.c dbf_rec.c");
7+
EXTENSION("dbase", "dbase.c dbf_head.c dbf_misc.c dbf_ndx.c dbf_rec.c gregor.c");
88
AC_DEFINE('HAVE_DBASE', 1, 'dbase support');
99
ADD_FLAG("CFLAGS_DBASE", "/D DBASE=1");
1010
}

dbase.c

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,20 @@ static void php_dbase_put_record(INTERNAL_FUNCTION_PARAMETERS, int replace)
244244
}
245245

246246
convert_to_string(field);
247-
snprintf(t_cp, cur_f->db_flen+1, cur_f->db_format, Z_STRVAL_P(field));
247+
248+
switch (cur_f->db_type) {
249+
case 'T':
250+
{
251+
int jdn, msecs;
252+
253+
db_get_timestamp(Z_STRVAL_P(field), &jdn, &msecs);
254+
put_long(t_cp, jdn);
255+
put_long(t_cp + 4, msecs);
256+
}
257+
break;
258+
default:
259+
snprintf(t_cp, cur_f->db_flen+1, cur_f->db_format, Z_STRVAL_P(field));
260+
}
248261

249262
t_cp += cur_f->db_flen;
250263
}
@@ -355,7 +368,11 @@ static void php_dbase_get_record(INTERNAL_FUNCTION_PARAMETERS, int assoc)
355368
cursize = cur_f->db_flen + 1;
356369
fnp = erealloc(fnp, cursize);
357370
}
358-
snprintf(str_value, cursize, cur_f->db_format, get_field_val(data, cur_f, fnp));
371+
if (*cur_f->db_format) {
372+
snprintf(str_value, cursize, cur_f->db_format, get_field_val(data, cur_f, fnp));
373+
} else {
374+
memcpy(str_value, get_binary_field_val(data, cur_f, fnp), cur_f->db_flen);
375+
}
359376

360377
/* now convert it to the right php internal type */
361378
switch (cur_f->db_type) {
@@ -423,6 +440,18 @@ static void php_dbase_get_record(INTERNAL_FUNCTION_PARAMETERS, int assoc)
423440
case 'M':
424441
/* this is a memo field. don't know how to deal with this yet */
425442
break;
443+
case 'T':
444+
{
445+
char buf[19];
446+
447+
db_set_timestamp(buf, get_long(str_value), get_long(str_value + 4));
448+
if (!assoc) {
449+
add_next_index_string(return_value, buf);
450+
} else {
451+
add_assoc_string(return_value, cur_f->db_fname, buf);
452+
}
453+
}
454+
break;
426455
default:
427456
/* should deal with this in some way */
428457
break;
@@ -571,6 +600,7 @@ PHP_FUNCTION(dbase_create)
571600
/* should create the memo file here, probably */
572601
break;
573602
case 'D':
603+
case 'T':
574604
cur_f->db_flen = 8;
575605
break;
576606
case 'F':
@@ -734,14 +764,15 @@ PHP_FUNCTION(dbase_get_header_info)
734764

735765
/* field type */
736766
switch (cur_f->db_type) {
737-
case 'C': add_assoc_string(&row, "type", "character"); break;
738-
case 'D': add_assoc_string(&row, "type", "date"); break;
739-
case 'I': add_assoc_string(&row, "type", "integer"); break;
740-
case 'N': add_assoc_string(&row, "type", "number"); break;
741-
case 'L': add_assoc_string(&row, "type", "boolean"); break;
742-
case 'M': add_assoc_string(&row, "type", "memo"); break;
767+
case 'C': add_assoc_string(&row, "type", "character"); break;
768+
case 'D': add_assoc_string(&row, "type", "date"); break;
769+
case 'T': add_assoc_string(&row, "type", "datetime"); break;
770+
case 'I': add_assoc_string(&row, "type", "integer"); break;
771+
case 'N': add_assoc_string(&row, "type", "number"); break;
772+
case 'L': add_assoc_string(&row, "type", "boolean"); break;
773+
case 'M': add_assoc_string(&row, "type", "memo"); break;
743774
case 'F': add_assoc_string(&row, "type", "float"); break;
744-
default: add_assoc_string(&row, "type", "unknown"); break;
775+
default: add_assoc_string(&row, "type", "unknown"); break;
745776
}
746777

747778
/* length of field */

dbf_head.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ int get_dbf_field(dbhead_t *dbh, dbfield_t *dbf)
165165
dbf->db_flen = 1;
166166
break;
167167
case 'D':
168+
case 'T':
168169
dbf->db_flen = 8;
169170
break;
170171
default:
@@ -257,6 +258,9 @@ char *get_dbf_f_fmt(dbfield_t *dbf)
257258
case 'M':
258259
strlcpy(format, "%s", sizeof(format));
259260
break;
261+
case 'T':
262+
format[0] = '\0';
263+
break;
260264
default:
261265
return NULL;
262266
}

dbf_misc.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <sys/types.h>
99

1010
#include "dbf_misc.h"
11+
#include "dbf_sdncal.h"
1112

1213
#include "php_reentrancy.h"
1314

@@ -144,6 +145,33 @@ int db_date_day(char *cp)
144145
return day;
145146
}
146147

148+
void db_set_timestamp(char *cp, int jdn, int msecs)
149+
{
150+
int year, month, day, hour, minute, second, millis;
151+
152+
db_sdn_to_gregorian(jdn, &year, &month, &day);
153+
154+
millis = msecs % 1000;
155+
msecs /= 1000;
156+
second = msecs % 60;
157+
msecs /= 60;
158+
minute = msecs % 60;
159+
msecs /= 60;
160+
hour = msecs;
161+
162+
snprintf(cp, 19, "%04d%02d%02d%02d%02d%02d.%03d", year, month, day, hour, minute, second, millis);
163+
}
164+
165+
void db_get_timestamp(char *cp, int *jdn, int *msecs)
166+
{
167+
int year, month, day, hour, minute, second, millis;
168+
169+
sscanf(cp, "%04d%02d%02d%02d%02d%02d.%03d", &year, &month, &day, &hour, &minute, &second, &millis);
170+
171+
*jdn = db_gregorian_to_sdn(year, month, day);
172+
*msecs = (hour * 60 * 60 * 1000) + (minute * 60 * 1000) + (second * 1000) + millis;
173+
}
174+
147175
#include <time.h>
148176

149177
char *db_cur_date(char *cp)

dbf_misc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ extern int db_date_year(char *cp);
1111
extern int db_date_month(char *cp);
1212
extern int db_date_day(char *cp);
1313
extern char *db_cur_date(char *cp);
14+
void db_set_timestamp(char *cp, int jdn, int msecs);
15+
void db_get_timestamp(char *cp, int *jdn, int *msecs);

dbf_rec.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,16 @@ char *get_field_val(char *rp, dbfield_t *fldp, char *cp)
170170
return cp;
171171
}
172172

173+
char *get_binary_field_val(char *rp, dbfield_t *fldp, char *cp)
174+
{
175+
int flen = fldp->db_flen;
176+
177+
if ( !cp )
178+
cp = (char *)emalloc(flen);
179+
memcpy(cp, &rp[fldp->db_foffset], flen);
180+
return cp;
181+
}
182+
173183
void put_field_val(char *rp, dbfield_t *fldp, char *cp)
174184
{
175185
strncpy(&rp[fldp->db_foffset], cp, fldp->db_flen);

dbf_rec.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ extern int put_dbf_eof_marker(dbhead_t *dbh);
55
extern int del_dbf_record(dbhead_t *dbh, long rec_num);
66
int pack_dbf(dbhead_t *dbh);
77
extern char *get_field_val(char *rp, dbfield_t *fldp, char *cp);
8+
char *get_binary_field_val(char *rp, dbfield_t *fldp, char *cp);
89
void put_field_val(char *rp, dbfield_t *fldp, char *cp);
910
void out_rec(dbhead_t *dbh, dbfield_t *dbf, char *cp);
1011
extern int is_valid_rec(char *cp);

dbf_sdncal.h

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#ifndef DBF_SDNCAL_H
2+
#define DBF_SDNCAL_H
3+
/*
4+
* This code has been modified for use with PHP
5+
* by Shane Caraveo [email protected]
6+
* see below for more details
7+
*
8+
* This code has been adapted for the dbase extension
9+
* by Christoph M. Becker <[email protected]> *
10+
*/
11+
12+
/* $selId: sdncal.h,v 2.0 1995/10/24 01:13:06 lees Exp $
13+
* Copyright 1993-1995, Scott E. Lee, all rights reserved.
14+
* Permission granted to use, copy, modify, distribute and sell so long as
15+
* the above copyright and this permission statement are retained in all
16+
* copies. THERE IS NO WARRANTY - USE AT YOUR OWN RISK.
17+
*/
18+
19+
/**************************************************************************
20+
*
21+
* This package defines a set of routines that convert calendar dates to
22+
* and from a serial day number (SDN). The SDN is a serial numbering of
23+
* days where SDN 1 is November 25, 4714 BC in the Gregorian calendar and
24+
* SDN 2447893 is January 1, 1990. This system of day numbering is
25+
* sometimes referred to as Julian days, but to avoid confusion with the
26+
* Julian calendar, it is referred to as serial day numbers here. The term
27+
* Julian days is also used to mean the number of days since the beginning
28+
* of the current year.
29+
*
30+
* The SDN can be used as an intermediate step in converting from one
31+
* calendar system to another (such as Gregorian to Jewish). It can also
32+
* be used for date computations such as easily comparing two dates,
33+
* determining the day of the week, finding the date of yesterday or
34+
* calculating the number of days between two dates.
35+
*
36+
* When using this software on 16 bit systems, be careful to store SDNs in
37+
* a long int, because it will not fit in the 16 bits that some systems
38+
* allocate to an int.
39+
*
40+
* For each calendar, there are two routines provided. One converts dates
41+
* in that calendar to SDN and the other converts SDN to calendar dates.
42+
* The routines are named SdnTo<CALENDAR>() and <CALENDAR>ToSdn(), where
43+
* <CALENDAR> is the name of the calendar system.
44+
*
45+
* SDN values less than one are not supported. If a conversion routine
46+
* returns an SDN of zero, this means that the date given is either invalid
47+
* or is outside the supported range for that calendar.
48+
*
49+
* At least some validity checks are performed on input dates. For
50+
* example, a negative month number will result in the return of zero for
51+
* the SDN. A returned SDN greater than one does not necessarily mean that
52+
* the input date was valid. To determine if the date is valid, convert it
53+
* to SDN, and if the SDN is greater than zero, convert it back to a date
54+
* and compare to the original. For example:
55+
*
56+
* int y1, m1, d1;
57+
* int y2, m2, d2;
58+
* zend_long sdn;
59+
* ...
60+
* sdn = GregorianToSdn(y1, m1, d1);
61+
* if (sdn > 0) {
62+
* SdnToGregorian(sdn, &y2, &m2, &d2);
63+
* if (y1 == y2 && m1 == m2 && d1 == d2) {
64+
* ... date is valid ...
65+
* }
66+
* }
67+
*
68+
**************************************************************************/
69+
70+
#include "php.h"
71+
72+
/* Gregorian calendar conversions. */
73+
void db_sdn_to_gregorian(zend_long sdn, int *pYear, int *pMonth, int *pDay);
74+
zend_long db_gregorian_to_sdn(int year, int month, int day);
75+
76+
#endif /* DBF_SDNCAL_H */

0 commit comments

Comments
 (0)