首页 > 其他分享 >lua的os.time()受时区影响

lua的os.time()受时区影响

时间:2024-09-02 21:14:11浏览次数:4  
标签:受时区 int timep ts lua tm time os

问题

最近在项目中发现os.time()返回的结果里把输入当作本地时间进行处理,而不是当作UTC的时间处理。
例如这样一行代码:

local tm = os.time({year=1970, month=1, day=1, hour = 8});
print(tm)

预期返回是8*60*60,但是输出tm会得到0。具体原因就是作者目前所在位置使用北京时间(UTC+8),去掉8小时的时区偏移后,结果就是1970年1月1日00:00:00 UTC的时间戳。

深入

查看文档

对于这个结果有疑问,于是翻阅了一下Lua官方文档,可以看到os.time的介绍里有这样一行提示

The returned value is a number, whose meaning depends on your system.
(返回值是一个数字,它的含义取决于你的系统)

所以这里其实已经指出了os.time()的返回值会受到系统时区的影响,因此在跨平台使用时需要特别注意时区差异。

查看源码

但是不满足于此,我又翻了一下lua的源码,看到os.date的实现如下:

static int os_time (lua_State *L) {
  time_t t;
  if (lua_isnoneornil(L, 1))  /* called without args? */
    t = time(NULL);  /* get current time */
  else {
    struct tm ts;
    luaL_checktype(L, 1, LUA_TTABLE);
    lua_settop(L, 1);  /* make sure table is at the top */
    ts.tm_sec = getfield(L, "sec", 0, 0);
    ts.tm_min = getfield(L, "min", 0, 0);
    ts.tm_hour = getfield(L, "hour", 12, 0);
    ts.tm_mday = getfield(L, "day", -1, 0);
    ts.tm_mon = getfield(L, "month", -1, 1);
    ts.tm_year = getfield(L, "year", -1, 1900);
    ts.tm_isdst = getboolfield(L, "isdst");
    t = mktime(&ts); /* 真正获取时间的地方 */
    setallfields(L, &ts);  /* update fields with normalized values */
  }
  if (t != (time_t)(l_timet)t || t == (time_t)(-1))
    return luaL_error(L,
                  "time result cannot be represented in this installation");
  l_pushtime(L, t);
  return 1;
}

可以的看到真正获取时间的代码是

t = mktime(&ts);

而mktime函数是C标准库中的一个函数,它将一个struct tm结构体转换为time_t类型的值,同时考虑了本地时区的影响。

那么为什么lua要用这样一个函数来实现,有没有接口能够避免时区的影响,确保返回的时间戳是基于UTC的呢?在stackoverflow中找到了答案,简而言之目前没有比较简单和通用的方法来避免时区的影响。不过在部分平台可以使用其它函数,例如在windows平台可以使用_mkgmtime,而在linux和BSD可以使用timegm,但是在PS5平台则没有比较合适的函数。

手动计算

考虑到既然计算规则是很明确的,就是计算给定的日期相对于1970年1月1日0点的时间戳,那么理论上我们是可以自己实现计算逻辑的。

正巧我发现luau(Lua语言的一个分支,但它包含了一些改进和优化)实现了上述计算逻辑,在luau中os.time()返回的是UTC时间的时间戳,其计算时间戳代码如下:

static time_t os_timegm(struct tm* timep)
{
    // Julian day number calculation
    int day = timep->tm_mday;
    int month = timep->tm_mon + 1;
    int year = timep->tm_year + 1900;

    // year adjustment, pretend that it starts in March
    int a = timep->tm_mon % 12 < 2 ? 1 : 0;

    // also adjust for out-of-range month numbers in input
    a -= timep->tm_mon / 12;

    int y = year + 4800 - a;
    int m = month + (12 * a) - 3;

    int julianday = day + ((153 * m + 2) / 5) + (365 * y) + (y / 4) - (y / 100) + (y / 400) - 32045;

    const int utcstartasjulianday = 2440588;                              // Jan 1st 1970 offset in Julian calendar
    const int64_t utcstartasjuliansecond = utcstartasjulianday * 86400ll; // same in seconds

    // fail the dates before UTC start
    if (julianday < utcstartasjulianday)
        return time_t(-1);

    int64_t daysecond = timep->tm_hour * 3600ll + timep->tm_min * 60ll + timep->tm_sec;
    int64_t julianseconds = int64_t(julianday) * 86400ull + daysecond;

    if (julianseconds < utcstartasjuliansecond)
        return time_t(-1);

    int64_t utc = julianseconds - utcstartasjuliansecond;
    return time_t(utc);
}

