☰
Godot 4 Time 单例完全指南:系统时间、Unix 时间戳与精确计时实战
2026/10/13 22:27:45 网站建设 项目流程
  • 文档
  • 教程
  • 游戏开发

【免费下载链接】godot-docs

Godot Engine official documentation

项目地址:https://gitcode.com/GitHub_Trending/go/godot-docs
点击查看免费下载

导读

Time是 Godot 引擎内置的时间单例(继承自Object),负责在系统时间、Unix 时间戳、ISO 8601 字符串与日期字典等格式之间互相转换,同时提供基于引擎内部时钟的精确计时能力。无论是做存档时间戳、排行榜日期、UI 倒计时,还是性能剖析与音频同步,Time都是 Godot 4 中处理时间的首选入口。读完本文,你将掌握Time的全部 20 个方法的用途与参数细节,并能正确区分"系统时钟"与"单调时钟"的使用场景,写出可复用、可运行的 GDScript / C# 时间处理代码。

Time 单例的设计原则

根据 class_time.rst 的官方说明,Time单例提供两类核心能力:

  1. 时间格式转换:在系统时间、Unix 时间戳、ISO 8601 字符串和字典之间互相转换;
  2. 系统时间获取:读取本地时区或 UTC 下的当前日期与时间。

在开始使用之前,必须先理解这个类的三个底层约定,否则很容易写出有隐患的代码。

ISO 8601 与 Proleptic Gregorian(先验格里高利)日历

Time尽量遵循 ISO 8601 标准,所有日期均按Proleptic Gregorian 历法(先验格里高利历)计算,即把现行公历规则无限向过去外推:

  • 1582-10-15的前一天是1582-10-14,而不是历史上真实使用的儒略历日期1582-10-04;
  • 公元 1 年(1 AD)的前一年记为年份0(即 1 BC),再往前一年是-1(2 BC),依此类推。

也就是说,Time可以正确处理负数年份,这在处理历史日期或天文场景时很有用,但在常规业务中要注意它与你熟悉的"公元纪年"在公元前部分的计数差异。

时区与闰秒:Time 不帮你做的三件事

Time的转换类方法有一个共同假设:输入与输出处于同一时区。具体来说:

  • 不自动做时区换算:get_unix_time_from_datetime_dict()等转换方法不会把你给的本地时间转换成 UTC,也不会处理夏令时(DST)偏移;
  • 不处理闰秒:闰秒需要你按需手动补偿;
  • 不解析时区后缀:像"2024-01-01T12:00:00Z"中结尾的Z(表示 UTC)不会被自动处理,你需要手动剥离后再传入。

因此,在把外部系统(如服务器返回的 API 时间)传入Time之前,必须先自行确认其时区语义,并做好归一化。

系统时钟与引擎时钟:两条绝对不能混淆的时间线

Time内部存在两条本质不同的时间来源,官方文档用加粗字体特别强调了这一点:

Important:所有_from_system方法读取的是用户可以手动修改的系统时钟。永远不要用它做精确计时,因为其结果会受到用户或操作系统自动校时的影响。始终使用get_ticks_usec()或get_ticks_msec()做精确计时,它们保证单调递增(即永远不会减小)。

简言之:

用途应使用方法时钟来源特性
显示日期、生成时间戳、记录"现在几点"get_*_from_system()系列系统墙钟(wall clock)会被用户/系统校时影响,可能回拨
测量耗时、性能剖析、音频/动画同步get_ticks_usec()/get_ticks_msec()引擎内部单调时钟永不减小,适合做差值计算

这两条时间线的选择是本文贯穿始终的主线,下文会分别展开。

枚举常量:Month 与 Weekday

Time提供两组枚举,用于与字典转换结果中的month、weekday字段对照取值。

Month 枚举(1~12)

Month枚举的取值与公历月份一一对应,注意它是1 起始的:

常量数值对应月份
MONTH_JANUARY1一月
MONTH_FEBRUARY2二月
MONTH_MARCH3三月
MONTH_APRIL4四月
MONTH_MAY5五月
MONTH_JUNE6六月
MONTH_JULY7七月
MONTH_AUGUST8八月
MONTH_SEPTEMBER9九月
MONTH_OCTOBER10十月
MONTH_NOVEMBER11十一月
MONTH_DECEMBER12十二月

