Lua的OS库时间函数
时间的三种表示格式
- 用数值表示时间值 (时间戳) 用数字值来表示时间值, 实际上时间值的本质就是一个数字值。 例如: d = 1131286477 这里的 1131286477 是一个以秒为单位的 格林威志时间(gmt),注意不同时区的时差
- 用字符串表示时间 (string) 用字符串来来表示时间。 例如: d = “2005-11-06 22:18:30” --2005年11月6日 22点18分30秒 d = “11/06/2005 22:18:30” --即2005年11月6日 22点18分30秒
- 时间的列表格式 (table) 用table对象来表示时间 例如: d = {year=2005, month=11, day=6, hour=22,min=18,sec=30,isdst=false} –同样表示2005年11月6日 22点18分30秒,其中isdst=false表示不使用夏令时 –夏时制,夏时令(Daylight Saving Time:DST),又称“日光节约时制”和“夏令时间”,是一种为节约能源而人为规定地方时间的制度,在这一制度实行期间所采用的统一时间称为“夏令时间”。一般在天亮早的夏季人为将时间提前一小时,可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。各个采纳夏时制的国家具体规定不同。目前全世界有近110个国家每年要实行夏令时。 我们可以这样访问时间列表 d.year = 2005;
每种格式时间取值
取得数值(number)格式的时间值 (转换为时间戳)
time = os.time()返回一个标准的number(数值)格式的时间值,这也是最原始的时间值 os.time()返回的时间是以秒为单位的。
1. 获取当前时间数值 time = os.time(); –如果没有指定任何参数,则返回当前时间
2. 通过table参数指定时间,获取指定的时间数值 time = os.time(tab);通过一个table参数,指定一个时间,返回时间的数值格式. 例如:
local tab = {year=2005, month=11, day=6, hour=22,min=18,sec=30,isdst=false} –year表示年,month表示月,day表示日期,hour表示小时,min表示分钟,sec表示秒,isdst表示是否夏令时 time = os.time(tab) -->返回值为1131286710
取得列表(table)格式的时间(时间戳转换为table)
tab = os.date("*t",time) 可以将一个数值格式的时间转换为字符串或者列表 其中第一个参数指定返回的格式,如果第一个参数指定为"*t" 则返回一个table格式的时间。 第二个参数指定一个时间数值时间戳(以秒为单位) 1. 获取当前时间的table格式 tab = os.date("*t") 如果不指定os.date的第二个参数,则返回当前时间的table表示格式 2. 通过时间数值,获取指定时间的table格式 tab = os.date("*t", time)通过os.date函数的第二个参数指定一个时间数值。 tab2=os.date(“!*t”, time) 得到的是UTC(时区为0)的时间戳。 注:UTC —— 协调世界时,又称世界统一时间、世界标准时间 也就是说 “!t” 得到的是一个 UTC 时间,为0度的经线(子午线),亦称本初子午线,通常将它与GMT视作等同(但是UTC更为科学和精确)。 例如:
local tab = os.date("*t", 1131286710); –返回值 tab 的数据 {year=2005, month=11, day=6, hour=22,min=18,sec=30} –year表示年,month表示月,day表示日期,hour表示小时,min表示分钟,sec表示秒,isdst表示是否夏令时 –tab成包括一些其他的成员 tab.yday 表示一年中的第几天 tab.wday 表示星期几(星期天为1) time = os.time(tab) -->返回值为1131286710
local function getTimeZone()
local now = os.time()
return os.difftime(now, os.time(os.date("!*t", now))) / 3600
end
取得时间的字符串(string)格式
tab = os.date(format,time)
实际上与取得table格式的时间一样,同样是使用os.date参数,不同的是第一个参数 如果第一个参数不是"*t" os.date返回字符串
os.date 的用法与 string.ftime 完全相同
例如:
win.consoleOpen() print( os.date(“today is %A, in %B”) ) print( os.date("%x", 906000490) ) –输出类似这样的字符串 09/16/1998
1、获取当前时间的字符串(string)格式 如果不指定os.date的第二个参数,则返回当前时间的table表示格式 例如:
str= os.date("*x")
2、通过时间数值,获取指定时间的(string)格式
通过os.date函数的第二个参数指定一个时间数值。 例如:
str= os.date("*x",1131286710)
3、获取时间的指定部份的(string)格式
例如:
str = os.date("%M") --返回当前时间分钟部份的字符串格式
时间数值与字符串之间的转换
请参考:string.time string.ftime 函数用法
计算时间值间隔
int = os.difftime(t2,t1);
t2,t1都是数值格式的时间值,返回值也是一个数值表示间隔的时间。 参数以及返回的时间值都是以秒为单位的数值格式。
local tab = {year=2005, month=11, day=6, hour=22,min=18,sec=30,isdst=false}
t1 = os.time(tab)
tab.day = tab.day +1;--前进一天
t2 = os.time(tab)
int = os.difftime(t2,t1);
win.messageBox(int.."秒","时间差"
系统时钟
int = os.tick(); 读取系统时钟,以毫秒为单位。表示从系统启动到当前时刻所过去的毫秒数。
int = os.clock() 读取系统时钟,以秒为单位。表示从系统启动到当前时刻所过去的秒数。
注意 : 因为内部实现的差异,os.tick 与 os.clock()的返回值并不完全一致。
local getTime = os.date(“%c”);
其中的%c可以是以下的一种:(注意大小写)
%a abbreviated weekday name (e.g., Wed)
%A full weekday name (e.g., Wednesday)
%b abbreviated month name (e.g., Sep)
%B full month name (e.g., September)
%c date and time (e.g., 09/16/98 23:48:10)
%d day of the month (16) [01-31]
%H hour, using a 24-hour clock (23) [00-23]
%I hour, using a 12-hour clock (11) [01-12]
%M minute (48) [00-59]
%m month (09) [01-12]
%p either “am” or “pm” (pm)
%S second (10) [00-61]
%w weekday (3) [0-6 = Sunday-Saturday]
%x date (e.g., 09/16/98)
%X time (e.g., 23:48:10)
%Y full year (1998)
%y two-digit year (98) [00-99]
%% the character ‘%’
如获取当前年月日时分秒:local date=os.date(“%Y-%m-%d %H:%M:%S”);
os.time() 获取当前秒
应用举例
目前大部分游戏都采用了Lua语言进行功能开发,在进行多语种发行的时候就会遇到时区显示的问题。以韩国版本为例,场景如下:
1、服务器处于固定的位置,比如放在首尔机房;
2、玩家所处的位置不确定,可能在韩国,或者是出差在其它国家或地区; 需求:
无论在哪个国家或地区,统一显示服务器的当前时间。在PC上查看,即便在国内测试的时候也显示韩国首尔的时间(比北京时间快1个小时)。
-- 北京时间
local serverTime = 1536722753 -- 2018/09/12 11:25
function getTimeZone()
local now = os.time()
return os.difftime(now, os.time(os.date("!*t", now)))
end
-- 8 hour * 3600 seconds = 28800 seconds
local timeZone = getTimeZone()/ 3600
print("timeZone : " .. timeZone)
local timeInterval = os.time(os.date("!*t", serverTime)) + timeZone * 3600 + (os.date("*t", time).isdst and -1 or 0) * 3600
local timeTable = os.date("*t", timeInterval)
--[[
for k, v in pairs(timeTable) do
print(k .. ":" .. tostring(v))
end
]]
print(timeTable.year .. "/" .. timeTable.month .. "/" .. timeTable.day .. " " .. timeTable.hour .. ":" .. timeTable.min .. ":" .. timeTable.sec)
关注是这个方法: os.date("!*t", now),其中以!为关键。
lua 源码, loslib.c Line 283 行
static int os_date (lua_State *L) {
size_t slen;
const char *s = luaL_optlstring(L, 1, "%c", &slen);
time_t t = luaL_opt(L, l_checktime, 2, time(NULL));
const char *se = s + slen; /* 's' end */
struct tm tmr, *stm;
if (*s == '!') { /* UTC? */
stm = l_gmtime(&t, &tmr);
s++; /* skip '!' */
}
else
stm = l_localtime(&t, &tmr);
if (stm == NULL) /* invalid date? */
luaL_error(L, "time result cannot be represented in this installation");
if (strcmp(s, "*t") == 0) {
lua_createtable(L, 0, 9); /* 9 = number of fields */
setallfields(L, stm);
}
else {
char cc[4]; /* buffer for individual conversion specifiers */
luaL_Buffer b;
cc[0] = '%';
luaL_buffinit(L, &b);
while (s < se) {
if (*s != '%') /* not a conversion specifier? */
luaL_addchar(&b, *s++);
else {
size_t reslen;
char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT);
s++; /* skip '%' */
s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */
reslen = strftime(buff, SIZETIMEFMT, cc, stm);
luaL_addsize(&b, reslen);
}
}
luaL_pushresult(&b);
}
return 1;
}
从源码可以看到 ! 调用了
#define l_gmtime(t,r) gmtime_r(t,r)
gmtime_r 函数是标准的POSIX函数,它是线程安全的,将日历时间转换为用UTC时间表示的时间。
注:UTC —— 协调世界时,又称世界统一时间、世界标准时间
也就是说 “!*t” 得到的是一个 UTC 时间,为0度的经线(子午线),亦称本初子午线,通常将它与GMT视作等同(但是UTC更为科学和精确)。 首尔位于东9区,所以实际的时间应该是 UTC + 9,9就是时区差 —— 9个小时。北京位于东8区,即 UTC + 8。
如何保证游戏内全部统一为服务器的时间呢?
服务器需要返回给客户端当前的时区的差值,比如韩国就返回 9,国内就返回 8,越南返回 7,北美返回 –16,记为 serverTimeZone。
服务端返回当前服务器时间serverTime(即首尔当前时间),我们只需要将服务器时间转为 UTC 的时间,然后再加上 serverTimeZone即可。
os.time(os.date("!*t", serverTime)) + serverTimeZone * 3600
这样无论在哪个地区或国家,都将显示首尔的时候,与服务器显示的时间就同步上了。
为什么要一直显示服务器的时间呢?
游戏中有很多功能是有时间限制的,比如运营活动,或者功能开启。如果用本地时间就不好控制,统一用服务器时间避免了很多问题。
可是也容易遇到一个坑,运营配置的活动时间都是针对当前服务器的时间,例如某个活动的截止时间是:2018-10-08 00:00:00,游戏需要显示活动截止倒计时。
通常的做法: ployEndTime – serverTime,得到一个秒数,然后将秒转成:xx天xx小时xx分xx秒
serverTime 是固定的,可是ployEndTime就容易出错,为什么?
serverTime 是在东9区 —— 首尔的时间,而 os.time({year=…}) 是根据本地时间来算时间的,这中间就存在问题。有一个时差的问题,之前计算一直用的是serverTimeZone —— 一个固定值,而我当前处于地区或国家,它相对于UTC的时区不确定的,怎么办?
用 (currTimeZone – serverTimeZone) * 3600 / 秒,os.time()之后再加上这个时区差就是首尔当前的时间戳了。国内东8 - 东9 = -1,也就是要减去一个1时区,最终将得到首尔地区的时间戳,再减去 serverTime 就是剩下的秒数了,然后将它转为 xx 天 xx 小时 xx 分 xx 秒。 最后小结一下:
1)os.time({year=xx}),这个时间算出来的是针对当前所处时区的那个时间戳。
2)os.date(“!*t”, 时间戳) 得到的是UTC(时区为0)的时间戳。
3)获取当前时区的值,可以通过文章开头的 getTimeZone 方法
4)想显示固定时区的时间(例如无论在哪都显示服务器的时间),只需要将(服务器)时间戳(秒),通过第2步的方法,得到 UTC 再加上固定的时区差
5)计算倒计时的时候,需要考虑到 os.time 是取当前时区,需要再将当前时区减去目标时区,再计划时间戳
6)夏令时,本身已经拨快了一个小时,当需要显示为固定时区的时间,则需要减去一个小时
- zabbix中配置当memory剩余不足20%时触发报警
- VB6再回首:数据访问
- python运用中文注释时报错解决方法
- 分页器常用样式
- 分布式监控系统Zabbix-3.0.3--短信报警设置
- linux下用户操作记录审计环境的部署记录
- open-falcon ---客户机agent操作
- 双拼域名yansuan.com被木雨林收购
- open-falcon ---安装Dashboard时候报错"SSLError: The read operation timed out"
- Flash/Flex学习笔记(37):不用系统组件(纯AS3)的视频播放器--只有8.82K
- Flash/Flex学习笔记(35):如何正确监听Stage对象的事件
- Flash/Flex学习笔记(34):AS3中的自定义事件
- 字符串处理总结(旧)
- 计算某年某月的某一天是星期几的算法
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- FastAdmin后台GetShell
- scRNA-seq Clustering
- Dizzy Blog
- LeetCode 392. 判断子序列
- LeetCode 5473. 灯泡开关 IV
- LeetCode 5472. 重新排列字符串
- BootStrap 导航栏实现下滑消失,上滑出现
- 七日Python之路--第六天
- 超性感的React Hooks(六)自定义hooks的思维方式
- 七日Python之路--第五天(之找点乐子)
- 超性感的React Hooks(七)useReducer
- 七日Python之路--第四天(之Django官方文档)
- 六、setTimeout与循环闭包经典面试题详解
- 七日Python之路--第四天
- 七、this