在.NET Interop X++一文中有简单提到在.NET interop中如何使用日期时间类型,这里着重看看X++中这些日期时间类型的区别。
创建一个表,包含类型分别为Date、Time和UTCDateTime的字段,如下使用JOB在表中插入数据:
static void DateTimeInsertJob(Args _args) { utcDateTime utcdatetime1; TableTestDateTime ttd; date date1=24\09\2012; TimeOfDay time1=str2time("12:40:00"); ; ttsBegin; delete_from ttd; ttd.DateLineID=1; ttd.FieldUTCDateTime=DateTimeUtil::newDateTime(date1,time1); ttd.FieldDate=date1; ttd.FieldTime=time1; ttd.insert(); ttd.DateLineID=2; ttd.FieldUTCDateTime=DateTimeUtil::newDateTime(date1,time1,DateTimeUtil::getUserPreferredTimeZone()); ttd.FieldDate=date1; ttd.FieldTime=time1; ttd.insert(); ttd.DateLineID=3; utcdatetime1=DateTimeUtil::utcNow(); ttd.FieldUTCDateTime=utcdatetime1; ttd.FieldDate=DateTimeUtil::date(ttd.FieldUTCDateTime); ttd.FieldTime=DateTimeUtil::time(ttd.FieldUTCDateTime); ttd.insert(); ttd.DateLineID=4; utcdatetime1=DateTimeUtil::utcNow(); ttd.FieldUTCDateTime=utcdatetime1; ttd.FieldDate=today(); ttd.FieldTime=timeNow(); ttd.insert(); ttd.DateLineID=5; ttd.FieldUTCDateTime=DateTimeUtil::applyTimeZoneOffset(utcdatetime1,Timezone::GMTPLUS0545KATHMANDU); ttd.FieldDate=DateTimeUtil::date( ttd.FieldUTCDateTime); ttd.FieldTime=DateTimeUtil::time(ttd.FieldUTCDateTime); ttd.insert(); ttsCommit; }
我们从form上显示这些插入的纪录,得到的结果是:
第一行纪录,我们直接赋值一个日期到Date类型date1,从字符串得到一个TimeOfDay类型time1,然后使用DateTimeUtil::newDateTime(date1,time1)从这个Date和TimeofDay类型构建一个UTC日期时间类型,DateTimeUtil::newDateTime()的第三个参数可以指定一个时区,这里没有指定,默认使用了UTC的0时区,最后会发现显示在窗口的UTC时间比我们插入的时间晚了8个小时,这是因为form上的utcdatetime控件的“TimeZonePreference”属性设置为了“Auto”,它会自动使用当前用户的时区(北京时间+8时区)来显示这个UTC标准时间,如果改为“No conversion”则始终使用0时区的日期时间来显示。
第二行纪录使用了在构建UTC时间时使用了用户设置的时区参数DateTimeUtil::getUserPreferredTimeZone(),form显示的时间和TimeOfDay的时间是一致的。
第三行纪录从DateTimeUtil::utcNow()得到当前UTC日期时间,并从它得到一个timeofday时间-DateTimeUtil::time(ttd.FieldUTCDateTime),注意这个时间是UTC时间在0时区的时间部分,比form上显示的UTC当前时区时间早了8小时。
第四行纪录则是使用today()和timenow()两个函数来分别初始化Date日期和TimeOfDay时间,得到的是当前系统的日期时间,和显示的UTC当前时间是一致的。
第五行使用DateTimeUtil::applyTimeZoneOffset()对一个UTC日期时间做时区转换,转换后保存后重新显示的时间不再是插入纪录时的当时系统UTC时间。
注意UTC时间(世界协调时间)和格林威治时间不是一回事,前者基于世界原子时钟,后者基于地球自转,目前世界上的标准时间是UTC时间而不再是格林威治时间。
从上面的结果可以看到,在处理UTC时间时需要考虑到当前的时区,否则可能得到和预想不符的结果。再来看看保存在数据库中的数据:
首先可以看到保存在数据库中的Date是不纪录时间的;有一叫做FIELDUTCDATETIMEZID的字段,这是AOT表中看不到的隐藏字段,它在创建一个UTC字段的时候自动生成,用来保存UTC字段日期时间的时区及夏令时信息,第二条纪录我们手工指定了时区信息,所以该纪录的这个字段的值和其他纪录不同的,这个字段用来帮助我们在比如政府重新定义了所属时区、夏令时起始时更新保存在数据库中的UTC;timeofday在数据库中是以整数保存在数据库中的。
以上的测试基于windows系统的时区和AX用户option里设置的时区同在+8时区,如果我们把系统的时区改成其他其他时区,重启AX客户端程序会得到“Time zone mismatch”的提示信息(用户option->General->Time zone mismatch notification打勾的情况下),重新执行下JOB重填表纪录,会发现第四条纪录TimNow()得到的时间是当前时区显示的系统时间,而UTC时间仍然是正确的显示用户option定义的UTC时区时间,所以TimNow()是和当前系统时间一致受时区设置影响,而DateTimeUtil::utcNow()则不受时区设置影响。
下一项测试是在一台Client机器上(不是AOS服务器)将日期设置提前一天来看看today()和DateTimeUtil::utcNow()是从客户机还是从AOS取值,测试的结果是两者都使用了当前Client的日期设置,得到的结果比AOS上的时间提前一天。
继续测试以下几个和System date有关的日期时间:
info(date2str(today(),123,2,2,2,2,4)); info(date2str(systemdateget(),123,2,2,2,2,4)); info(date2str(DateTimeUtil::date(DateTimeUtil::getSystemDateTime()),123,2,2,2,2,4)); info(date2str(DateTimeUtil::date(DateTimeUtil::applyTimeZoneOffset(DateTimeUtil::getSystemDateTime(), DateTimeUtil::getUserPreferredTimeZone())),123,2,2,2,2,4));
得到的结果是systemdateget()、DateTimeUntil::getSystemDateTime()得到的日期是设置在Tools->Session date and time下的日期时间。
那如何得到AOS的系统时间呢?这似乎没有专门的类及方法来从AOS获取日期时间,其实是最简单的办法就是把today()、TimeNow()、DateTimeUntil::utcNow()放到Server去执行。
以上的测试比较凌乱,读者自行去总结吧,更多有关时区的内容可以参见http://msdn.microsoft.com/EN-US/library/cc622312.aspx。