Skip to Content.
Sympa Menu

freetds - Date patch

freetds AT lists.ibiblio.org

Subject: FreeTDS Development Group

List archive

Chronological Thread  
  • From: tetherow AT nol.org
  • To: freetds AT franklin.oit.unc.edu
  • Subject: Date patch
  • Date: Tue, 18 Jul 2000 14:04:23 -0500 (CDT)

As eluded to weeks ago, here is the date fix for the tds layer. It
should be transparent to users except that dates < 1902 and > 2036
should now be working. I have also added tdsset_date_format which will
allow you to change the string format for dates as returned by the
tds_convert functions. The tdsset_date_format function takes the same
type of format definition as strftime (as that is how it formats the
output).


------------------------------------------------------------------------
Sam Tetherow tetherow AT nol.org
Director of Development
Nebrask@ Online http://www.nol.org/
Index: src/tds/convert.c
===================================================================
RCS file: /Repository/freetds/src/tds/convert.c,v
retrieving revision 1.41
diff -c -r1.41 convert.c
*** convert.c 2000/06/06 04:33:10 1.41
--- convert.c 2000/07/18 18:53:08
***************
*** 197,202 ****
--- 197,223 ----
}
}

+ /**
+ * tds_convert_char - Convert/copy SYBCHAR/SYBVARCHAR.
+ * @srctype: Source element type. Should be SYBCHAR, SYBVARCHAR,
SYBNVARCHAR
+ * @src: Source element
+ * @srclen: Length of source element
+ * @desttype: Destination element type
+ * @dest: Destination element
+ * @destlen: The size of the destination element
+ *
+ * Convert/copy SYBCHAR, SYBVARCHAR, SYBNVARCHAR to either another SYBCHAR,
+ * SYBVARCHAR, SYBNVARCHAR or SYBINT1, SYBINT2, SYBINT4, SYBFLOAT8, SYBREAL,
+ * SYBBIT, SYBDATETIME, SYBDATETIME4
+ *
+ * TODO:
+ * Various bounds checking.
+ * Do we want to return TDS_FAIL if srctype not SYBCHAR, SYBVARCHAR,
SYBNVARCHAR
+ * Do we want to return TDS_FAIL if desttype not covered
+ * Implementations for SYBTEXT, SYBBINARY, SYBIMAGE, SYBMONEY4, SYBMONEY
+ * SYBNUMERIC, SYBDECIMAL
+ **/
+
TDS_INT tds_convert_char(int srctype,unsigned char *src,
int desttype,unsigned char *dest,TDS_INT destlen)
{
***************
*** 238,254 ****
case SYBMONEY4:
break;
case SYBDATETIME:
! _string_to_tm(src, &t);
! secs_from_epoch = mktime(&t);
! any.dt.dtdays = (secs_from_epoch/60/60/24)+25567;
! any.dt.dttime = (secs_from_epoch%60%60%24)*300;
! break;
! case SYBDATETIME4:
! _string_to_tm(src, &t);
! secs_from_epoch = mktime(&t);
! any.dt4.days = (secs_from_epoch/60/60/24)+25567;
! any.dt4.minutes = (secs_from_epoch%60%60%24)/60;
! break;
case SYBNUMERIC:
case SYBDECIMAL:
break;
--- 259,276 ----
case SYBMONEY4:
break;
case SYBDATETIME:
! /* Current implementation is off by one day
for dates < 1970-01-01 */
! /* This converts from MM.DD.YY HH.mm.SS.sss
to SYBDATETIME */
! _string_to_tm(src, &t);
!
any.dt.dtdays=t.tm_year*365+number_of_leap_years(t.tm_year+1899)+julian(t.tm_year+1900,
t.tm_mon, t.tm_mday);
!
any.dt.dttime=t.tm_hour*60*60+t.tm_min*60+t.tm_sec*300;
! break;
! case SYBDATETIME4:
! /* TODO: Need to bounds check here */
! _string_to_tm(src, &t);
!
any.dt4.days=t.tm_year*365+number_of_leap_years(t.tm_year-1899)+julian(t.tm_year+1900,
t.tm_mon, t.tm_mday);
!
any.dt4.minutes=t.tm_hour*60*60+t.tm_min*60+t.tm_sec*300;
! break;
case SYBNUMERIC:
case SYBDECIMAL:
break;
***************
*** 479,566 ****
break;
}
}
TDS_INT tds_convert_datetime(int srctype,unsigned char *src,int
desttype,unsigned char *dest,TDS_INT destlen)
{
! TDS_INT dtdays, dttime;
! time_t tmp_secs_from_epoch;
!
! switch(desttype) {
! case SYBCHAR:
! case SYBVARCHAR:
! /* FIX ME -- This fails for dates before 1902 or after 2038 */
! if (destlen<0) {
! memset(dest,' ',30);
! } else {
! memset(dest,' ',destlen);
! }
! if (!src) {
! *dest='\0';
! return 0;
}
- memcpy(&dtdays, src, 4);
- memcpy(&dttime, src+4, 4);
- tmp_secs_from_epoch = ((dtdays - 25567)*24*60*60) + (dttime/300);
- if (strlen(src)>destlen) {
- strftime(dest, destlen-1, "%b %d %Y %I:%M%p",
- (struct tm*)gmtime(&tmp_secs_from_epoch));
- return destlen;
- } else {
- strftime(dest, 20, "%b %d %Y %I:%M%p",
- (struct tm*)gmtime(&tmp_secs_from_epoch));
- return (strlen(dest));
- }
- break;
- case SYBDATETIME:
- memcpy(dest,src,sizeof(TDS_DATETIME));
- return sizeof(TDS_DATETIME);
- break;
- case SYBDATETIME4:
- break;
- default:
- return TDS_FAIL;
- break;
- }
}
TDS_INT tds_convert_datetime4(int srctype,unsigned char *src,int
desttype,unsigned char *dest,TDS_INT destlen)
{
! TDS_USMALLINT days, minutes;
! time_t tmp_secs_from_epoch;
!
! switch(desttype) {
! case SYBCHAR:
! case SYBVARCHAR:
! if (destlen<0) {
! memset(dest,' ',30);
! } else {
! memset(dest,' ',destlen);
! }
! if (!src) {
! *dest='\0';
! return 0;
! }
! memcpy(&days, src, 2);
! memcpy(&minutes, src+2, 2);
! tdsdump_log("%L inside tds_convert_datetime4() days = %d minutes =
%d\n", days, minutes);
! tmp_secs_from_epoch = (days - 25567)*(24*60*60) + (minutes*60);
! if (strlen(src)>destlen) {
! strftime(dest, destlen-1, "%b %d %Y %I:%M%p",
! (struct tm*)gmtime(&tmp_secs_from_epoch));
! return destlen;
! } else {
! strftime(dest, 20, "%b %d %Y %I:%M%p",
! (struct tm*)gmtime(&tmp_secs_from_epoch));
! return (strlen(dest));
}
- break;
- case SYBDATETIME:
- break;
- case SYBDATETIME4:
- memcpy(dest,src,sizeof(TDS_DATETIME4));
- return(sizeof(TDS_DATETIME4));
- default:
- return TDS_FAIL;
- break;
- }
}
TDS_INT tds_convert_real(int srctype,unsigned char *src,int
desttype,unsigned
char *dest,TDS_INT destlen)
--- 501,804 ----
break;
}
}
+
+ /**
+ * is_leap_year - Determine if a given year is a leap year
+ * @year: Year in century format CCYY
+ *
+ * If year is a leap year return one otherwise return zero
+ **/
+ int is_leap_year(int year) {
+ if (year%4!=0) return(0);
+ if (year%400==0) return(1);
+ if (year%100==0) return(0);
+ return(1);
+ }
+
+
+ /**
+ * number_of_leap_years - Determine number of leap years between 1900 and
year
+ * @year: Year in century format CCYY
+ *
+ * Return the number of leap years between 1900 and year
+ **/
+ int number_of_leap_years(int year) {
+ int leapyears;
+ int actualyear;
+
+ actualyear=year+1900;
+
leapyears=(int)(actualyear/4)-(int)(actualyear/100)+(int)(actualyear/400);
+ return(leapyears);
+ }
+
+ /**
+ * days_in_month - Determine the number of days in a month
+ * @month: month [0..11]
+ * @leapyear: 1 if a leapyear, 0 otherwise
+ *
+ * Return the number of days in a given month
+ **/
+ int days_in_month(int month, int leapyear) {
+ switch(month+1) {
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ case 8:
+ case 10:
+ case 12:
+ return(31);
+ break;
+ case 2: return(28+leapyear);
+ break;
+ case 4:
+ case 6:
+ case 9:
+ case 11:
+ return(30);
+ break;
+ default:
+ return(0);
+ }
+ }
+
+ /**
+ * julian - return the julian date for a give date
+ * @year: year in century format CCYY
+ * @month: month [0..11]
+ * @day: day [1..31]
+ *
+ * Return the julian day portion for a given date
+ **/
+ int julian(int year, int month, int day) {
+ int days=0;
+ int leapyear;
+ int x;
+ const int jday[]={0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304,
334};
+ const int ljday[]={0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305,
335};
+
+ if (is_leap_year(year)) {
+ return(ljday[month]+day-1);
+ } else {
+ return(ljday[month]+day-1);
+ }
+ }
+
+ /**
+ * compute_date - compute date from number of days since 1900
+ * @days: number of days since 1900
+ * @datetime: struct tm to contain date
+ *
+ * Compute the date give the number of days since 1900. Will handle
negative
+ * days as well for dates < 1900. Will handle full percision DATETIME8 from
+ * 0000-01-01 to 5881510-07-12
+ *
+ * TODO: - Figure out what standard date format for years BC is and we can
+ * handle back to about 6 million BC while we are at it.
+ * - Need to look into division to speed things up but not sure where
the
+ * tradeoff is between looping and division plus leapyear
calculation.
+ **/
+ int compute_date(TDS_INT days, struct tm *datetime) {
+ int leapyear=0;
+ int dom;
+ int year;
+ const int t[]={0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
+
+ if (days>=0) {
+ datetime->tm_year=1900;
+ leapyear=0;
+ datetime->tm_mon=0;
+ datetime->tm_mday=1;
+
+ while(days>=365+leapyear) {
+ days-=(365+leapyear);
+ datetime->tm_year++;
+ leapyear=is_leap_year(datetime->tm_year);
+ }
+ while(days>0) {
+ dom=days_in_month(datetime->tm_mon, leapyear);
+ if (days<dom) break;
+ days-=dom;
+ datetime->tm_mon++;
+ }
+ datetime->tm_mday+=days;
+ }
+ else {
+ datetime->tm_year=1899;
+ leapyear=0;
+ datetime->tm_mon=11;
+ datetime->tm_mday=31;
+
+ days*=-1;
+ days-=1;
+ while(days>365+leapyear) {
+ days-=(365+leapyear);
+ datetime->tm_year--;
+ leapyear=is_leap_year(datetime->tm_year);
+ }
+ while(1) {
+ dom=days_in_month(datetime->tm_mon, leapyear);
+ if (days<dom) break;
+ days-=dom;
+ datetime->tm_mon--;
+ }
+ datetime->tm_mday=dom-days;
+ }
+ /* Compute day of week */
+
+ year=datetime->tm_year;
+ year-=datetime->tm_mon<2;
+
datetime->tm_wday=((year+year/4-year/100+year/400+t[datetime->tm_mon]+datetime->tm_mday)%7);
+ datetime->tm_yday=julian(datetime->tm_year, datetime->tm_mon,
datetime->tm_mday);
+ datetime->tm_year-=1900;
+ return(1);
+ }
+
+ /**
+ * compute_time - compute time from 300ths seconds since midnight
+ * @secs: 300ths seconds since midnight
+ * @datetime: struct tm to contain time
+ *
+ * Convert 300th's seconds since midnight to standard hour, minute, seconds
+ * and store it in a struct tm
+ *
+ * TODO: Check to see if division and mod would be faster.
+ **/
+ int compute_time(TDS_INT secs, struct tm *datetime) {
+ datetime->tm_hour=0;
+ datetime->tm_min=0;
+ datetime->tm_sec=0;
+
+ secs/=300; /* Reported in 300th's of second */
+ if (secs>=0) {
+ while(secs>=3600) {
+ secs-=3600;
+ datetime->tm_hour++;
+ }
+ while(secs>=60) {
+ secs-=60;
+ datetime->tm_min++;
+ }
+ datetime->tm_sec=secs;
+ }
+ else {
+ datetime->tm_hour=23;
+ datetime->tm_min=59;
+ secs*=-1;
+ while(secs>=3600) {
+ secs-=3600;
+ datetime->tm_hour--;
+ }
+ while(secs>=60) {
+ secs-=60;
+ datetime->tm_min--;
+ }
+ datetime->tm_sec=60-secs;
+ }
+ }
+
+ /**
+ * tds_convert_datetime - Convert/copy a SYBDATETIME element.
+ * @srctype: Source element type. Should be SYBDATETIME
+ * @src: Source element
+ * @desttype: Destination element type
+ * @dest: Destination element
+ * @destlen: The size of the destination element
+ *
+ * Convert/copy SYBDATETIME to SYBCHAR, SYBVARCHAR, SYBDATETIME or
SYBDATETIME4
+ *
+ * TODO:
+ * Do we want to return TDS_FAIL if srctype is not SYBDATETIME
+ * SYBDATETIME4 should do a bounds check and copy if fits fail otherwise.
+ **/
TDS_INT tds_convert_datetime(int srctype,unsigned char *src,int
desttype,unsigned char *dest,TDS_INT destlen)
{
! TDS_INT dtdays, dttime;
! struct tm datetime;
! char datestr[20];
!
! switch(desttype) {
! case SYBCHAR:
! case SYBVARCHAR:
! if (destlen<0) {
! /* What is valid for destlen? One would
think this would core */
! memset(dest,' ',30);
! } else {
! memset(dest,' ',destlen);
! }
! if (!src) {
! *dest='\0';
! return 0;
! }
! memcpy(&dtdays, src, 4);
! memcpy(&dttime, src+4, 4);
! compute_date(dtdays, &datetime);
! compute_time(dttime, &datetime);
! strftime(dest, destlen, tdsget_date_format(),
&datetime);
! /* Return includes terminating NULL */
! return(strlen(dest)+1);
! case SYBDATETIME:
! memcpy(dest,src,sizeof(TDS_DATETIME));
! return sizeof(TDS_DATETIME);
! break;
! case SYBDATETIME4:
! /* Bounds check and copy here ? */
! break;
! default:
! return TDS_FAIL;
! break;
}
}
+ /**
+ * tds_convert_datetime4 - Convert/copy a SYBDATETIME4 element.
+ * @srctype: Source element type. Should be SYBDATETIME4
+ * @src: Source element
+ * @desttype: Destination element type
+ * @dest: Destination element
+ * @destlen: The size of the destination element
+ *
+ * Convert/copy SYBDATETIME4 to SYBCHAR, SYBVARCHAR, SYBDATETIME or
SYBDATETIME4
+ *
+ * TODO:
+ * Do we want to return TDS_FAIL if srctype is not SYBDATETIME4
+ **/
TDS_INT tds_convert_datetime4(int srctype,unsigned char *src,int
desttype,unsigned char *dest,TDS_INT destlen)
{
! TDS_USMALLINT days, minutes;
! struct tm datetime;
!
! switch(desttype) {
! case SYBCHAR:
! case SYBVARCHAR:
! if (destlen<0) {
! /* What is valid for destlen? One would
think this would core */
! memset(dest,' ',30);
! } else {
! memset(dest,' ',destlen);
! }
! if (!src) {
! *dest='\0';
! return 0;
! }
! memcpy(&days, src, 2);
! memcpy(&minutes, src+2, 2);
! tdsdump_log("%L inside tds_convert_datetime4() days =
%d minutes = %d\n", days, minutes);
! compute_date(days, &datetime);
! compute_time(minutes, &datetime);
! strftime(dest, destlen-1, tdsget_date_format(),
&datetime);
! /* Return includes terminating NULL */
! return(strlen(dest)+1);
! case SYBDATETIME:
! /* Shouldn't we be able to copy here since we are
going to higher
! percision */
! break;
! case SYBDATETIME4:
! memcpy(dest,src,sizeof(TDS_DATETIME4));
! return(sizeof(TDS_DATETIME4));
! default:
! return TDS_FAIL;
! break;
}
}
TDS_INT tds_convert_real(int srctype,unsigned char *src,int
desttype,unsigned
char *dest,TDS_INT destlen)
***************
*** 676,681 ****
--- 914,936 ----
}
return TDS_FAIL;
}
+
+ /**
+ * tds_convert - Convert/copy a SYB element
+ * @srctype: Source element type
+ * @src: Source element
+ * @desttype: Destination element type
+ * @dest: Destination element
+ * @destlen: The size of the destination element
+ *
+ * Convienence function to conver from one type to another. Currently
handles
+ * source types of SYBCHAR, SYBVARCHAR, SYBNVARCHAR, SYBMONEY, SYBMONEY4,
+ * SYBNUMERIC, SYBBIT, SYBINT1, SYBINT2, SYBINT4, SYBREAL, SYBFLT8,
SYBDATETIME,
+ * SYBDATETIME4, SYBVARBINARY, SYBBINARY, SYBTEXT
+ *
+ * TODO:
+ * Complete mappings for all types to other possible types.
+ **/
TDS_INT tds_convert(int srctype,
TDS_CHAR *src,
TDS_UINT srclen,
***************
*** 700,705 ****
--- 955,972 ----
return tds_convert_money(srctype,src,
desttype,dest,destlen);
break;
+ case SYBMONEYN:
+ if (srclen==4) {
+ return(tds_convert_money4(SYBMONEY4, src,
srclen, desttype, dest, destlen));
+ }
+ else if (srclen==8) {
+ return(tds_convert_money(SYBMONEY, src,
desttype, dest, destlen));
+ }
+ else if (srclen==0) {
+ memset(dest, 0, destlen);
+ return(TDS_FAIL);
+ }
+ else return(TDS_FAIL);
case SYBNUMERIC:
return tds_convert_numeric(srctype,(TDS_NUMERIC *)
src,srclen,
desttype,dest,destlen);
***************
*** 720,725 ****
--- 987,1010 ----
return tds_convert_int4(srctype,src,
desttype,dest,destlen);
break;
+ case SYBINTN:
+ if (srclen==1) {
+ return tds_convert_int1(SYBINT1, src,
desttype, dest, destlen);
+ }
+ else if (srclen==2) {
+ return tds_convert_int2(SYBINT2, src,
desttype, dest, destlen);
+ }
+ else if (srclen==4) {
+ return tds_convert_int4(SYBINT4, src,
desttype, dest, destlen);
+ }
+ else if (srclen==0) {
+ memset(dest, 0, destlen);
+ return(TDS_FAIL);
+ }
+ else {
+ return(TDS_FAIL);
+ }
+ break;
case SYBREAL:
return tds_convert_real(srctype,src,
desttype,dest,destlen);
***************
*** 736,741 ****
--- 1021,1038 ----
return tds_convert_datetime4(srctype,src,
desttype,dest,destlen);
break;
+ case SYBDATETIMN:
+ if (srclen==8) {
+ return(tds_convert_datetime(SYBDATETIME, src,
desttype, dest, destlen));
+ }
+ else if (srclen==4) {
+ return(tds_convert_datetime4(SYBDATETIME4,
src, desttype, dest, destlen));
+ }
+ else if (srclen==0) {
+ memset(dest, 0, destlen);
+ return(TDS_FAIL);
+ }
+ else return(TDS_FAIL);
case SYBVARBINARY:
varbin = (TDS_VARBINARY *)src;
return tds_convert_binary(srctype,varbin->array,
Index: src/tds/login.c
===================================================================
RCS file: /Repository/freetds/src/tds/login.c,v
retrieving revision 1.24
diff -c -r1.24 login.c
*** login.c 2000/03/13 12:56:47 1.24
--- login.c 2000/07/18 18:53:15
***************
*** 139,144 ****
--- 139,147 ----
/* end */


+ /* Set the default date format. I don't think this should really
occur
+ ** here but it had to go someplace */
+ tdsset_date_format("%b %d %Y %i:%M%p");
memcpy(tds->capabilities,login->capabilities,TDS_MAX_CAPABILITY);
if (!login->port) {
get_server_info(login->server_name, ip_addr, ip_port,
tds_ver);
Index: src/tds/util.c
===================================================================
RCS file: /Repository/freetds/src/tds/util.c,v
retrieving revision 1.14
diff -c -r1.14 util.c
*** util.c 2000/03/13 12:56:48 1.14
--- util.c 2000/07/18 18:53:17
***************
*** 50,55 ****
--- 50,57 ----


static char interf_file[MAXPATH];
+ /* More of the evil static variables - Not thread safe should go in a
context */
+ static char *tds_date_format=NULL;

void tds_set_parent(TDSSOCKET* tds, void* the_parent)
{
***************
*** 587,591 ****
--- 589,615 ----
#endif
};
/* Jeff's hack ***NEW CODE END**** */
+
+ /**
+ * tdsset_date_format - Set the strftime style date format string.
+ * @datefmt: date format
+ *
+ * Set the strftime style date format string to be used for datetime to
string
+ * conversions.
+ **/
+ void tdsset_date_format(const char *datefmt) {
+ if (tds_date_format!=NULL) free(tds_date_format);
+ tds_date_format=strdup(datefmt);
+ }
+
+ /**
+ * tdsget_date_format - Return the strftime style date format string
+ *
+ * Return the strftime style date format string. The format can be set with
+ * tdsset_date_format(datefmt)
+ **/
+ char *tdsget_date_format() {
+ return(tds_date_format);
+ }




  • Date patch, tetherow, 07/18/2000

Archive powered by MHonArc 2.6.24.

Top of Page