//ole 时间对象 import sys.locale; import sys.locale.abbrevNames; namespace time; class ole { ctor( value, ... ) {{ if(type(value)==type.number){ var tm = ..time( , ... ); if(! VariantTimeToSystemTime( value ,tm) ) return; ..table.assign(this,tm);//移除元表 } elseif( ..time.istime(value) ){ ..table.assign(this,value); }; else { var tm = ..time( value, ... ) if(!tm) return; ..table.assign(this,tm);//移除元表 } var format,locale = ...; if(format!==null) this.format=format; if(locale!==null) this.locale=locale; this.dayofyear = null; }}; @_meta } namespace ole{ _meta = { _type = "time.ole.datetime"; _eq = function(b) { return tonumber(owner) == tonumber(b) }; _le = function(b) { return tonumber(owner) <= tonumber(b) } ; _lt = function(b) { return tonumber(owner) < tonumber(b) }; addday = function(span){ return owner._fromnumber( tonumber(owner)+span ) } addhour = function(span){ return owner._fromnumber( ( tonumber(owner)*24 + span)/24 ) } addminute = function(span){ return owner._fromnumber( ( tonumber(owner)*1440 + span)/1440 ) } addsecond = function(span){ return owner._fromnumber( ( tonumber(owner)*86400 + span)/86400 ) } update = function(){ return owner._fromnumber( tonumber(owner) ); } diffhour = function(tm2){ return (tonumber(owner) -tonumber(tm2)) /24 } diffminute = function(tm2){ return (tonumber(owner) -tonumber(tm2)) /1440 } diffsecond= function(tm2){ return (tonumber(owner) -tonumber(tm2)) /86400 } diffday = function(tm2){ return (tonumber(owner) -tonumber(tm2)) } _fromnumber = function(n){ VariantTimeToSystemTime(n,owner); return owner; }; _tonumber = function(){ var re,n = SystemTimeToVariantTime(owner,0) return n; }; systemFormat = function(format,locale){ if(!locale) locale = owner.locale; locale = locale ? ..sys.locale.abbrevNames[locale] : 0x0800 ; format = owner.toSystemFormat(format); var size = ::Kernel32.GetDateFormatW(locale,flags,owner,format,null,0); if( size > 0){ var buf = ..raw.buffer((size-1)*2); if( ::Kernel32.GetDateFormatW(Locale,flags,owner,format,buf,size) ) format = buf; } size = ::Kernel32.GetTimeFormatW(locale,flags,owner,format,null,0); if( size > 0){ var buf = ..raw.buffer((size-1)*2); if( ::Kernel32.GetTimeFormatW(locale,flags,owner,format,buf,size) ) return ..string.fromUtf16(buf); } }; toSystemFormat = function(format){ var f =..string.replace(format : owner[["formatString"]],"\%(\w)",{ d ="dd";a ="ddd";A ="dddd";B ="MMMM";b ="MMM";m = "MM";y = "yy";Y = "yyyy";H = "HH";I = "hh";M = "mm";S = "ss";p = "tt";Z="Z"; c = function(){ var d = ..sys.locale.getString(,0x1F/*_LOCALE_SSHORTDATE*/) var t = ..sys.locale.getString(,0x1003/*_LOCALE_STIMEFORMAT*/) return d + " " + t; }; x = function(){ return ..sys.locale.getString(,0x1F/*_LOCALE_SSHORTDATE*/); }; X = function(){ return ..sys.locale.getString(,0x1003/*_LOCALE_STIMEFORMAT*/); }; }); f = ..string.replace(f,"[YSD]+",..string.lower); if(f[1]=='!'#) f = ..string.right(f,-2); return f; }; local = function(copy){ var tm = copy ? ..time.ole(owner) : owner; if( !(owner.format && owner.format[1]=='!'#) ){ return tm; } UtcToLocal(null,owner,tm); tm.format = ..string.right(tm.format,-2); return tm; }; utc = function(copy){ var tm = copy ? ..time.ole(owner) : owner; if( owner.format && owner.format[1]=='!'# ){ return tm; } LocalToUtc(null,owner,tm); tm.format = tm.format ? ('!'+tm.format) : '!%Y/%m/%d %H:%M:%S'; return tm; }; _tostring = function(format,locale){ return owner.systemFormat(format,locale); }; _serialize = function(kernelCall){ if(kernelCall){ return ..string.format(`global.import('time.ole') : ..time.ole(%s,"%s")`,tonumber(owner), owner.format); } else { return ..string.format(`..time.ole(%s,"%s")`,tonumber(owner), owner.format); } }; _get = function(k,ownerCall){ if(k==="format"){ if( ownerCall ){ return _meta.systemFormat; } return owner.formatString; } elseif(k==="systemFormat"){ if( ownerCall ){ return _meta.systemFormat; } return owner.toSystemFormat(); } return _meta[[k]]; }; } ..table.mix(_meta,..time._meta); now = function(...){ return ..time.ole(,...); } ::OleAut32 := ..raw.loadDll("OleAut32.dll") VariantTimeToSystemTime = ::OleAut32.api("VariantTimeToSystemTime","bool(double vtime,struct& lpSystemTime)"); SystemTimeToVariantTime = ::OleAut32.api("SystemTimeToVariantTime","bool(struct lpSystemTime,double&pvtime)"); UtcToLocal = ::Kernel32.api("SystemTimeToTzSpecificLocalTime","bool(ptr zone,struct src,struct &dst)"); LocalToUtc = ::Kernel32.api("TzSpecificLocalTimeToSystemTime","bool(ptr zone,struct src,struct &dst)"); } isole = function(v){ return v@ == ole._meta; } //@guide time.ole 继承自 time 对啗,请参考:[📄 time 库使用指南](doc://library-guide/builtin/time/time.html) /**details(关于 OLE 时间) time.ole 则继承自 time 对象,time.ole 属于标准库, 需要用 import 语句导入才能使用。 time 是 aardio 的内置类型,也是一个 SYSTEMTIME 结构体对象, 在数值运算时会转换为通用的 Unix 时间戳,也就是 1970年1月1日 00:00:00 到 3000年12月31日 23:59:59 之间的秒数。 要注意同一时间无论是转换为 UTC 还是本地时间,Unix 时间戳都是不变的。 time.ole 在进行数值运算时会转换为 OLE 时间值, 这是一个 64 位浮点数( double 类型), 表示自 1899 年 12 月 30 日算起的天数。 并以负数表示 1899 年 12 月 30 日之前的时间, 小数部分表示一天当中的片段时间。 支持从 `100/01/01` 到 `9999/12/31` 的时间。很明显,time.ole 可以表示更大的时间范围, 但是 OLE 时间并没有约定标准时区。 所以 time.ole 对象在转换 UTC 与本地时间以后,OLE 时间值也会发生改变。 也就是说 time.ole 在转换时区以后,再转换为数值时会返回不同的数值。 如果希望得到统一的数值,则应当调用 utc 方法以后再获取数值。 COM 接口使用的 Variant Time(类型 VT_DATE) 存储的也是 OLE 时间值, 但 aardio 的 COM 接口会将 VT_DATE 类型自动转换为 time 类型。 Excel 日期序列号(Excel date serial number)存储的也是 OLE 时间值。 但要注意 Excel 存在一个历史遗留问题: > Excel 将 1900/1/1 作为第 1 天,又将 1900 错误地视为闰年多出了不存在的 1900/2/29 这一天。 > 所以 Excel 序列号在 1900/2/29 以前与 OLE 时间值是不一样的。而在 1900/2/29(日期序列号为 60) 之后则是一样的。在 Excel 的日期单元格直接输入数值序列号可以查看对应的日期。 end details**/ /*intellisense() time.ole(.(__,"%Y/%m/%d %H:%M:%S") = 创建 OLE 时间对象,\n返回对象可传入其他线程使用。\n此对象继承自 time 对象,基本用法相同,\n请参考 time 库文档与使用指南。\n\n参数 @1 可指定 OLE 时间数值、字符串、或 time,time.ole 对象,\n也可以指定仅提供 time 对象部分键值的表作为参数。\n\n参数 @2 可选指定格式化串,首字符为`!`表示使用 UTC 时区。\n兼容百分号与无百分号格式化语法,请参考 format 属性说明。\n\n可选使用参数 @3 指定对象的 locale 属性,即文本格式化使用的区域语言代码。\nlocale 与 setlocale 函数支持相同的语言代码,例如:英文"enu",中文"chs"\n\n此对象继承自 time 对象,所有功能相同。与time对象的区别是:\nOLE 时间对象在输入数值或转换为数值是是以天数为单位的,而 time 对象以秒为单位.\nOLE 时间对象支持从 100 年 1 月 1 日到 9999 年 12 月 31 日的时间,正数 1899 年 12 月 30 日算起的天数,\n小数部分是一天当中的片段时间,负数表示 1899 年 12 月 30 日之前的时间.\n\n注意:格式化为字符串时调用 systemFormat 函数格式化,请参考该函数说明 time.ole() = !time_ole. time.ole.now() = 返回表示当前时间的 OLE 时间对象。\等价于无参数调用 time.ole 构造函数。\n!time_ole. time.ole = OLE 时间(OLE Date / Variant Time )对象。\n进行数值运算时会转换为自 1899 年 12 月 30 日算起的天数 ,\n并以负数表示 1899 年 12 月 30 日之前的时间,\n小数部分表示一天当中的片段时间。\n\n注意:格式化为字符串时调用 systemFormat 格式化,请参考该函数说明。 time.isole(__) = 检测参数中指定的值是否time.ole对象 end intellisense*/ /**intellisense(!time_ole) year = 年 dayofweek = 星期。\n星期一到星期六的值对应数值为1到6,星期日的值为 0,\n注意修改这个字段不会更新时间值,\n\n这个字段只有参与数值运算或调用 add*** 函数才会填充或更新,\n例如调用 addday(0) 会更新此字段 day = 日 month = 月 hour = 小时 minute = 分钟 second = 秒 milliseconds = 毫秒 endstr = 文本解析为时间后,\n最后一个格式化标记解析成功并跳过空白字符以后的剩余的连续不含空白字符串,\n可用于后续解析iso8601等格式的时区(解析后将必须删除)。 locale = 格式化时间使用的语言代码。\n参数与 setlocale 相同,英文"enu",中文"chs",\n该属性为空表示使用当前默认语言。 local( = 将UTC时间转换为本地时间。\n要特别注意 time 对象转换 UTC 与本地时区并不会改数值时间戳。\n但是 time.ole 转换 UTC 与 本地时区以后对应的 OLE 时间数值也会改变。 local(.() = 将UTC时间转换为本地时间,修改并返回自身。\n格式化串首字符为 `!` 表示UTF时间。\n如果不是 UTC 时间直接返回自身。 local(.(true) = 将 UTC 时间转换为本地时间,不修改自身并返回新对象。\n如果不是 UTC 时间直接复制并返回新对象。 utc( = 将本地时间转换为 UTC 时间。\n要特别注意 time 对象转换 UTC 与本地时区并不会改数值时间戳。\n但是 time.ole 转换 UTC 与 本地时区以后对应的 OLE 时间数值也会改变。 utc(.() = 将本地时间转换为 UTC 时间,修改并返回自身。\n格式化串首字符为 `!` 表示 UTC 时间。\n如果已经是 UTC 时间直接返回自身。 utc(.(true) = 将本地时间转换为 UTC 时间,不修改自身并返回新对象。\n如果已经是 UTC 时间直接复制并返回新对象。 update() = 重新计算时间并更新 dayOfWeek 字段。 addmonth(__/*输入数值*/) = 增加月份数,返回自身。 addday(__/*输入数值*/) = 增加天数,可以为负数,返回自身。 addsecond(__/*输入数值*/) = 增加秒数,可以为负数,返回自身。 addminute(__/*输入数值*/) = 增加分钟数,可以为负数,返回自身。 addhour(__/*输入数值*/) = 增加小时数,可以为负数,返回自身。 diffmonth(__/*指定datetime对象*/) = 计算两个 time.ole 对象相差月份 diffday(__/*指定datetime对象*/) = 计算两个 time.ole 对象相差天数 diffsecond(__/*指定datetime对象*/) = 计算两个 time.ole 对象相差秒数 diffminute(__/*指定datetime对象*/) = 计算两个 time.ole 对象相差分钟数 diffhour(__/*指定datetime对象*/) = 计算两个 time.ole 对象相差小时数 format = 作为属性可读写默认格式化串,首字符为`!`表示 GMT 时间。\n例如: "yyyy-mm-dd HH:MM:SS"。\n兼容带百分号风格的时间格式串,例如 "%Y年%m月%d日 %H时%M分%S秒" 。\n关于格式化串详细说明请查看 time 库文档。\n参数 @2 setlocale 函数支持相同的区域语言代码,例如:英文"enu",中文"chs" format(.(格式化串,区域代码) = 作为方法调用( ownerCall 方式 )时可格式化时间并返回字符串。\n所有参数可选,格式化串兼容百分号与无百分号风格,示例:\n"yyyy-mm-dd HH:MM:SS" 或 "%Y年%m月%d日 %H时%M分%S秒"。\n关于格式化串详细说明请查看 time 库文档。\n\n对于 time.ole 对象此方法实际上会调用 systemFormat 函数。 systemFormat(.(格式化串,区域代码) = 作为方法调用( ownerCall 方式 )时,\n使用系统格式化规则格式化时期,区域代码应使用 3 字母缩写。\n此函数使用不带百分号的格式化语法,但也兼容带百分号的格式串,\n对应规则如下(Y S D 忽略大小写):\n"%y" -> "yy" 表示2位年份\n"%Y" -> "yyyy" 表示4位年份\n"%B" -> "MMMM" "" 表示月份全称\n"%b" -> "MMM" 表示月份缩写\n"%m" -> "MM" 表示2位月份\n"%d" -> "dd" 表示月份的第几天\n"%H" -> "HH" 表示2位24时制小时\n"%I" -> "hh" 表示2位12时制小时\n"%M" -> "mm" 表示2位分钟数\n"%S" -> "ss" 表示2分秒数\n"%p" -> "tt" 表示12时制显示上午或下。 systemFormat = 作为属性可读取无百分号风格的格式化串。 \n例如: "yyyy-mm-dd HH:MM:SS"。\n如果 format 属性使用了百分号风格的格式串,\nsystemFormat 会将其转换为无百分号风格后返回。\nsystemFormat 不会在首字符使用 `!` 标记 UTC 时间。\n\n此属性只可读取,不应写入其他值,应使用 format 属性读写格式化串。 addmonth() = !time_ole. addday() = !time_ole. addsecond() = !time_ole. addminute() = !time_ole. addhour() = !time_ole. end intellisense**/