時(shí)間和日歷類的設(shè)計(jì)
C++通用框架的設(shè)計(jì) 作者:naven
1 介紹
時(shí)間和日歷以及時(shí)間的格式化處理在軟件的設(shè)計(jì)中起著非常重要的作用,但是目前C++的庫(kù)卻未有一個(gè)簡(jiǎn)單易用的時(shí)間類,大部分都需要開發(fā)者直接調(diào)用操作系統(tǒng)的API來完成,而且很多API都不是線程安全的。某些大型的C++框架雖然提供一些時(shí)間類,但是卻不通用,也很難直接拿出來使用。下面介紹一下參考Java Framework中的時(shí)間相關(guān)的類來設(shè)計(jì)并實(shí)現(xiàn)C++版本的時(shí)間和日歷類。
主要有如下一些類
Time類,對(duì)應(yīng)于Java的java.util.Date類,表示特定的瞬間,精確到毫秒(Linux可精確到微秒,Solaris可精確到十億分之一秒)。Time只表示某時(shí)某地的瞬間,從1970年1月1日 00:00:00 GMT以來的微秒數(shù),無時(shí)區(qū)。
Calendar 類,對(duì)應(yīng)于Java的java.util.Calendar類,它既表示了Time的精確瞬間,還代表了此時(shí)的年、月、日、時(shí)區(qū)等。它為特定瞬間與一組諸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日歷字段之間的轉(zhuǎn)換提供了一些方法,并為操作日歷字段(例如獲得下星期的日期)提供了一些方法。瞬間可用微秒值來表示,它是距歷元(即格林威治標(biāo)準(zhǔn)時(shí)間 1970 年 1 月 1 日的 00:00:00.000,GMT)的偏移量。
TimeFormat類,類似Java的java.text.SimpleDateFormat類,它用來將時(shí)間和日歷格式化成一個(gè)本地時(shí)間格式的文本形式,或者指定格式的文本形式。如果格式化時(shí)間,將缺省使用操作系統(tǒng)設(shè)定的本地(locale)時(shí)間格式處理。
TimeParser類,類似Java的java.text.SimpleDateFormat類,與TimeFormat相反,它用來將一個(gè)時(shí)間的文本轉(zhuǎn)化成一個(gè)本地時(shí)間Time,缺省使用操作系統(tǒng)設(shè)定的本地時(shí)間格式處理。
2 Hello World!
下面介紹一下它們的使用
void main()


{
// 獲取當(dāng)前時(shí)間,精確到微秒
Time t = Time::getCurrentTime();
// 使用CRT的API格式化時(shí)間,只能精確到秒
time_t tt = t.sec();
printf("\n%s\n", ctime(&tt));
// 使用系統(tǒng)缺省的locale輸出格式化時(shí)間文本
TimeFormat tf("%c");
printf("\n%s\n", tf.format(t).c_str());
// 使用系統(tǒng)缺省的locale輸出格式化時(shí)間文本
TimeFormat tf2("%#c");
printf("\n%s\n", tf2.format(t).c_str());
// 自定義輸出格式化時(shí)間文本,精確到豪秒
TimeFormat tff("%Y-%m-%d %H:%M:%S.%q");
printf("\n%s\n", tf2.format(t).c_str());
// 自定義輸出格式化時(shí)間文本,精確到豪秒,輸出所有時(shí)間信息,包括時(shí)區(qū)和年代等
TimeFormat tfCN("%G %Y年%B%d日 %A %H時(shí)%M分%S秒%q豪秒 時(shí)區(qū)%z", "zh_CN.gb2312");
printf("\n%s\n", tfCN.format(t).c_str());
// 使用自定義 zh_CN.gb2312 的locale和字符集輸出格式化時(shí)間文本,不受系統(tǒng)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和字符集輸出格式化時(shí)間文本,不受系統(tǒng)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 標(biāo)準(zhǔn)格式輸出格式化時(shí)間文本
TimeFormat tfMIME("%a, %d %b %Y %H:%M:%S %z");
printf("\n%s\n", tfMIME.format(t).c_str());

// 使用 MimeParse 方法轉(zhuǎn)換時(shí)間
Time t2 = tf.mimeParse(tf.mimeFormat(t));
printf("\n%s\n", tfCN.format(t2).c_str());
}

編譯程序?qū)⑤斎肴缦陆Y(jié)果
Wed Nov 9 16:09:40 2005


11/09/05 16:09:40

