Lua 语言的标准库提供了两个用于操作日期和时间的函数,这两个函数在 C 语言标准库中也存在,提供的是同样的功能。 虽然这两个函数看上去很简单,但依旧可以基于这些简单的功能完成很多复杂的工作。
Lua 语言针对日期和时间使用两种表示方式。 第1种表示方式是一个数字 ,这个数字通常是一个整型数。尽管并非是 ISO C 所必需的,但在大多数系统中这个数字是自一个被称为纪元的固定日期后至今的秒数。特别地,在 POSIX 和 Windows 系统中这个固定日期均是 Jan 01, 1970, 0:00 UTC 。
Lua 语言针对日期和时间提供的第 2 种表示方式是一个表。日期表 具有以下几个重要的字段:year 、month 、day 、hour 、min , sec 、wday 、yday 和 isdst ,除 isdst以外的所有字段均为整型数。 前 6 个字段的含义非常明显,而 wday 字段表示本周中的第几天(第 1 天为星期天); yday 字段表示当年中的第几天(第 1 天是 1 月 1 日 ); isdst 字段表示布尔类型 ,如果使用夏时令则为真。 例如, Sep 16, 1998, 23:48:10 (星期三)对应的表是 :
Lua
{year = 1998, month = 9, day = 16, yday = 259, wday = 4,
hour = 23, min = 48, sec = 10, isdst = false}
日期表中不包括时区,程序需要负责结合相应的时区对其正确解析。
12.1 函数 os.time
不带任何参数调用函数 os.time 会以数字形式返回当前的日期和时间:
Lua
> os.time()
1745743311
在一个 POSIX 系统中,可以使用一些基本的数学运算分离这个数值:
Lua
local date = 1745743311
local day2year = 365.242 -- 1年的天数
local sec2hour = 60 * 60 -- 1小时的秒数
local sec2day = sec2hour * 242 -- 1天的秒数
local sec2year = sec2day * day2year -- 1年的秒数
-- 年
print(date // sec2year + 1970)
-- 小时(UTC格式)
print(date % sec2day // sec2hour)
-- 分钟
print(date % sec2hour // 60)
-- 秒
print(date % 60)
如果以一个日期表作为参数调用函数 os.time ,那么该函数会返回该表中所描述日期和时间对应的数字。year 、month 和 day 字段是必需的,hour、min 和 sec 字段如果没有提供的话则默认为 12:00:00 ,其余字段(包括 wday 和 yday )则会被忽略。
Lua
> os.time({year=2015, month=8, day=15, hour=12, min=45, sec=20})
1439613920
> os.time({year=2002, month=1, day=1, hour=0})
1009814400
> os.time({year=2002, month=1, day=1, hour=0, sec=1})
1009814401
> os.time({year=2002, month=1, day=1})
1009857600
12.2 函数 os.date
函数 os.date 在一定程度上是函数 os.time 的反函数(尽管这个函数的名字写的是date ),它可以将一个表示日期和时间的数字转换为某些高级的表示形式,要么是日期表要么是字符串 。该函数的第 1 个参数是描述期望表示形式的格式化字符串, 第 2 个参数是数字形式的日期和时间(如果不提供,则默认为当前日期和时间 )。
要生成一个日期表,可以使用格式化字符串"*t"。例如,调用函数 os.date ("*t ",906000490 ) 会返回下列表:
Lua
> a= os.date("*t", 906000490)
> print(a.year, a.month, a.day, a.hour, a.min, a.sec, a.wday, a.yday, a.isdat)
1998 9 17 10 48 10 5 260 nil
>
大致上,对于任何有效的时间 t, os.time(os.date("*t", t))==t 均成立。
对于其他格式化字符串,函数 os.date 会将日期格式化为一个字符串,该字符串是根据指定的时间和日期信息对特定的指示符进行了替换的结果。所有的指示符都以百分号开头紧跟一个字母,例如:
Lua
> print(os.date("a %A in %B"))
a Sunday in April
> print(os.date("%d/%m/%Y", 906000490))
17/09/1998
下表列出了主要的指示符,这些指示符使用的时间为 1998 年 9 月 16 日 (星期三)23:48:10。

对于数值,表中也给出了它们的有效范围。以下是一些演示如何创建 ISO 8601 格式日期和时间的示例 :
Lua
t = 906000490
-- ISO 8601格式的日期
print(os.date("%Y-%m-%d", t))
> 1998-09-17
-- ISO 8601格式的日期和时间
print(os.date("%Y-%m-%dT%H:%M:%S", t))
> 1998-09-17T10:48:10
-- ISO 8601格式的序数日期
print(os.date("%Y-%j", t))
> 1998-260
如果格式化字符串以叹号开头,那么函数 os .date 会以 UTC 格式对其进行解析:
Lua
-- 纪元
print(os.date("!%c", 0))
> 01/01/70 00:00:00
如果不带任何参数调用函数 os.date ,那么该函数会使用格式%c,即以一种合理的格式表示日期和时间信息 。请注意,%x、%X和%c 会根据不同的区域和系统而发生变化。如果需要诸如 dd/mm/yyyy 这样的固定表示形式,那么就必须显式地使用诸如 "%d/%m/%Y" 这样的格式化字符串 。
12.3 日期和时间处理
当函数 os.date 创建日期表时,该表的所有字段均在有效的范围内 。当我们给函数 os.time 传入一个日期表时,其中的字段并不需要归一化。 这个特性对于日期和时间处理非常重要。
举一个简单的例子,假设想知道从当前向后数 40 天的日期,那么可以使用如下的代码进行计算:
Lua
t = os.date("*t")
print(os.date("%Y/%m/%d", os.time(t)))
t.day = t.day + 40
print(os.date("%Y/%m/%d", os.time(t)))
如果我们把数字表示的时间转换成日期表,那么就能得到日期和时间的归一化形式:
Lua
t = os.date("*t")
print(t.day, t.month)
> 27 4
t.day = t.day - 40
print(t.day, t.month)
> -13 4
t = os.date("*t", os.time(t))
print(t.day, t.month)
> 18 3
在这个例子中,4月13 被归一化为 3月18 ,也就是4月13的前 40 天。
在大多数系统中,也可以对数字形式的时间增加或减少 3456000 ( 40 天对应的秒数)。不过,由于标准 C 并不要求数值表示的时间是从纪元开始的,因此标准 C 并不保证这种操作的正确性。此外,如果我们想增加的是月份而非天数,由于不同的月份具有不同的天数,那么直接操作秒数就会有问题。而以归一化的方式处理则没有这些问题:
Lua
t = os.date("*t")
print(os.date("%Y/%m/%d", os.time(t)))
> 2025/04/27
t.month = t.month + 6 -- 从当天开始往后6个月
print(os.date("%Y/%m/%d", os.time(t)))
> 2025/10/27
函数 os.difftime 用来计算两个时间之间的差值,该函数以秒为单位返回两个指定数字形式表示的时间的差值。对于大多数系统而言,这个差值就是一个时间相对于另一个时间的减法结果。 但是,与减法不同,函数 os.difftime 的行为在任何系统中都是确定的 。以下的示例计算了 Lua5.2 和 Lua5.3 发布时间之间间隔的天数 :
Lua
local t5_3 = os.time({year=2015, month=1, day=12})
local t5_2 = os.time({year=2011, month=12, day=16})
local d = os.difftime(t5_3, t5_2)
print(d // (24 * 3600)) --> 1123.0
使用函数 difftime 可以获取指定日期相对任意时刻的秒数:
Lua
> myepoch = os.time{year = 2000, month = 1, day = 1, hour = 0}
> now = os.time{year = 2015, month = 11, day = 20}
> t = os.difftime(now, myepoch)
> print(t)
501336000
通过归一化,可以很容易地将用秒表示的时间转换为合法的数字形式表示的时间,即我们可以创建一个带有开始时刻的日期表并将日期表中的秒数设置为想要转换的数字。 例如 :
Lua
T = {year = 2000, month = 1, day = 1, hour = 0}
T.sec = 501336000
t = os.date("%d/%m/%Y", os.time(T))
print(t)
> 20/11/2015
我们还可以使用函数 os.difftime 来计算一段代码的执行时间 。不过,对于这个需求,更好的方式是使用函数 os.clock ,该函数会返回程序消耗的 CPU 时间(单位是秒)。函数os.clock 在性能测试( benchmark )中的典型用法形如 :
Lua
local x = os.clock()
local s = 0
for i = 1, 100000 do s = s + i end
print(string.format("elapsed time: %.2f\n", os.clock() - x))
> elapsed time: 0.00
与函数 os.time 不同,函数 os.clock 通常具有比秒更高的精度,因此其返回值为一个浮点数。具体的精度与平台相关,在 POSIX 系统中通常是 1 毫秒。