免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4308 | 回复: 9
打印 上一主题 下一主题

编程中的时间问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-01-10 10:13 |只看该作者 |倒序浏览
基本概念
UT:
        平太阳时的基本单位是平太阳日,1平太阳日等于24平太阳小时,86400平太阳秒。以平子夜作为0时开始的格林威治平太阳时,就称为世界时,简称UT。世界时与恒星时有严格的转换关系,人们是通过观测恒星得到世界时的。

UT0是本初子午线的太平时,直接由天文观测得到;
UT1是UT0校正过在恒星参考系中地球相对其旋转轴的微小运动(极向变化)效应的时间;
UT2是对UT1校正过在恒星参考系中地球旋转速度的微小季节性起伏效应的时间。

GMT:
格林威治标准时间。格林尼治所在地的标准时间,亦称“世界时”。

TAI:
原子时是以秒,而不是以日为基本时间单位的。原子时秒长定义为:铯原子基态的两个超精细能级间在海平面、零磁场下跃迁辐射9192631770周所持续的时间。原子时起点定在1958年1月1日0时(UT), 即规定在这一瞬间,原子时和世界时重合。世界各国的原子钟按照规定的方法进行相互比对,其数据再由专门的国际机构进行处理,求出全世界统一的 原子时,称为国际原子时,简称TAI。

UTC:
        相对于以地球自转为基础的世界时来说,原子时是均匀的计量系统,这对于测量时间间隔非常重要,但世界时时刻反映了地球在空间的位置,这也是需要的。为兼顾这两种需要,引入了协调世界时(UTC)系统。UTC在本质上还是一种原子时,因为它的秒长规定要和原子时秒长相等,只是在时刻上,通过人工干预,尽量靠近世界时。
        协调世界时(UTC)尽量靠近世界时(UT1)的意思是:必要时对协调世界时(UTC)作一整秒的调整(增加1秒或去掉1秒),使UTC和UT1的时刻之差保持在±0.9秒以内。这一技术措施就称为闰秒(或跳秒),增加1秒称为正闰秒(或正跳秒);去掉1秒称为负闰秒(或负跳秒)。是否闰秒,由国际地球自转服务(英文缩写为IERS)决定。
        闰秒的首选日期是每年的12月31日和6月30日,或者是3月31日和9月30日。如果是正闰秒,则在闰秒当天的23时59分60秒后插入1秒,插入后的时序是:…58秒,59秒,60秒,0秒,…,这表示地球自转慢了,这一天不是86400秒,而是86401秒;如果是负闰秒,则把闰秒当天23时59分中的第59秒去掉,去掉后的时序是:…57秒,58秒,0秒,…,这一天是86399秒。

GMT与UT
格林威治标准时和世界时 (UT) 是相等的,GMT 是标准的“民间”名称;UT 是相同标准的“科学”名称。格林威治的太平时亦称为格林威治平均时间(GMT),可看作等效的世界时,但它们相差12小时。

UTC与UT
UTC 是基于原子时钟的,UT 是基于天体观察的,两者在实际应用中难分轩轾。
我们日常所用的北京时间既不是原子时(TAI),也不是世界时(UT), 而是协调世界时(UTC)。
       
时区
        将地球表面按经线从南到北,划成一个个区域,并且规定相邻区域的时间相差1小时。今全球共分为 24个时区。由于实用上常常1个国家,或1个省份同时跨着 2个或更多时区,
为了照顾到行政上的方便,常将1个国家或 1个省份划在一起。所以时区并不严格按南北直线来划分,而是按自然条件来划分。例如,我国幅员宽广,差不多跨5个时区,但实际上在只用东八时区的标准时即北京时间为准。

夏令时
        一种法定的时间。夏天太阳升起得比较早,白天时间很长。为了节约能源和充分利用白天的宝贵时间,世界上不少国家都采用法律规定的形式,每到夏天就将这个国家使用的时间提前一小时,也有提前半小时或几小时的;到了冬季,又将拨快的时间拨回来。这样的时间就是“夏令时”,是一种法定时间

        还有一些关系其他时间计量的系统和概念,如:区时,地区时,各种时钟,授时系统等等一些相关概念可以参考更为详细的资料和专业的网站。

论坛徽章:
0
2 [报告]
发表于 2007-01-10 10:14 |只看该作者
各位不要顶,本人会更新这个帖子,后面会又很多内容

论坛徽章:
0
3 [报告]
发表于 2007-01-10 10:16 |只看该作者
ANSI C中的时间:
        在ANSI C中对时间结构的定义和操作函数声明在time.h中,对于结构struct tm和操作函数详细信息可以参考ANSI C标准库说明。
        日历时间:该值是自1970年1月1日00:00:00以来国际标准时间(U T C)所经过的秒数累计值(早期的手册称UTC为格林尼治标准时间)
        本地时间:是表达为本地时区的日历时间。
        进程时间:也称作CPU时间。
       