Weekday 枚举(0~6)

Weekday枚举是0 起始的,与月份恰好相反,周日对应0:

常量数值对应星期
WEEKDAY_SUNDAY0星期日
WEEKDAY_MONDAY1星期一
WEEKDAY_TUESDAY2星期二
WEEKDAY_WEDNESDAY3星期三
WEEKDAY_THURSDAY4星期四
WEEKDAY_FRIDAY5星期五
WEEKDAY_SATURDAY6星期六

方法全景:一张转换矩阵看懂 20 个方法

Time的 20 个方法可以按"数据形态"分成三大类,理解这张矩阵是高效使用它的关键:

形态一:字典(Dictionary)

日期时间被表示为字典,按需包含以下键:year、month、day、weekday、hour、minute、second、dst。

方法输入 → 输出
get_date_dict_from_system(utc)系统 →{year, month, day, weekday}
get_date_dict_from_unix_time(unix)Unix 时间戳 →{year, month, day, weekday}
get_datetime_dict_from_system(utc)系统 →{year, month, day, weekday, hour, minute, second, dst}
get_datetime_dict_from_unix_time(unix)Unix 时间戳 →{year, month, day, weekday, hour, minute, second}
get_datetime_dict_from_datetime_string(datetime, weekday)ISO 8601 字符串 → 上述字典
get_time_dict_from_system(utc)系统 →{hour, minute, second}
get_time_dict_from_unix_time(unix)Unix 时间戳 →{hour, minute, second}

形态二:字符串(ISO 8601)

字符串形式的输出严格遵循 ISO 8601:

方法输出格式
get_date_string_from_system(utc)YYYY-MM-DD
get_date_string_from_unix_time(unix)YYYY-MM-DD
get_time_string_from_system(utc)HH:MM:SS
get_time_string_from_unix_time(unix)HH:MM:SS
get_datetime_string_from_system(utc, use_space)YYYY-MM-DDTHH:MM:SS
get_datetime_string_from_unix_time(unix, use_space)YYYY-MM-DDTHH:MM:SS
get_datetime_string_from_datetime_dict(dict, use_space)字典 →YYYY-MM-DDTHH:MM:SS

其中use_space参数为true时,日期与时间之间的分隔符由字母T改为一个空格(如2024-05-01 09:30:00),便于人类阅读的日志输出。

形态三:Unix 时间戳

方法说明
get_unix_time_from_system()返回系统当前 Unix 时间戳,float 类型,保留亚秒精度
get_unix_time_from_datetime_dict(dict)字典 → Unix 时间戳(int)
get_unix_time_from_datetime_string(datetime)ISO 8601 字符串 → Unix 时间戳(int)

注意:get_unix_time_from_system()由操作系统实现,始终返回 UTC,即自 1970-01-01 00:00:00(Unix epoch)以来的秒数。与其它返回整数时间戳的方法不同,它返回float,因此可以用小数部分表示毫秒/微秒级精度。

从系统获取当前时间

读取系统当前时间是Time最常用的功能,几乎所有方法都带一个utc布尔参数:

  • utc = false(默认):返回系统本地时区的值;
  • utc = true:返回 UTC 协调世界时的值。

唯一的例外是get_unix_time_from_system(),它恒为 UTC。

下面的例子演示如何在 GDScript 与 C# 中组合使用这些方法:

# 获取本地时区的当前日期与时间字典 var now = Time.get_datetime_dict_from_system() print(now) # {year: 2026, month: 10, day: 13, weekday: 2, hour: 11, minute: 5, second: 48, dst: 0} # 获取 UTC 时区的当前日期字符串 var utc_date = Time.get_date_string_from_system(true) print(utc_date) # 如 "2026-10-13" # 获取本地时区的当前完整时间字符串(用空格分隔) var local_dt = Time.get_datetime_string_from_system(false, true) print(local_dt) # 如 "2026-10-13 11:05:48" # 带亚秒精度的 Unix 时间戳 var now_unix: float = Time.get_unix_time_from_system() print(now_unix) # 如 1786327548.123
var now = Time.GetDatetimeDictFromSystem(); GD.Print(now); // {year: 2026, month: 10, day: 13, weekday: 2, ...} var utcDate = Time.GetDateStringFromSystem(utc: true); GD.Print(utcDate); var localDt = Time.GetDatetimeStringFromSystem(useSpace: true); GD.Print(localDt); double nowUnix = Time.GetUnixTimeFromSystem(); GD.Print(nowUnix);

