时间和日历类的设计
C++通用框架的设计作者:naven
1介绍
时间和日历以及时间的格式化处理在软件的设计中起着非常重要的作用,但是目前C++的库却未有一个简单易用的时间类,大部分都需要开发者直接调用操作系统的API来完成,而且很多API都不是线程安全的。某些大型的C++框架虽然提供一些时间类,但是却不通用,也很难直接拿出来使用。下面介绍一下参考Java Framework中的时间相关的类来设计并实现C++版本的时间和日历类。
主要有如下一些类
Time类,对应于Java的java.util.Date类,表示特定的瞬间,精确到毫秒(Linux可精确到微秒,Solaris可精确到十亿分之一秒)。Time只表示某时某地的瞬间,从1970年1月1日00:00:00 GMT以来的微秒数,无时区。
Calendar类,对应于Java的java.util.Calendar类,它既表示了Time的精确瞬间,还代表了此时的年、月、日、时区等。它为特定瞬间与一组诸如YEAR、MONTH、DAY_OF_MONTH、HOUR等日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。瞬间可用微秒值来表示,它是距历元(即格林威治标准时间1970 年 1月1日的00:00:00.000,GMT)的偏移量。
TimeFormat类,类似Java的java.text.SimpleDateFormat类,它用来将时间和日历格式化成一个本地时间格式的文本形式,或者指定格式的文本形式。如果格式化时间,将缺省使用操作系统设定的本地(locale)时间格式处理。
TimeParser类,类似Java的java.text.SimpleDateFormat类,与TimeFormat相反,它用来将一个时间的文本转化成一个本地时间Time,缺省使用操作系统设定的本地时间格式处理。
2Hello World!
下面介绍一下它们的使用
voidmain()
{
//获取当前时间,精确到微秒Time t=Time::getCurrentTime();
//使用CRT的API格式化时间,只能精确到秒time_t tt=t.sec();
printf("\n%s\n", ctime(&tt));
//使用系统缺省的locale输出格式化时间文本TimeFormat tf("%c");
printf("\n%s\n", tf.format(t).c_str());
//使用系统缺省的locale输出格式化时间文本TimeFormat tf2("%#c");
printf("\n%s\n", tf2.format(t).c_str());
//自定义输出格式化时间文本,精确到豪秒TimeFormat tff("%Y-%m-%d %H:%M:%S.%q");
printf("\n%s\n", tf2.format(t).c_str());
//自定义输出格式化时间文本,精确到豪秒,输出所有时间信息,包括时区和年代等TimeFormat tfCN("%G %Y年%B%d日 %A %H时%M分%S秒%q豪秒 时区%z","zh_CN.gb2312");
printf("\n%s\n", tfCN.format(t).c_str());
//使用自定义 zh_CN.gb2312 的locale和字符集输出格式化时间文本,不受系统locale影响TimeFormat tfCN2("%G %Y %b %d %a %H:%M:%S.%q %z","zh_CN.gb2312");
printf("\n%s\n", tfCN2.format(t).c_str());
//使用自定义 en_US.iso8859-1 的locale和字符集输出格式化时间文本,不受系统locale影响TimeFormat tfUS("%G %d %B %Y %A %H:%M:%S.%q TZ:%z","en_US.iso8859-1");
printf("\n%s\n", tfUS.format(t).c_str());
TimeFormat tfUS2("%G %Y %b %d %a %H:%M:%S.%q %z","en_US.iso8859-1");
printf("\n%s\n", tfUS2.format(t).c_str());
//使用 MIME 标准格式输出格式化时间文本TimeFormat tfMIME("%a, %d %b %Y %H:%M:%S %z");
printf("\n%s\n", tfMIME.format(t).c_str());
//使用 MimeParse 方法转换时间Time t2=tf.mimeParse(tf.mimeFormat(t));
printf("\n%s\n", tfCN.format(t2).c_str());
}
编译程序将输入如下结果
Wed Nov916:09:402005
11/09/0516:09:40
Wednesday, November09,200516:09:40
Wednesday, November09,200516:09:40
公元 2005年十一月09日 星期三 16时09分40秒078豪秒 时区+0800
公元200511月09周三16:09:40.078+0800
AD09November2005Wednesday16:09:40.078TZ:+0800
AD2005Nov09Wed16:09:40.078+0800
Wed,09Nov200516:09:40+0800
公元 2005年十一月09日 星期三 16时09分40秒000豪秒 时区+0800
3Time类
由于Time要精确到微秒,所以Time类使用timeval结构存储时间,该结构包含两个long整形,一个表示秒数(从1970年1月1日00:00:00 GMT以来的偏移量),一个表示微秒数。
Time类的定义如下
classTime
{
private:
/**//**
* Store the values as a timeval which fields as.
* struct timeval {
* long tv_sec; // seconds
* long tv_usec; // microseconds
*/structtimeval _tv;
}
Time类获取当前精确到微秒的实现如下(Win32提供的API只能精确到豪秒)
Time
Time::gettimeofday()
{
#ifdefined(HAVE_GETTIMEOFDAY)structtimeval tp;
tp.tv_sec=0;
tp.tv_usec=0;
#ifdef IS_SOLARIS_OS
::gettimeofday(&tp,0);//microseconds = 1/1,000,000 secTime nowtime(tp);
#elsestructtimezone tz;
tz.tz_dsttime=0;
tz.tz_minuteswest=0;
::gettimeofday(&tp,&tz);//microseconds = 1/1,000,000 secTime nowtime(tp);
#endifreturnnowtime;
#elifdefined(HAVE_FTIME)structtimeb tb;
tb.dstflag=0;
tb.millitm=0;
tb.time=0;
tb.timezone=0;
ftime(&tb);//milliseconds = 1/1,000 secTime nowtime(tb);
returnnowtime;
#elifdefined(HAVE_CLOCK_GETTIME)structtimespec ts;
ts.tv_sec=0;
ts.tv_nsec=0;
::clock_gettime(CLOCK_REALTIME,&ts);//nanoseconds = 1/1,000,000,000 secTime nowtime(ts);
returnnowtime;
#else//#warning "Time::gettimeofday()- low resolution timer: gettimeofday and ftime unavailable"Time nowtime(::time(0),0);//secondsreturnnowtime;
#endif}
Time类提供很多方法如果构造方法和操作符等,可以在time_t和其他时间类型之间转换,也可以单独获取和设置时间的秒和微秒。
4Calendar类
Calendar类是一个表示日历的类,它可以实现日历的向前向后前进等所有功能。例如你可以设置,获取,和操纵一个日期对象的各个部分,比方一个月的一天或者是一个星期的一天。举例如下:
//定义日历对象并设置为当前时间Time nowtm=Time::getCurrentTime();
TimeFormat ntf("%Y-%m-%d %H:%M:%S.%q");
printf("\n%s\n", ntf.format(nowtm).c_str());
Calendar cal;
cal.setTime(nowtm);
//年月日都向前移动一单位cal.rollUpYear();
cal.rollUpMonth();
cal.rollUpDayOfMonth();
String scal;
ntf.format(cal, scal);
printf("\n%s\n", scal.c_str());
程序输出结果
2005-11-0916:45:54.589
2006-12-1016:45:54.589
Calendar类是建立在Time类的基础上的,并加入了时区等信息,它的定义看起来如下所示:
classCalendar
{
protected:
/**//**
* Value of the ERA
field indicating
* the period before the common era (before Christ), also known as BCE.
* The sequence of years at the transition from BC
to AD
is
*
, 2 BC, 1 BC, 1 AD, 2 AD,
* @see Calendar#ERA
*/int_era;
/**//**
* The currently set time for this calendar, expressed in milliseconds after
* January 1, 1970, 0:00:00 GMT.
* @see #isTimeSet
* @serial
*/ Time _time;
/**//**
* The TimeZone
used by this calendar. Calendar
* uses the time zone data to translate between locale and GMT time.
* @serial
*/structtimezone _zone;
/**//**
* The asctime() and mktime() functions both take an argument
* representing broken-down time which is a binary represen-
* tation separated into year, month, day, etc. Broken-down
* time is stored in the structure tm which is defined in
* as follows:
*
* struct tm
* {
* int tm_sec; // seconds
* int tm_min; // minutes
* int tm_hour; // hours
* int tm_mday; // day of the month
* int tm_mon; // month
* int tm_year; // year
* int tm_wday; // day of the week
* int tm_yday; // day in the year
* int tm_isdst; // daylight saving time
* };
*
*/structtm _tm;
}
Calendar类定义了很多方法和操作符用来在Time类和时区、年月日等之间转换和设置、读取等。Calendar类实现时间的生成、转换等都是自己实现的,并不调用操作系统的API如mktime()等,并不使用CRT(C运行时)的全局变量如timezone等,所以它是线程安全的,每一个Calendar对象都是互相独立的,拥有自己的时区等信息。
5TimeFormat类
TimeFormat类主要实现了将时间格式化成一个时间文本,可以使用系统缺省的本地格式,也可以指定格式转换。转换的用法如下:
TimeFormat tf("%Y-%m-%d %H:%M:%S.%q");//定义一个格式Time t=Time::getCurrentTime();
String s=tf.format(t);//将时间对象格式化成文本字符串
时间格式化pattern有如下几种
%aThe abbreviated weekday name according to the current locale.
%AThe full weekday name according to the current locale.
%bThe abbreviated month name according to the current locale.
%BThe full month name according to the current locale.
%cThe preferred date and time representation for the current locale.
%CThe century number (year/100) as a 2-digit integer. (SU)
%dThe day of the month as a decimal number (range 01 to 31).
%DEquivalentto%m/%d/%y. (Yecch - for Americans only.
Americans should note that in other countries
%d/%m/%y is rather common. This means that in international context
thisformatisambiguousand should not be used.) (SU)
%eLike %d, the day of the month as a decimal number, but a leading
zero is replaced by a space. (SU)
%EModifier: use alternative format, see below. (SU)
%GTheISO8601 year with century as a decimal number.The 4-digit
year corresponding to the ISO week number (see %V).This has the
same format and value as %y,exceptthatiftheISOweeknumber
belongs to the previous or next year, that year is used instead. (TZ)
%gLike %G, but without century, i.e., with a 2-digit year (00-99). (TZ)
%hEquivalent to %b. (SU)
%HThe hour as a decimal number using a 24-hour clock (range 00 to 23).
%IThe hour as a decimal number using a 12-hour clock (range 01 to 12).
%jThe day of the year as a decimal number (range 001 to 366).
%kThehour (24-hour clock) as a decimal number (range 0 to 23); single
digits are preceded by a blank. (See also %H.) (TZ)
%lThe hour (12-hour clock) as a decimal number (range 1 to 12); single
digits are preceded by ablank. (See also %I.) (TZ)
%mThe month as a decimal number (range 01 to 12).
%MThe minute as a decimal number (range 00 to 59).
%nA newline character. (SU)
%OModifier: use alternative format, see below. (SU)
%pEither`AM'or `PM' according to the given time value, or the
corresponding strings for the current locale.Noon is treated as `pm'
and midnight as `am'.
%PLike %p but in lowercase: `am' or `pm' or a corresponding string for
the current locale. (GNU)
%rThe time in a.m. or p.m. notation.In the POSIX locale this is
equivalent to `%I:%M:%S %p'. (SU)
%RThe time in 24-hour notation (%H:%M). (SU) For a version including the
seconds, see %T below.
%sThe number of seconds since the Epoch, i.e., since 1970-01-01 00:00:00 UTC.
(TZ)
%SThe second as a decimal number (range 00 to 61).
%tA tab character. (SU)
%TThe time in 24-hour notation (%H:%M:%S). (SU)
%uThe day of the week as a decimal, range 1 to 7, Monday being 1.
See also %w. (SU)
%UThe week number of the current year as a decimal number, range 00 to 53,
starting with the first Sun? day as the first day of week 01.
See also %V and %W.
%VTheISO8601:1988 week number of the current year as a decimal number,
range 01 to 53, where week 1 is the first week that has at least 4 days
in the current year, and with Monday as the firstdayof the week.
See also %U and %W. (SU)
%wThe day of the week as a decimal, range 0 to 6, Sunday being 0.See also %u.
%WThe week number of the current year as a decimal number, range 00 to 53,
starting with the first Mon? day as the first day of week 01.
%xThe preferred date representation for the current locale without the time.
%XThe preferred time representation for the current locale without the date.
%yThe year as a decimal number without a century (range 00 to 99).
%YThe year as a decimal number including the century.
%zThe time-zone as hour offset from GMT.Required to emit RFC822-conformant
dates (using "%a, %d %b %Y %H:%M:%S %z"). (GNU)
%ZThe time zone or name or abbreviation.
%+The date and time in date(1) format. (TZ)
%%A literal `%' character.
6TimeParser类
TimeParser类实现与TimeFormat相反的功能,是将一个指定格式的时间文本转换成时间Time对象或Calendar对象,它的设计与TimeFormat类似,目前还未全部完成,只实现了转换Mime时间格式的文本的功能。
示例见上面的Hello World程序。
C++通用框架的设计作者:naven日期:2005-11-9