标签:受时区,int,timep,ts,lua,tm,time,os
From: https://www.cnblogs.com/wenxuanh/p/18393551

相关文章

  • GeoScene Pro教程(006):GeoScenePro地图集制作
    文章目录1、加载数据2、修改地图样式3、修改外观4、显示上下左右各为哪个地市5、新建布局6、选择地图框显示区域7、插入指北针、比例尺、图例8、显示相邻地市9、导出地图地图系列的构建来自单个地图图幅的集合,每个图幅显示特定的地图范围,包含动态地......
  • GeoScene Pro教程(004):GeoScene Pro制作与使用矢量切片包
    文章目录1、为什么创建矢量切片**提高渲染效率****优化数据传输****增强用户体验****更好的数据管理****便于数据分析和处理**2、创建矢量切片包3、导入到GeoSceneOnline1、为什么创建矢量切片矢量切片(VectorSlicing)是一种将大规模矢量数据(如地图......
  • 基于centos7.5安装mysql8
    @目录环境初始化部署mysql配置主从报错问题解决重启集群操作环境初始化mysql官网下载使用环境VMware17,centos7.5节点IPmysql01192.168.200.20mysql02192.168.200.21初始化两台节点;免密,主机名,主机映射等viinit.sh#!/bin/bash#定义节点信息NODES=("1......
  • 【网络安全】PostMessage:分析JS实现XSS
    未经许可,不得转载。目录前言示例正文前言PostMessage是一个用于在网页间安全地发送消息的浏览器API。它允许不同的窗口(例如,来自同一域名下的不同页面或者不同域名下的跨域页面)进行通信,而无需通过服务器。通常情况下,它用于实现跨文档消息传递(Cross-DocumentMessaging),这在一......
  • 光伏储能直流系统MATLAB仿真(PV光伏阵列+Boost DCDC变换器+负载+双向DCDC变换器+锂离子
     ......
  • 光伏储能直流系统MATLAB仿真(PV光伏阵列+Boost DCDC变换器+负载+双向DCDC变换器+锂离子
     ......
  • 为代码块添加 Mac OS X 窗口样式
    为代码块添加MacOSX窗口样式为代码块添加MacOSX窗口样式,在代码块pre之前添加图片,在代码块pre之后添加文本。pre{padding:30px2px2px2px;line-height:1;overflow:auto;word-wrap:normal;border-radius:5px;}pre>code{mar......
  • DOS相关知识
    DOS知识相对路径与绝对路径相对路径:从当前目录开始定位,形成的路径绝对路径:从顶级目录d开始定位,形成的路径DOS命令行(注:本贴为笔者学习记录所用)DOS是磁盘操作系统,将我们在控制器输入的指令接收解析并执行相对路径与绝对路径​因为\反斜杠有转义功能,windows文件路......
  • [全网独家原创]FVIM-XGBoost多输出回归 基于四向量优化算法优化XGBoost多输出回归多输
    [全网独家原创]FVIM-XGBoost多输出回归基于四向量优化算法优化XGBoost多输出回归Matlab代码(多输入多输出)每个输出都有以下线性拟合图等四张图!!!具体看图,独家图像!!!程序已经调试好,替换数据集根据输出个数修改outdim值即可运行!!!数据格式为excel!(如下)需要其他算法的都可以定制......
  • 使用Redis调用Lua脚本的方式对SpringBoot接口进行限流
    使用Redis调用Lua脚本的方式对SpringBoot接口进行限流使用Redis调用Lua脚本的方式对SpringBoot接口进行限流前言一、步骤1、自定义限流注解Limit.java,用于标注在需要限流的接口上2、编写限流类型枚举类LimitType.java3、编写限流具体实现类LimitAspect.java,通过AOP方式......