Wednesday, November 09, 2005 16:09:40

Wednesday, November 09, 2005 16:09:40

公元 2005年十一月09日 星期三 16時(shí)09分40秒078豪秒 時(shí)區(qū)+0800

公元 2005 11月 09 周三 16:09:40.078 +0800

AD 09 November 2005 Wednesday 16:09:40.078 TZ:+0800

AD 2005 Nov 09 Wed 16:09:40.078 +0800

Wed, 09 Nov 2005 16:09:40 +0800

公元 2005年十一月09日 星期三 16時(shí)09分40秒000豪秒 時(shí)區(qū)+0800

3 Time類
由于Time要精確到微秒,所以Time類使用timeval結(jié)構(gòu)存儲(chǔ)時(shí)間,該結(jié)構(gòu)包含兩個(gè)long整形,一個(gè)表示秒數(shù)(從1970年1月1日 00:00:00 GMT以來的偏移量),一個(gè)表示微秒數(shù)。
Time類的定義如下
class Time


{
private:

/**//**
* Store the values as a timeval which fields as.
* struct timeval {
* long tv_sec; // seconds
* long tv_usec; // microseconds
*/
struct timeval _tv;
}

Time類獲取當(dāng)前精確到微秒的實(shí)現(xiàn)如下(Win32提供的API只能精確到豪秒)
Time
Time::gettimeofday()


{
#if defined(HAVE_GETTIMEOFDAY)
struct timeval tp;
tp.tv_sec = 0;
tp.tv_usec = 0;
#ifdef IS_SOLARIS_OS
::gettimeofday(&tp, 0); // microseconds = 1/1,000,000 sec
Time nowtime(tp);
#else
struct timezone tz;
tz.tz_dsttime = 0;
tz.tz_minuteswest = 0;
::gettimeofday(&tp, &tz); // microseconds = 1/1,000,000 sec
Time nowtime(tp);
#endif
return nowtime;

#elif defined(HAVE_FTIME)
struct timeb tb;
tb.dstflag = 0;
tb.millitm = 0;
tb.time = 0;
tb.timezone = 0;
ftime(&tb); // milliseconds = 1/1,000 sec
Time nowtime(tb);
return nowtime;

#elif defined(HAVE_CLOCK_GETTIME)
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 0;
::clock_gettime(CLOCK_REALTIME, &ts); // nanoseconds = 1/1,000,000,000 sec
Time nowtime(ts);
return nowtime;

#else
//#warning "Time::gettimeofday()- low resolution timer: gettimeofday and ftime unavailable"
Time nowtime(::time(0), 0); // seconds
return nowtime;

#endif
}

Time類提供很多方法如果構(gòu)造方法和操作符等,可以在time_t和其他時(shí)間類型之間轉(zhuǎn)換,也可以單獨(dú)獲取和設(shè)置時(shí)間的秒和微秒。
4 Calendar類
Calendar類是一個(gè)表示日歷的類,它可以實(shí)現(xiàn)日歷的向前向后前進(jìn)等所有功能。例如你可以設(shè)置,獲取,和操縱一個(gè)日期對(duì)象的各個(gè)部分,比方一個(gè)月的一天或者是一個(gè)星期的一天。舉例如下:
// 定義日歷對(duì)象并設(shè)置為當(dāng)前時(shí)間
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);
// 年月日都向前移動(dòng)一單位
cal.rollUpYear();
cal.rollUpMonth();
cal.rollUpDayOfMonth();
String scal;
ntf.format(cal, scal);
printf("\n%s\n", scal.c_str());

程序輸出結(jié)果
2005-11-09 16:45:54.589

2006-12-10 16:45:54.589

Calendar類是建立在Time類的基礎(chǔ)上的,并加入了時(shí)區(qū)等信息,它的定義看起來如下所示:
class Calendar


{
protected:

/**//**
* Value of the <code>ERA</code> field indicating
* the period before the common era (before Christ), also known as BCE.
* The sequence of years at the transition from <code>BC</code> to <code>AD</code> 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 <code>TimeZone</code> used by this calendar. </code>Calendar</code>
* uses the time zone data to translate between locale and GMT time.
* @serial
*/
struct timezone _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
* <time.h> as follows:<P>
* <code>
* 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
* };
* </code>
*/
struct tm _tm;
}

