解决C语言程序中的2000年问题 用户以往开发的计算机应用软件,当运行到2000年时,很可能将发生时间混乱。笔 者认为,应该从不同的角度来看待这个问题。从宏观的角度分析,以往开发的应用 软件若要全部顺利地适应新世纪日历记年,的确要花费巨大的人力和资金,其损失 和影响不可低估。然而 ,仅对某个应用软件系统的开发应用单位和个人而言,解决 这个问题并不会费多少周折,只需对原有的程序进行适当的修改即可。 以往的计算机应用系统从其应用范围和程序规模来说,可归纳为以下三类: (1)通用的支撑软件。如文字排版编辑软件、通用表格处理软件等。这类软件一般 均采用C语言开发。 (2)大、中型数据库管理软件。如大中型企业的数据库管理系统、通用财务数据处 理系统等。这类软件很多是采用C语言嵌套SQL查询语句(如Oracle数据库系统)的 方法开发。 (3)小型数据统计软件。这类软件主要是应用单位的软件开发者根据本单位的需求 ,结合实际开发的数据统计报表软件。这种小型应用软件大都采用Dbase、Foxbase 、Foxpro数据库系统编程。 上述三类应用软件若当初开发时没有考虑如何适应新世纪日历记年的特殊要求,程 序员就必须对其进行适当的修改,因为大多数应佑妹软件含有以日历为基础的用户 注册、备忘录、安全检测以及系统日历时钟显示功能。若不对其进行修改,进入21 世纪时这些应用软件将产生难以预测的混乱和错误。笔者在此以用C语言开发的软 件为例,介绍如何修改原有应用程序,以解决2000年问题的有关技术和方法。 首先分析C语言(以MS_C6.0 为例)的日历时间函数是如何处理日历记年信息的。 C语言编译系统提供了有关系统日历时钟调用的一组函数,程序员只要正确地使用 这些函数和嵌入文件并做适当的调整,就能够准确地获取和利用系统日历时钟信息 ,以适应21世纪的记年要求。 获取系统日历时钟编程(参见例程PROCTIME.C清单),应注意调用编译系统提供的嵌 入文件time.h。该文件中包含有关系统日历时钟调用函数以及结构数组tm的说明 。在tm结构中,定义了年(tm_year) 、 月(tm_mon)、日(tm_mday)、时(tm_hour) 、分(tm_min)、秒( tm_sec)以及星期标志(tm_wday)等存储单元。应当特别注意 的是,其中tm_year的定义说明 : "years since 1900",其含义就是time()及 aclock()等函数在处理记年计数时,只考虑2 0世纪内的年计数而不处理世纪的更 迭。 调用有关函数前必须对time.h文件中说明的tm结构缓冲区予以重定义,然后适时地 调用time()及aclock()等函数,将系统日历、时钟信息读出后存放到结构缓冲区 newtime中备用。 显示日历及时钟信息前,程序员应对结构缓冲区的日历、星期信息进行适当的中英 文转换和顺序排列,并对记年变量newtime->tm_year做特殊处理(YEAR=newtime- >tm_year+1 990),即可解决2000年的问题。若不这样处理,到2000年时,C程序中 tm_year变量的记年值会变成100。 本实例在应用程序的主循环体内,根据用户的按键值,以1秒钟为周期,将系统日历 及时钟信息显示到屏幕的适当座标上。 PROCTIME.C程序用 MS_C 6.0 编写,在286以上档次PC机的DOS环境下已运行通过, 详细说明参阅程序清单注释。 PROCTIME.C源程序清单: #include <time.h> #include <stdio.h> #include <bios.h> void goto_ xy(int x,int y) /* 设置光标位置 */ { union REGS r; r.h.ah=2; r.h.bh=0; r.h.dh=(char)x; r.h.dl=(char)y; int86(0x10,&r,&r); } get_key() /* 读键盘按键值 */ { union REGS r; r.h.ah=0; return int86(0x16,&r,&r); } int wherex() /* 读当前光标位置X座标值 */ { int x; union REGS r; r.h.ah=3; r.h.bh=0; int86(0x10,&r,&r); (char)x=r.h.dh; return x; } int wherey() /* 读当前光标位置Y座标值 */ { int y; union REGS r; r.h.ah=3; r.h.bh=0; int86(0x10,&r,&r); (char)y=r.h.dl; return y; } void write_video(int x,int y,char *p,int attrib) /*显示字符串*/ { register int I; union REGS r; for (I=y;*p;I++) { if (*p=='\n') { goto_ xy(x+1,2);break; } goto_ xy(x,I); r.h.ah=9; r.h.bh=0; r.x.cx=1; r.h.al=*p++; r.h.bl=(char)attrib; int86(0x10,&r,&r); } } void disptime(int x,int y) /*显示系统日历、时钟*/ { struct tm *newtime; /* 定义时间结构指针 */ time_t aclock; long startsec,currsec; int dqx,dqy,YEAR; char sj[50],week[3]; time(&aclock); newtime=localtime(&aclock); /* 将系统时钟读入结构缓冲区 */ YEAR=newtime->tm_year+1990 /* 记年变量处理 */ startsec=newtime->tm_sec; switch(newtime->tm_wday) /* 转换星期 */ { case 0: strcpy(week,"日"); break; case 1: strcpy(week,"一"); break; case 2: strcpy(week,"二"); break; case 3: strcpy(week,"三"); break; case 4: strcpy(week,"四"); break; case 5: strcpy(week,"五"); break; case 6: strcpy(week,"六"); break; } while(!kbhit()) /* 循环读取和组装系统时钟字符串 */ { time(&aclock); newtime=localtime(&aclock); sprintf(sj,"%d.%2d.%2d 星期%s %d:%2d:%2d",YEAR,newtime->tm_mon+1,newtime- >tm_mday,week,newtime->tm_hour,newtime->tm_min,newtime->tm_sec); currsec=newtime->tm_sec; if(startsec!=currsec) /* 以1秒为周期显示系统日历时钟 */ { dqx=wherex(); dqy=wherey(); write_video(x,y,sj,0x0a); goto_ xy(dqx,dqy); return; } } } main () { union inkey { char ch[2]; int I; }c; for (;;) { for(;;) { if (kbhit()) { c.I=get_key(); break; }/* 无按键时显示系统时钟 */ disptime(0,54); if (c.ch[0]==27) exit(0); /* 按ESC键时退出 */ } } }