结构struct tm结构的成员:
成员        说明
int tm_sec        分后的秒(0-61), 多出来的两秒是用来处理跳秒问题用的
int tm_min        小时后的分(0-59)
int tm_hour        午夜后的小时(0-23)
int tm_mday        月中的天(0-31),本月第几日
int tm_mon        一月后的月数(0-11),本年第几月
int tm_year        1900年后的年数,要加1900表示那一年
int tm_wday        星期日后的天数(0-6),本周第几日
int tm_yday        一月一日后的天数(0-365),本年第几日,闰年有366日
int tm_isdst        夏令时标志(大于0的值说明夏令时有效,0说明无效,负数说明信息不可用)
       
常用时间函数:
clock_t clock(void);
获取自从调用程序以来经过的处理器时间的最近近似。

double difftime(time_t time1, time_t time0);
就是两个时间的秒差。

time_t mktime(struct tm *tm);
转换tm成为time_t格式,使用本地时间。

time_t time(time_t *t);
返回传回从epoch开始计算起的秒数。

char *asctime(const struct tm *tm);
转换时间格式为标准UNIX时间格式。

char *ctime(const time_t *timep);
转换时间格式为标准UNIX时间格式。

struct tm *gmtime(const time_t *timep);
转换成格林威治时间。

struct tm *localtime(const time_t *timep);
转换成本地时间。它可以透过修改TZ环境变数来在一台机器中,不同使用者表示不同时间。

size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
strftime有点像sprintf,其格式由fmt来指定,具体格式请参考

论坛徽章:
0
4 [报告]
发表于 2007-01-10 10:17 |只看该作者
UNIX操作系统中的时间
        UNIX及Linux的时间系统是由「新纪元时间」Epoch开始计算起,单位为秒,Epoch则是指定为1970年一月一日凌晨零点零分零秒,格林威治时间。  
目前大部份的UNIX系统都是用32位元来记录时间,正值表示为1970以後,负值则表示1970年以前。我们可以很简单地计算出其时间领域(当前几乎所有操作系统都假定 1 天 = 24 × 60 × 60 = 86400 秒):  
2^31/86400(s) = 24855.13481(天) ~ 68.0958(年)  
1970+68.0958 = 2038.0958  
1970-68.0958 = 1901.9042  
时间领域为[1901.9042,2038.0958], 这就是所谓的UNIX 2038 BUG

在一些64位元的平台上,例如Digital Alpha、SGI、Sparc等等,则用64位元来表示时间。  
2^63/86400 ~ 1E14(天) ~ 2.92E11(年)  
大约是292亿年,这样就无需担心了。不过虽然到2038年时,大部分目前32位元或更低位元的计算机将被64位元或更高位元的计算机所替代,不过对那些依然在服役的32位元或更低位元的计算机依然需要处理该问题。

应该注意的是:当前几乎所有操作系统都假定 1 天 = 24 × 60 × 60 = 86400 秒。但对于 UTC,大约每一两年出现一次额外的一秒,称为“闰秒”。闰秒始终作为当天的最后一秒增加,并且始终在 12 月 31 日或 6 月 30 日增加。例如,1995 年的最后一分钟是 61 秒,因为增加了闰秒。大多数计算机时钟不是特别的准确,因此不能反映闰秒的差别。

几个常见UNIX系统中对于更精确时间问题的处理:

(-)我怎样睡眠小于一秒
在所有Unix中都有的‘sleep()’函数只允许以秒计算的时间间隔。如果你想要更细化,那么你需要寻找替换方法:
1.        许多系统有一个‘usleep()’函数。
2.        你可以使用‘select()’或‘poll()’, 并设置成无文件描述符并试验; 一个普遍技巧是基于其中一个函数写一个‘usleep()’函数。 (参见comp.unix.questions FAQ 的一些例子)。
3.        如果你的系统有itimers(很多是有的)(译者注: setitimer和getitimer是两个操作itimers的函数, 使用“man setitimer”确认你的系统支持), 你可以用它们自己撺一个‘usleep()’。 (参见BSD源程序的‘usleep()’以便知道怎样做)。
4.        如果你有POSIX实时(realtime)支持, 那会有一个‘nanosleep()’函数。
       
众观以上方法,‘select()’可能是移植性最好的(直截了当说,它经常比 ‘usleep()’或基于itimer的方法更有效)。但是,在睡眠中捕获信号的做法会有所不同;基于不同应用,这可以成为或不成为一个问题。

