引言
  众所周知,VC++是一个功能十分强大的应用程序开发工具,而Microsoft Access是一个中小型的数据库管理系统,可以处理多种数据信息。因此,在使用VC++链接ODBC中小型数据库的时候,常常选用Access作为数据库来源。实际应用中,VC++和Access的数字类型数据(如整形、浮点型和双精度型)进行链接的时候,简便易用,但是在VC++与Access数据库进行日期/时间类型数据的交换时,经常会出现一些意想不到的问题。例如:在VC++中存入一条记录的时候,VC++会自动为记录的时间字段加上日期;无法用通用的SQL语句同时对记录的日期和时间字段进行查找,等等。这类问题是由于VC++对日期/时间类型变量定义的缺陷而产生的。

  下面我们就对VC++和Access日期/时间的类型做一简要的说明,探讨产生的原因,并举例说明解决的方法。

  VC++和Access日期/时间类型的数据交换方式

  Visual C++ 的日期时间控件(Date Time Picker)是由一个静态控件加日历控件组成,用于让用户选择日期,也可以让用户选择输入时间。它的三种格式如下:

  (1) Short Date显示日期(格式9/10/1970)

  (2) Long Date显示日期(格式September 10,1970)

  (3) Time 显示时间(格式8:00:00)

  Microsoft Access为日期/时间数据提供了7种格式:通用日期 如9/19/97 14:32:10 PM;完整日期 如Wednesday,February 13,1997;中日期 如19-Jun-94;简短日期 如6/19/97;完整时间 如05:34:10PM;中时间 如05:34;短时间 如17:34。由此可见,除了通用日期,Microsoft Access的日期/时间字段可以分为两大类:"日期"字段和"时间"字段。

  我们在编制ODBC数据库的时候就可以将VC++中属性为Short Date(Long Date)日期时间控件与Access中的日期字段、VC++中属性为Time的日期时间控件和Access中的时间字段一一对应起来,很方便的实现数据的读取和其他操作。

  但是这时候问题出现了。我们常常需要在VC++中向数据库添加纪录,当加入一条新记录(如:日期9/19/99,时间18:30:00)后,打开Access数据库发现,在新添加记录中,时间字段显示为:70-1-1 18:30:00,也就是说VC++为每条添加记录的时间字段自动加上了日期-70年1月1日。这就为我们今后的程序设计带来了隐患。

  究其原因,原来是VC++将所有的CTime类型变量全部换算成秒来计算,它将70年1月1日0:00:00定义为0时刻,其后所有的日期全部换算为距70年1月1日0:00:00的秒数。因此就不难理解VC会自动为时间字段添加年月日了。但这时假如我们在VC++中用SQL语句对时间(如:18:30:00)进行查找,那我们到底是应该查找当前所在的时间(1999-9-9 18:30:00)呢,还是查70-1-1 18:30:00呢;如果我们要查找同时满足99年9月19日前后15天和18:30:00前后30分钟的记录,又该如何查找呢。下面我们讨论如何解决这个问题。

时间问题的修正
  现在,我们以一个实际的某舰船之间通讯的数据库为例,说明如何在应用中克服这个问题。在这个程序中,我们比较了数字类型变量与日期/时间类型变量在VC++和Access数据交换时的区别,并对时间类型变量存在的问题提出了相应的解决方法。


  本程序需要同时实现这样两个功能:


  (1)查找的时间范围:一天内给定时间前后T分钟,共计前后N天;


  (2)查找的位置范围:以给定A地为中心R为半径范围内的所有点作为A点,和以给定B地为中心R为半径范围内的所有点作为B点,且两点关联(是通信的双方)。


  本例所涉及的框图如图1所示:



图1 按时间查找对话框


  各编辑控件对应的变量分别为:m_Date,m_Time,m_ Longitude_A,m_Longitude_B,m_Latitude_A,m_Latitude_B,m_DD,m_MM,m_Rad。


  所涉及的CPP源程序如下:

m_Date1=m_Date;
 m_Date2=m_Date;
 m_Date1-=m_DD*86400; //每一天是86400秒
 m_Date2+=m_DD*86400; 
 m_Time1 = m_Time;
 m_Time2 = m_Time;
 m_Time1-=m_MM*60; //每分钟为60秒
 m_Time2+=m_MM*60;
 //用SQL语句进行查找,注意对时间字段的处理
 sqlstr.Format("(((年月日)>#%d/%d/%d# AND (年月日)<#%d/%d/%d#) AND\
  ((时分)>#1/1/70 %d:%d:%d# AND (时分)<#1/1/70 %d:%d:%d#) AND\
  (A点经度)<%f AND (A点经度)>%f AND (A点纬度)<%f AND\
  (A点纬度)>%f AND (B点经度)<%f AND (B点经度)>%f AND\
  (B点纬度)<%f AND (B点纬度)>%f)",
 m_Date1.GetMonth(),m_Date1.GetDay(),m_Date1.GetYear(),
 m_Date2.GetMonth(),m_Date2.GetDay(),m_Date2.GetYear(),
 m_Time1.GetHour(),m_Time1.GetMinute(),m_Time1.GetSecond(),
 m_Time2.GetHour(),m_Time2.GetMinute(),m_Time2.GetSecond(),
 m_Longitude_A+m_Rad,m_Longitude_A-m_Rad,m_Latitude_A+m_Rad,
 m_Latitude_A-m_Rad,m_Longitude_B+m_Rad,m_Longitude_B-m_Rad,
 m_Latitude_B+m_Rad,m_Latitude_B-m_Rad); 

 m_SmartSet.m_strFilter=sqlstr;
 m_SmartSet.m_strSort="分值 DESC"; //降序排列


  在程序的SQL语句中,我们为每一个时间字段的查找人为地加上70年1月1日(即70/1/1),而对于数字类型变量的查找用标准的SQL语句就可以了。为什么要加上70/1/1呢?因为正如前所述,在主界面中,当我们添加一条记录的时候,VC自动为记录的时间字段添加的日期是70/1/1。这样,在SQL语句中在时间字段人为地插入日期不但克服了VC存在的时间问题,而且对其它字段的查找没有任何影响,这种解决方案经编程实验证明是完全正确且可行的。



  最后用ClistBox类的AddString()等成员函数,就可以把所查到的记录在列表框中一一显示出来,查找的记录如图2所示,这里我们仅在列表框中显示了几条模拟的记录。图2中的排序是按照分值的大小降序排列的。




图2 查找记录结果



  

结束语



  本文提供了一种简便实用的方法,修正了VC++与Access数据库日期/时间类型变量数据交换存在的问题。应用VC++与其它数据库链接的时候可以采用类似的思想。