真实用例:为 LineEdit 插入当前日期

在 class_lineedit.rst 中,官方给出了一个实际的编辑器扩展示例:在LineEdit的右键菜单里加入"Insert Date"菜单项,点击后把当前日期字符串直接插入光标处,其核心就是一行Time.get_date_string_from_system():

func _on_item_pressed(id): if id == MENU_MAX + 1: insert_text_at_caret(Time.get_date_string_from_system())

这个模式非常适合文本编辑类工具、日志模板插入等功能。

Unix 时间戳:跨系统的通用时间语言

Unix 时间戳(自 1970-01-01 00:00:00 起的秒数)是游戏存档、排行榜、聊天消息等跨系统数据交换的标准格式。Time提供了完整的时间戳 ⇄ 字典 ⇄ 字符串闭环。

时间戳转字典与字符串

var unix_time := 1786327548 # 时间戳 → 日期/时间字典 var date_dict = Time.get_date_dict_from_unix_time(unix_time) var time_dict = Time.get_time_dict_from_unix_time(unix_time) var dt_dict = Time.get_datetime_dict_from_unix_time(unix_time) # 时间戳 → ISO 8601 字符串 print(Time.get_date_string_from_unix_time(unix_time)) # "2026-10-13" print(Time.get_time_string_from_unix_time(unix_time)) # "11:05:48" print(Time.get_datetime_string_from_unix_time(unix_time)) # "2026-10-13T11:05:48"

值得注意的细节:get_datetime_dict_from_unix_time()返回的字典与get_datetime_dict_from_system()在当前时刻下取值完全一致,唯一的例外是dst(夏令时)键——因为夏令时状态无法仅凭 epoch 时刻推算,所以前者不包含dst。

字典与字符串转回时间戳

反向转换同样完整,且不进行任何时区换算,这一点官方文档特别提示:Unix 时间戳通常指 UTC,但这类方法只是"原样"把给定字典/字符串解释为时间戳,因此结果的时区与输入数据一致。

# 字符串 → 时间戳(支持只有日期、只有时间、或两者都有的字符串) var ts1 = Time.get_unix_time_from_datetime_string("2026-10-13T11:05:48") var ts2 = Time.get_unix_time_from_datetime_string("2026-10-13") var ts3 = Time.get_unix_time_from_datetime_string("11:05:48") # 字典 → 时间戳 var dict := {"year": 2026, "month": 10, "day": 13, "hour": 11, "minute": 5, "second": 48} var ts4 = Time.get_unix_time_from_datetime_dict(dict) # 官方文档保证的可逆性:字典转时间戳再转回字典,结果一致 var rt = Time.get_datetime_dict_from_unix_time(Time.get_unix_time_from_datetime_dict(dict)) print(rt == dict) # true

两个容易被忽略的"默认值"行为

get_datetime_string_from_datetime_dict()与get_unix_time_from_datetime_dict()对残缺字典的处理规则相同:

  • 传入空字典时返回0;
  • 省略部分键时,按 Unix epoch 时间戳 0 的对应值补齐,即默认到1970-01-01 00:00:00;
  • 字典中除year、month、day、hour、minute、second以外的其它键(包括dst)会被静默忽略。
print(Time.get_unix_time_from_datetime_dict({})) # 0 print(Time.get_datetime_string_from_datetime_dict({})) # "0" print(Time.get_datetime_string_from_datetime_dict({"year": 2026})) # 其余键按 1970-01-01 补齐

字符串解析的两个细节

get_datetime_dict_from_datetime_string()与get_unix_time_from_datetime_string()都会静默忽略时间字符串中的小数部分——例如"2026-10-13T11:05:48.123"会被当作"2026-10-13T11:05:48"处理。另外前者还有一个独立的weekday布尔参数:当它为false时,返回字典中会去掉weekday键,因为该键的计算开销相对昂贵(需要额外的星期推算);不需要星期信息时应显式传false以提升性能。

精确计时:get_ticks_msec() 与 get_ticks_usec()

这是Time中与"墙上时钟"完全隔离的另一条时间线,专用于性能度量与游戏逻辑计时。两个方法都以引擎启动时刻为原点:

  • get_ticks_msec():返回引擎启动以来经过的毫秒数;
  • get_ticks_usec():返回引擎启动以来经过的微秒数(1 毫秒 = 1000 微秒)。

二者均保证非负、单调递增(永不回拨),且使用64 位整数存储:毫秒计数约 5 亿年后才会回绕,微秒计数约 50 万年后回绕,游戏生命周期内可视为不会溢出。

实战一:手动剖析函数性能

官方在 cpu_optimization.rst 中给出了手动计时函数的推荐写法——用get_ticks_usec()包住待测代码,取差值即可:

var time_start = Time.get_ticks_usec() # 你要计时的函数 update_enemies() var time_end = Time.get_ticks_usec() print("update_enemies() took %d microseconds" % (time_end - time_start))

该文档同时给出了一条重要的工程建议:手动计时应多次运行(1000 次以上)取平均值,因为定时器精度有限,且 CPU 的任务调度具有随机性,单次测量不如多次平均可靠。

实战二:复杂函数的逐段定位

当函数整体性能差、但不知道瓶颈在哪一段时,the_profiler.rst 展示了用 ticks 逐段计数的做法:

# 测量 worker_function() 的运行耗时 var start = Time.get_ticks_usec() worker_function() var end = Time.get_ticks_usec() var worker_time = (end - start) / 1000000.0 # 测量遍历数组做计算的耗时 start = Time.get_ticks_usec() for calc in calculations: result = pow(2, calc.power) * calc.product end = Time.get_ticks_usec() var loop_time = (end - start) / 1000000.0 print("Worker time: %s\nLoop time: %s" % [worker_time, loop_time])

实战三:音频同步的精确基准

get_ticks_usec()的典型应用是音频播放同步。sync_with_audio.rst 中用它建立一个单调的时间基准,再补偿音频系统的输出延迟:

var time_begin var time_delay func _ready(): time_begin = Time.get_ticks_usec() time_delay = AudioServer.get_time_to_next_mix() + AudioServer.get_output_latency() $Player.play() func _process(delta): # 从 ticks 获得经过的时间(单位:秒) var time = (Time.get_ticks_usec() - time_begin) / 1000000.0 # 补偿延迟 time -= time_delay # 可能为负(尚未开始播放) time = max(0, time) print("Time is: ", time)

这个例子正是"系统时钟不可靠、单调时钟才是时间差计算基准"这一原则的完美诠释——音频延迟补偿要求时间线持续单调推进,任何一次系统校时回拨都会破坏同步。

时区信息:读取与格式化

get_time_zone_from_system()

返回当前系统的时区信息字典,包含两个键:

  • bias:与 UTC 的偏差,单位为分钟。注意不是所有时区都整小时偏移(例如尼泊尔为 UTC+5:45),因此用分钟计更精确;
  • name:根据当前用户操作系统区域设置(locale)得到的本地化时区名称。
var tz = Time.get_time_zone_from_system() print(tz) # 例如 {bias: 480, name: "中国标准时间"}(UTC+8 时区,bias 为正表示东八区) # 注意:bias 的符号约定取决于具体系统实现,东八区在部分平台上报告为 480,在另一部分平台报告为 -480

get_offset_string_from_offset_minutes()

把以分钟计的时区偏移转换成 ISO 8601 风格的偏移字符串(±HH:MM),官方文档给出了三个基准例子:

print(Time.get_offset_string_from_offset_minutes(-480)) # "-08:00" print(Time.get_offset_string_from_offset_minutes(345)) # "+05:45" print(Time.get_offset_string_from_offset_minutes(0)) # "+00:00"

该方法的典型用途是把get_time_zone_from_system()返回的bias渲染成人类可读的时区后缀,例如与日期字符串拼接生成带时区偏移的完整时间标识。

从 Godot 3 迁移:OS 时间 API 的替代