无论你选择哪条路,意识到你将受到系统计时器分辨率的限制是很重要的(一些系统允许设置非常短的时间间隔,而其他的系统有一个分辨率,比如说10毫秒,而且总是将所有设置时间取整到那个值)。而且,关于‘sleep()’,你设置的延迟只是最小值(译者注:实际延迟的最小值);经过这段时间的延迟,会有一个中间时间间隔直到你的进程重新被调度到。

(二)怎样得到一个更细分时间单位的alarm函数版本
当今Unix系统倾向于使用‘setitimer()’函数实现闹钟,它比简单的‘alarm()’函数具有更高的分辨率和更多的选择项。一个使用者一般需要首先假设‘alarm()’和‘setitimer(ITIMER_REAL)’可能是相同的底层计时器,而且假设同时使用两种方法会造成混乱。

Itimers可被用于实现一次性或重复信号;而且一般有3种不同的计时器可以用:
`ITIMER_REAL'
     计数真实(挂钟)时间,然后发送‘SIGALRM’信号
`ITIMER_VIRTUAL'
     计数进程虚拟(用户中央处理器)时间,然后发送‘SIGVTALRM’信号
`ITIMER_PROF'
     计数用户和系统中央处理器时间,然后发送‘SIGPROF’信号;它供解释器用来进行梗概处理(profiling)
   
然而itimers不是许多标准的一部份,尽管它自从4.2BSD就被提供。POSIX实时标准的扩充定义了类似但不同的函数。

论坛徽章:
0
5 [报告]
发表于 2007-01-10 10:18 |只看该作者
各位不要顶,本人会持续更新这个帖子

论坛徽章:
0
6 [报告]
发表于 2007-01-10 10:38 |只看该作者
数据库中的时间(ORACLE)
        ORACLE支持内部和外部两种数据类型,内部数据类型描述ORACLE按照什么格式把数据存储在数据库的表列中,外部数据类型描述如何把数据存储在SQL变量中。
        ORACLE表示日期和时间的内部数据类型为:
        名称:DATE
        代码:12
        描述:表示定长日期/时间值,7个字节

        ORACLE表示日期和时间的外部数据类型为:
        名称:DATE
        代码:12
        描述:表示定长日期/时间值,占用7个字节,自左至右存放世纪、年、月、日、小时、分和秒。
        DATE外部数据类型说明:
字节        1        2        3        4        5        6        7
含义        世纪        年        月        日        小时        分        秒

        日期相关的内外部数据类型转换:
        内部外部:
可以将DATE内部数据类型转换到以下外部数据类型中:
VARCHAR2, STRING, LONG, VARCHAR, DATE, LONGVARCHAR, CHAR, CHARZ。

可以将以下内部数据类型转换到DATE外部数据类型中:
VARCHAR2, LONG, DATE, CHAR
(一般在实际操作中很少做此类直接转换)

        外部内部:
                可以将DATE外部数据类型转换到以下内部数据类型中:
                VARCHAR2, LONG, DATE, CHAR
                (一般在实际操作中很少做此类直接转换)

                可以将以下外部数据类型转换到DATE内部数据类型中:
        VARCHAR2, STRING, LONG, VARCHAR, DATE, LONGVARCHAR, CHAR, CHARZ。
               
        具体转换细节和注意点,请查阅具体版本的数据库文档。

                Oracle中提供以下的日期时间操作函数:
ADD_MONTHS,CURRENT_DATE,CURRENT_TIMESTAMP,DBTIMEZONE,
EXTRACT (datetime),FROM_TZ,LAST_DAY,LOCALTIMESTAMP,
MONTHS_BETWEEN,NEW_TIME,NEXT_DAY,NUMTODSINTERVAL,
NUMTOYMINTERVAL,ROUND (date),SESSIONTIMEZONE,
SYS_EXTRACT_UTC,SYSDATE,SYSTIMESTAMP,
TO_CHAR (datetime),TO_TIMESTAMP,TO_TIMESTAMP_TZ,
TO_DSINTERVAL,TO_YMINTERVAL,TRUNC (date),TZ_OFFSET
以上函数详细用法请参考ORACLE具体版本的数据库文档。


下面举几个小例子:
1.增加或减去月份
SQL> select to_char(add_months(to_date('199912','yyyymm'),2),'yyyymm') from dual;

TO_CHA
------
200002
SQL> select to_char(add_months(to_date('199912','yyyymm'),-2),'yyyymm') from dual;

TO_CHA
------
199910

2. 给出date2-date1的月份
SQL> select months_between('19-12月-1999','19-3月-1999') mon_between from dual;

MON_BETWEEN
-----------
          9
SQL>selectmonths_between(to_date('2000.05.20','yyyy.mm.dd'),to_date('2005.05.20','yyyy.mm.dd')) mon_betw from dual;

MON_BETW
---------
      -60

3. NEW_TIME(date,'this','that')
给出在this时区=other时区的日期和时间
SQL> select to_char(sysdate,'yyyy.mm.dd hh24:mi:ss') bj_time,to_char(new_time
  2  (sysdate,'PDT','GMT'),'yyyy.mm.dd hh24:mi:ss') los_angles from dual;

BJ_TIME             LOS_ANGLES
------------------- -------------------
2004.05.09 11:05:32 2004.05.09 18:05:32

4. 给出日期date和星期x之后计算下一个星期的日期
SQL> select next_day('18-5月-2001','星期五') next_day from dual;

NEXT_DAY
----------
25-5月 -01

5. 用来得到系统的当前日期
SQL> select to_char(sysdate,'dd-mm-yyyy day') from dual;

TO_CHAR(SYSDATE,'
-----------------
09-05-2004 星期日
trunc(date,fmt)按照给出的要求将日期截断,如果fmt='mi'表示保留分,截断秒
SQL> select to_char(trunc(sysdate,'hh'),'yyyy.mm.dd hh24:mi:ss') hh,
  2  to_char(trunc(sysdate,'mi'),'yyyy.mm.dd hh24:mi:ss') hhmm from dual;

HH                  HHMM
------------------- -------------------
2004.05.09 11:00:00 2004.05.09 11:17:00

6. TO_CHAR(date,'format')
SQL> select to_char(sysdate,'yyyy/mm/dd hh24:mi:ss') from dual;

TO_CHAR(SYSDATE,'YY
-------------------
2004/05/09 21:14:41
要熟悉对格式的处理

7. TO_DATE(string,'format')
将字符串转化为ORACLE中的一个日期,要熟悉对格式的处理。

论坛徽章:
0
7 [报告]
发表于 2007-01-10 13:11 |只看该作者
JAVA中的时间:
        在JAVA中对日期和时间的处理主要涉及到两个类java.util.Date和java.util.Calendar类,其中Calendar类是一个抽象类。
        类 Date 表示特定的瞬间,精确到毫秒,也能格式化和分析日期字符串,但是由于设计上的问题从1.1之后就不提倡使用,应该使用 Calendar 类实现日期和时间字段之间转换,使用 DateFormat 类来格式化和分析日期字符串。

        另:Java 中定义了TimeZone 对象,它和Calendar一样是个抽象类,实现了对时区问题的处理,也可以用来计算夏令时。
       
关于JAVA中对时间的详细说明,可以参考JDK Document。

论坛徽章:
0
8 [报告]
发表于 2007-01-10 13:19 |只看该作者
不想写了,回头看看,内容还是真多,可写的东西也很多,不过个人知识有限,做过的项目对时间的要求也不是很严格。
想了想,觉得对以下问题,希望各位道友提出自己的见解:

1.其他数据库对时间的处理
2.时间日期格式,历法的转换问题
3.分布式系统中不同机器之间的时钟同步,如客户端,应用服务器,数据库服务器,和分布式系统中不同区域的本地性问题
等等

论坛徽章:
0
9 [报告]
发表于 2007-01-11 09:54 |只看该作者
无论你选择哪条路,意识到你将受到系统计时器分辨率的限制是很重要的(一些系统允许设置非常短的时间间隔,而其他的系统有一个分辨率,比如说10毫秒,而且总是将所有设置时间取整到那个值)。而且,关于‘sleep()’,你设置的延迟只是最小值(译者注:实际延迟的最小值);经过这段时间的延迟,会有一个中间时间间隔直到你的进程重新被调度到。

补充一下,我想这里的意思是:

设置的时间区间实际上受到时间片的影响。例如,Linux的时间片默认长度是10ms,Alpha是1ms。当设置的时间长度小于一个时间片时,在调用程序被调度时开始执行,一个时间片内可以完成定时;而当调用程序在运行时在时间片的末尾调用设置,那么这个时间就要长达至少一个时间片以上的耗时。

顺便提一下,在守护程序中,守护循环的末尾放一个“稍息”,可以显著降低系统的CPU资源消耗。这个“稍息”一般可以设置在1ms左右的量级,视处理器的级别而定。
这个做法还可以显著改善人机交互时鼠标或键盘的粘滞感。

论坛徽章:
0
10 [报告]
发表于 2007-01-18 18:19 |只看该作者
原帖由 colors 于 2007-1-10 10:14 发表
各位不要顶,本人会更新这个帖子,后面会又很多内容

我顶你个肺!!!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP