weitom1982

向各位技术前辈学习,学习再学习.
posts - 299, comments - 79, trackbacks - 0, articles - 0
  IT博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理
 时间和日历类的设计

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 ,缺省使用操作系统设定的本地时间格式处理。

 

2           Hello World!

下面介绍一下它们的使用

 

void  main() 
{
    
//  获取当前时间,精确到微秒
    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 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时09分40秒078豪秒 时区
+ 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时09分40秒000豪秒 时区
+ 0800

 

3           Time

由于 Time 要精确到微秒,所以 Time 类使用 timeval 结构存储时间,该结构包含两个 long 整形,一个表示秒数(从 1970 1 1 00:00:00 GMT 以来的偏移量),一个表示微秒数。

 

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 类获取当前精确到微秒的实现如下( 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 类提供很多方法如果构造方法和操作符等,可以在 time_t 和其他时间类型之间转换,也可以单独获取和设置时间的秒和微秒。

 

4           Calendar

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 - 09   16 : 45 : 54.589

2006 - 12 - 10   16 : 45 : 54.589

 

Calendar 类是建立在 Time 类的基础上的,并加入了时区等信息,它的定义看起来如下所示:

 

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 类和时区、年月日等之间转换和设置、读取等。 Calendar 类实现时间的生成、转换等都是自己实现的,并不调用操作系统的 API mktime() 等,并不使用 CRT C 运行时)的全局变量如 timezone 等,所以它是线程安全的,每一个 Calendar 对象都是互相独立的,拥有自己的时区等信息。

 

5           TimeFormat

TimeFormat 类主要实现了将时间格式化成一个时间文本,可以使用系统缺省的本地格式,也可以指定格式转换。转换的用法如下:

 

TimeFormat tf( " %Y-%m-%d %H:%M:%S.%q " );  // 定义一个格式
Time t  =  Time::getCurrentTime(); 
String s 
=  tf.format(t);  //  将时间对象格式化成文本字符串

 

时间格式化 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 类实现与 TimeFormat 相反的功能,是将一个指定格式的时间文本转换成时间 Time 对象或 Calendar 对象,它的设计与 TimeFormat 类似,目前还未全部完成,只实现了转换 Mime 时间格式的文本的功能。

 

示例见上面的 Hello World 程序。

 

 

C++ 通用框架的设计 作者: naven 日期: 2005-11-9

 

posted on 2005-11-09 17:28 Javen-Studio 阅读(415) 评论(3)  编辑 收藏收藏至365Key

FeedBack:
# 时间和日历类的设计(Java的Date和Calendar的C 实现)[TrackBack]
2005-11-09 17:47 | Naven
时间和日历以及时间的格式化处理在软件的设计中起着非常重要的作用,但是目前C 的库却未有一个简单易用的时间类,大部分都需要开发者直接调用操作系统的API来完成,而且很多API都不是线程安全的。某些大型的C 框架虽然提供一些时间类,但是却不通用,也很难直接拿出来使用。下面介绍一下参考Java Framework中的时间相关的类来设计并实现C 版本的时间和日历类。

阅读请点 http://www.cppblog.com/javenstudio/articles/1018.html

Naven引用了该文章,地址:http://blog.csdn.net/javenstudio/archive/2005/11/09/526258.aspx  回复
  
# re: 时间和日历类的设计(Java的Date和Calendar的C++实现)
2006-03-17 14:09 | slummer
非常棒!!!
能开源码?  回复
  
# re: 时间和日历类的设计(Java的Date和Calendar的C++实现)
2006-03-18 21:11 | Javen-Studio
想打算以后开源,不过现在太忙了
所以我先介绍一下我的设计思想,过些时间有空再提供一个共享库试用
谢谢  回复
 

Feedback

# re: 时间和日历类的设计(Java的Date和Calendar的C++实现) (转)  回复  更多评论   

2014-03-02 15:43 by luoweifu
我也正想自己设计这样一个类,C++的时间类太麻烦,太不灵活了。
只有注册用户登录后才能发表评论。