Time单例在 Godot 4 中接管了原先分散在OS上的时间功能。迁移指南 upgrading_to_godot_4.rst 明确列出了一条替代关系:

OS.get_system_time_secs()应转换为Time.get_time_dict_from_system()["second"]。

更完整地看,Godot 3 中常见的OS时间调用在 Godot 4 中应迁移到:

Godot 3(OS 上)Godot 4(Time 上)
OS.get_unix_time()Time.get_unix_time_from_system()
OS.get_datetime(utc)Time.get_datetime_dict_from_system(utc)
OS.get_date(utc)Time.get_date_dict_from_system(utc)
OS.get_time(utc)Time.get_time_dict_from_system(utc)
OS.get_system_time_secs()Time.get_time_dict_from_system()["second"]
OS.get_system_time_msecs()Time.get_ticks_msec()
OS.get_ticks_msec()Time.get_ticks_msec()

(前五条对应关系可由 class_time.rst 中各方法的官方描述直接推导;get_system_time_secs()的替代写法则由迁移指南明文给出。)

完整实战:存档系统时间戳方案

把本文介绍的能力组合起来,可以得到一个完整的存档时间戳读写方案,它涵盖了"取系统时间 → 转 Unix 时间戳 → 存盘 → 读盘 → 转回可读字符串"的全流程:

extends Node # 保存时:生成带毫秒精度的 UTC 时间戳写入存档 func save_timestamp(): var now_unix: float = Time.get_unix_time_from_system() save_data["timestamp"] = now_unix # 同时存一份人类可读的本地时间字符串便于调试 save_data["readable"] = Time.get_datetime_string_from_system(false, true) # 加载时:把时间戳还原成可显示的日期字符串 func load_timestamp(): var ts: float = save_data["timestamp"] var ts_int: int = int(ts) print(Time.get_datetime_string_from_unix_time(ts_int)) # "2026-10-13T11:05:48" # 对比两个存档谁更新(直接比较时间戳,勿用系统时间字符串做字典序比较以外的事) func is_newer(a: float, b: float) -> bool: return a > b

几点工程建议:

  1. 存档时间戳统一用 UTC 的 Unix 时间戳,避免玩家切换时区或系统时区设置导致本地时间比较错乱;显示时再通过get_datetime_string_from_unix_time()转成目标格式;
  2. 只做一次方向转换:把"取时间"与"格式化显示"分开,不要在每帧都调用_from_system方法,既浪费又容易引入不一致;
  3. 性能敏感循环里优先用字典缺省:解析字符串时若不需要星期,给get_datetime_dict_from_datetime_string()传weekday = false,省去昂贵的星期推算;
  4. 精确计时永远用 ticks:任何涉及差值的测量(帧耗时、音频同步、动画延迟)都使用get_ticks_usec(),它单调递增、不受用户校时影响,详见 the_profiler.rst 与 cpu_optimization.rst 中的官方实践。

总结:Time 单例使用要点速查

  • 格式转换:字典(get_*_dict_*)、字符串(get_*_string_*)、Unix 时间戳(get_unix_time_*)三形态之间可双向互转,覆盖系统时间与任意给定时刻;
  • 时区语义:转换方法假设输入输出同处一时区,不处理 DST、闰秒与Z后缀;系统读取方法用utc参数切换本地/UTC;get_unix_time_from_system()恒为 UTC;
  • 两条时间线:_from_system系列是用户可调的墙钟,只能用于"显示时间";get_ticks_msec()/get_ticks_usec()是引擎单调时钟,专用于耗时测量与同步;
  • 数据细节:月份枚举 1 起始、星期枚举 0 起始;空字典转时间戳返回0,缺失键按 1970-01-01 补齐;字符串中的小数秒被静默忽略;
  • 迁移:Godot 3 中OS的时间 API 已全部由Time单例取代,迁移写法见 upgrading_to_godot_4.rst。

掌握Time的完整能力后,你可以在 Godot 4 中自信地处理从日志时间戳到跨平台存档同步的所有时间需求。

  • 文档
  • 教程
  • 游戏开发

【免费下载链接】godot-docs

Godot Engine official documentation

项目地址:https://gitcode.com/GitHub_Trending/go/godot-docs
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询