Calendar類定義了很多方法和操作符用來在Time類和時(shí)區(qū)、年月日等之間轉(zhuǎn)換和設(shè)置、讀取等。Calendar類實(shí)現(xiàn)時(shí)間的生成、轉(zhuǎn)換等都是自己實(shí)現(xiàn)的,并不調(diào)用操作系統(tǒng)的API如mktime()等,并不使用CRT(C運(yùn)行時(shí))的全局變量如timezone等,所以它是線程安全的,每一個(gè)Calendar對(duì)象都是互相獨(dú)立的,擁有自己的時(shí)區(qū)等信息。
5 TimeFormat類
TimeFormat類主要實(shí)現(xiàn)了將時(shí)間格式化成一個(gè)時(shí)間文本,可以使用系統(tǒng)缺省的本地格式,也可以指定格式轉(zhuǎn)換。轉(zhuǎn)換的用法如下:
TimeFormat tf("%Y-%m-%d %H:%M:%S.%q"); //定義一個(gè)格式
Time t = Time::getCurrentTime();
String s = tf.format(t); // 將時(shí)間對(duì)象格式化成文本字符串

時(shí)間格式化pattern有如下幾種
%a The abbreviated weekday name according to the current locale.
%A The full weekday name according to the current locale.
%b The abbreviated month name according to the current locale.
%B The full month name according to the current locale.
%c The preferred date and time representation for the current locale.
%C The century number (year/100) as a 2-digit integer. (SU)
%d The day of the month as a decimal number (range 01 to 31).
%D Equivalent to %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
this format is ambiguous and should not be used.) (SU)
%e Like %d, the day of the month as a decimal number, but a leading
zero is replaced by a space. (SU)
%E Modifier: use alternative format, see below. (SU)
%G The ISO 8601 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, except that if the ISO week number
belongs to the previous or next year, that year is used instead. (TZ)
%g Like %G, but without century, i.e., with a 2-digit year (00-99). (TZ)
%h Equivalent to %b. (SU)
%H The hour as a decimal number using a 24-hour clock (range 00 to 23).
%I The hour as a decimal number using a 12-hour clock (range 01 to 12).
%j The day of the year as a decimal number (range 001 to 366).
%k The hour (24-hour clock) as a decimal number (range 0 to 23); single
digits are preceded by a blank. (See also %H.) (TZ)
%l The hour (12-hour clock) as a decimal number (range 1 to 12); single
digits are preceded by a blank. (See also %I.) (TZ)
%m The month as a decimal number (range 01 to 12).
%M The minute as a decimal number (range 00 to 59).
%n A newline character. (SU)
%O Modifier: use alternative format, see below. (SU)
%p Either `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'.
%P Like %p but in lowercase: `am' or `pm' or a corresponding string for
the current locale. (GNU)
%r The time in a.m. or p.m. notation. In the POSIX locale this is
equivalent to `%I:%M:%S %p'. (SU)
%R The time in 24-hour notation (%H:%M). (SU) For a version including the
seconds, see %T below.
%s The number of seconds since the Epoch, i.e., since 1970-01-01 00:00:00 UTC.
(TZ)
%S The second as a decimal number (range 00 to 61).
%t A tab character. (SU)
%T The time in 24-hour notation (%H:%M:%S). (SU)
%u The day of the week as a decimal, range 1 to 7, Monday being 1.
See also %w. (SU)
%U The 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.
%V The ISO 8601: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 first day of the week.
See also %U and %W. (SU)
%w The day of the week as a decimal, range 0 to 6, Sunday being 0. See also %u.
%W The 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.
%x The preferred date representation for the current locale without the time.
%X The preferred time representation for the current locale without the date.
%y The year as a decimal number without a century (range 00 to 99).
%Y The year as a decimal number including the century.
%z The time-zone as hour offset from GMT. Required to emit RFC822-conformant
dates (using "%a, %d %b %Y %H:%M:%S %z"). (GNU)
%Z The time zone or name or abbreviation.
%+ The date and time in date(1) format. (TZ)
%% A literal `%' character.
6 TimeParser類
TimeParser類實(shí)現(xiàn)與TimeFormat相反的功能,是將一個(gè)指定格式的時(shí)間文本轉(zhuǎn)換成時(shí)間Time對(duì)象或Calendar對(duì)象,它的設(shè)計(jì)與TimeFormat類似,目前還未全部完成,只實(shí)現(xiàn)了轉(zhuǎn)換Mime時(shí)間格式的文本的功能。
示例見上面的 Hello World 程序。
C++通用框架的設(shè)計(jì) 作者:naven 日期:2005-11-9