跳到主要内容

逻辑时间工具库

概述

这是一个基于 GS 语言的逻辑时间工具库,用于管理和操作服务器的逻辑时间,区别于系统原生的基于真实时间的接口。该工具库提供了丰富的时间管理、计算和事件通知功能,为游戏服务器提供统一的时间服务。

主要特性

逻辑时间管理

  • 获取和维护服务器的逻辑时间(now)
  • 支持设置时区(set_time_zone, get_time_zone)
  • 支持设置时间偏移量(set_time_offset, get_time_offset)
  • 支持暂停和恢复时间流动(pause_time, resume_time)
  • 支持偏移时间持久化和进程间同步
    • 临时将持久化数据保存在本地文件中:/pkg/logical_time/logical_time_d.o

常用时间函数

  • 时间格式化和解析(format_local_time, parse_local_time)
  • 时间比较(同一分、同一小时、同一天、同一周、同一月、同一年)
  • 计算两个时间累计经过多少天
  • 计算指定时间是今年、今月、今周、今天、当前小时的开始和结束时间
  • 闰年计算相关
  • 友好时间文本展示
  • 不处理夏令时相关逻辑

事件通知机制

  • 时间服务就绪事件
  • 时间流动暂停事件
  • 时间流动恢复事件
  • 时间偏移量变更事件

每日重置偏移时间管理(Daily Reset Offset)

逻辑时间库支持设置每日重置偏移时间,用于定义游戏的"一天"开始时间。这个功能对于需要在非午夜时间点进行每日重置的游戏特别有用。

函数原型函数作用
int get_daily_reset_offset_time()获取每日重置偏移时间(秒)
int get_daily_reset_offset_hours()获取每日重置偏移时间(小时)

每日重置周期计算(Daily Reset Cycle)

基于设置的每日重置偏移时间,提供一系列计算和判断函数:

函数原型函数作用
int get_previous_reset_day_start(int timestamp = 0)获取前一个重置天的开始时间
int get_previous_reset_day_end(int timestamp = 0)获取前一个重置天的结束时间
int get_reset_day_start(int timestamp = 0)获取指定时间所在天的重置开始时间
int get_reset_day_end(int timestamp = 0)获取指定时间所在天的重置结束时间
int get_next_reset_day_start(int timestamp = 0)获取下一个重置天的开始时间
int get_next_reset_day_end(int timestamp = 0)获取下一个重置天的结束时间
bool is_same_reset_day(int time1, int time2)判断两个时间是否在同一个重置周期内
bool is_today_reset_cycle(int timestamp)判断指定时间是否在今天的重置周期内
bool is_yesterday_reset_cycle(int timestamp)判断指定时间是否在昨天的重置周期内
int between_reset_days(int time1, int time2)计算两个时间之间相差多少个重置天数
int get_previous_reset_week_start(int timestamp = 0)获取前一个重置周的开始时间
int get_previous_reset_week_end(int timestamp = 0)获取前一个重置周的结束时间
int get_reset_week_start(int timestamp = 0)获取指定时间所在周的重置开始时间(周一的重置时间)
int get_reset_week_end(int timestamp = 0)获取指定时间所在周的重置结束时间(周日的重置结束时间)
int get_next_reset_week_start(int timestamp = 0)获取下一个重置周的开始时间
int get_next_reset_week_end(int timestamp = 0)获取下一个重置周的结束时间
int get_previous_reset_month_start(int timestamp = 0)获取前一个重置月的开始时间
int get_previous_reset_month_end(int timestamp = 0)获取前一个重置月的结束时间
int get_reset_month_start(int timestamp = 0)获取指定时间所在月的重置开始时间(1号的重置时间)
int get_reset_month_end(int timestamp = 0)获取指定时间所在月的重置结束时间(月末的重置结束时间)
int get_next_reset_month_start(int timestamp = 0)获取下一个重置月的开始时间
int get_next_reset_month_end(int timestamp = 0)获取下一个重置月的结束时间
int get_previous_reset_year_start(int timestamp = 0)获取前一个重置年的开始时间
int get_previous_reset_year_end(int timestamp = 0)获取前一个重置年的结束时间
int get_reset_year_start(int timestamp = 0)获取指定时间所在年的重置开始时间(1月1号的重置时间)
int get_reset_year_end(int timestamp = 0)获取指定时间所在年的重置结束时间(12月31号的重置结束时间)
int get_next_reset_year_start(int timestamp = 0)获取下一个重置年的开始时间
int get_next_reset_year_end(int timestamp = 0)获取下一个重置年的结束时间

文件结构

  • logical_time.gs - 对外接口封装,绝大部分接口都以逻辑时间戳为基础
  • LogicalTimeD.gs - 逻辑时间管理守护进程,提供逻辑时间的偏移量及时区管理,以及简单的内部事件注册回调支持
  • date_formatter.gs - 时间格式化和解析工具类,提供时间格式化和解析的功能,可用于真实时间的工具类函数集合

组件接口

logical_time.gs

函数原型函数作用
int now()获取当前逻辑时间戳
bool set_time_zone(int time_zone)设置时区(-12~14)
int get_time_zone()获取当前时区
bool set_time_offset(int offset)设置时间偏移量(秒)
int get_time_offset()获取时间偏移量
bool pause_time()暂停时间流动
bool resume_time()恢复时间流动
bool is_time_paused()判断时间是否暂停
int add_time(int timestamp, int years = 0, int months = 0, int days = 0, int hours = 0, int minutes = 0, int seconds = 0)向指定时间戳添加时间
int add_years(int timestamp, int years)增加年份
int add_months(int timestamp, int months)增加月份
int add_days(int timestamp, int days)增加天数
int add_hours(int timestamp, int hours)增加小时
int add_minutes(int timestamp, int minutes)增加分钟
int add_seconds(int timestamp, int seconds)增加秒数
bool is_same_year(int time1, int time2)判断两个时间是否在同一年
bool is_same_month(int time1, int time2)判断两个时间是否在同一月
bool is_same_day(int time1, int time2)判断两个时间是否在同一天
bool is_same_hour(int time1, int time2)判断两个时间是否在同一小时
bool is_same_minute(int time1, int time2)判断两个时间是否在同一分钟
int get_hour_start(int timestamp)获取指定时间所在小时的开始时间
int get_hour_end(int timestamp)获取指定时间所在小时的结束时间
int get_day_start(int timestamp)获取指定时间所在天的开始时间
int get_day_end(int timestamp)获取指定时间所在天的结束时间
int get_week_start(int timestamp)获取指定时间所在周的开始时间
int get_week_end(int timestamp)获取指定时间所在周的结束时间
int get_month_start(int timestamp)获取指定时间所在月的开始时间
int get_month_end(int timestamp)获取指定时间所在月的结束时间
int get_year_start(int timestamp)获取指定时间所在年的开始时间
int get_year_end(int timestamp)获取指定时间所在年的结束时间
string format_local_time(string format, int timestamp = 0)格式化本地时间
int parse_local_time(string format, string date_str)解析本地时间字符串
int between_days(int time1, int time2)计算两个时间戳之间相差的天数,返回正数表示 time2 在 time1 之后,负数表示 time2 在 time1 之前
bool is_leap_year(int year)判断是否为闰年年份

样例

基本时间操作

import pkg.logical_time;

// 获取当前时间戳
int now = logical_time.now();

// 获取当前时间戳(最简写法)
int now = now();

// 设置时区为东八区
set_time_zone(8);

// 设置时间偏移(提前1小时)
set_time_offset(3600);

// 暂停时间
pause_time();

// 恢复时间
resume_time();

时间计算

// 获取当前时间
int now = logical_time.now();

// 计算1年2个月3天4小时5分钟6秒后的时间
int future = logical_time.add_time(now, 1, 2, 3, 4, 5, 6);

// 计算昨天同一时间
int yesterday = logical_time.add_days(now, -1);

// 计算下个月初
int next_month_start = logical_time.get_month_start(logical_time.add_months(now, 1));

时间比较

// 判断两个时间是否在同一天
int time1 = logical_time.parse_local_time("%Y-%m-%d %H:%M:%S", "2025-03-27 10:00:00");
int time2 = logical_time.parse_local_time("%Y-%m-%d %H:%M:%S", "2025-03-27 15:30:00");
bool same_day = logical_time.is_same_day(time1, time2); // true

// 判断是否今天
bool is_today = logical_time.is_same_day(time1, logical_time.now());

时间区间

// 获取今天的开始和结束时间
int today_start = logical_time.get_day_start(logical_time.now());
int today_end = logical_time.get_day_end(logical_time.now());

// 获取本月的开始和结束时间
int month_start = logical_time.get_month_start(logical_time.now());
int month_end = logical_time.get_month_end(logical_time.now());

// 获取本年的开始和结束时间
int year_start = logical_time.get_year_start(logical_time.now());
int year_end = logical_time.get_year_end(logical_time.now());

时间格式化

格式符描述示例
%Y四位数的年份2025
%y两位数的年份25
%m两位数的月份(01-12)03
%B完整的月份名称March
%b缩写的月份名称Mar
%d两位数的日期(01-31)28
%e日期,前面不补零 (1-31)4
%H两位数的小时(24小时制,00-23)15
%I两位数的小时(12小时制,01-12)03
%pAM/PM 标识PM
%M两位数的分钟(00-59)30
%S两位数的秒数(00-59)45
%f三位数的毫秒数(000-999)038
%w一周中的第几天(0-6,0表示周日)5
%j一年中的第几天(001-366)087
%U一年中的第几周(00-53,周日为一周的第一天)12
%W一年中的第几周(00-53,周一为一周的第一天)12
%a缩写的星期名称Fri
%A完整的星期名称Friday
%c本地日期和时间表示Fri Mar 28 15:30:45 2025
%x本地日期表示03/28/25
%X本地时间表示15:30:45
%%字面上的百分号%
// 格式化当前时间
string formatted = logical_time.format_local_time("%Y-%m-%d %H:%M:%S");

// 解析时间字符串
int timestamp = logical_time.parse_local_time("%Y-%m-%d %H:%M:%S", "2025-03-27 15:30:00");

// 自定义格式化
string custom = logical_time.format_local_time("今天是%A,%Y年%m月%d日");

事件处理

// 注册每日重置回调
logical_time.register_daily_reset((: my_daily_reset_handler :));

// 注册时间暂停回调
logical_time.register_callback("time_pause", (: my_time_pause_handler :));

// 注册时间恢复回调
logical_time.register_callback("time_resume", (: my_time_resume_handler :));

每日重置偏移时间使用

// 注意:daily_reset_offset在启动时通过环境变量DAILY_RESET_OFFSET设定,运行时不可修改
// 获取当前重置偏移配置
int current_offset_hours = logical_time.get_daily_reset_offset_hours();
trace_info("当前重置偏移配置: %d小时", current_offset_hours);

// 获取当前重置周期的开始和结束时间
int cycle_start = logical_time.get_reset_day_start();
int cycle_end = logical_time.get_reset_day_end();

// 获取前一天的重置周期
int prev_start = logical_time.get_previous_reset_day_start();
int prev_end = logical_time.get_previous_reset_day_end();

// 获取下一天的重置周期
int next_start = logical_time.get_next_reset_day_start();

// 判断两个时间是否在同一个重置周期内
bool same_cycle = logical_time.is_same_reset_day(time1, time2);

// 判断一个时间是否在今天的重置周期内
bool is_today = logical_time.is_today_reset_cycle(some_timestamp);

// 计算两个时间之间跨越了多少个重置周期
int cycles_diff = logical_time.between_reset_days(time1, time2);

基于重置偏移时间的时间区间计算

// 获取当前时间在重置概念下的各种时间区间
int reset_day_start = logical_time.get_reset_day_start(); // 今天5:00
int reset_day_end = logical_time.get_reset_day_end(); // 明天4:59:59

int reset_week_start = logical_time.get_reset_week_start(); // 本周一5:00
int reset_week_end = logical_time.get_reset_week_end(); // 本周日4:59:59

int reset_month_start = logical_time.get_reset_month_start(); // 本月1号5:00
int reset_month_end = logical_time.get_reset_month_end(); // 下月1号4:59:59

int reset_year_start = logical_time.get_reset_year_start(); // 今年1月1号5:00
int reset_year_end = logical_time.get_reset_year_end(); // 明年1月1号4:59:59

// 与传统时间区间的对比
int traditional_day_start = logical_time.get_day_start(); // 今天0:00
int reset_day_start_5am = logical_time.get_reset_day_start(); // 今天5:00(或昨天5:00,取决于当前时间)

Previous/Next 重置时间区间使用

// 获取当前每日重置时间配置(不可运行时修改)
int current_reset_hours = logical_time.get_daily_reset_offset_hours();

// 获取前一天/下一天的重置区间
int prev_day_start = logical_time.get_previous_reset_day_start(); // 昨天5:00
int prev_day_end = logical_time.get_previous_reset_day_end(); // 今天4:59:59
int next_day_start = logical_time.get_next_reset_day_start(); // 明天5:00
int next_day_end = logical_time.get_next_reset_day_end(); // 后天4:59:59

// 获取前一周/下一周的重置区间
int prev_week_start = logical_time.get_previous_reset_week_start(); // 上周一5:00
int prev_week_end = logical_time.get_previous_reset_week_end(); // 上周日4:59:59
int next_week_start = logical_time.get_next_reset_week_start(); // 下周一5:00
int next_week_end = logical_time.get_next_reset_week_end(); // 下周日4:59:59

// 获取前一月/下一月的重置区间
int prev_month_start = logical_time.get_previous_reset_month_start(); // 上月1号5:00
int prev_month_end = logical_time.get_previous_reset_month_end(); // 本月1号4:59:59
int next_month_start = logical_time.get_next_reset_month_start(); // 下月1号5:00
int next_month_end = logical_time.get_next_reset_month_end(); // 下下月1号4:59:59

// 获取前一年/下一年的重置区间
int prev_year_start = logical_time.get_previous_reset_year_start(); // 去年1月1号5:00
int prev_year_end = logical_time.get_previous_reset_year_end(); // 今年1月1号4:59:59
int next_year_start = logical_time.get_next_reset_year_start(); // 明年1月1号5:00
int next_year_end = logical_time.get_next_reset_year_end(); // 后年1月1号4:59:59

// 游戏应用示例:检查玩家是否错过了上周的活动
bool missed_last_week_activity = (player_last_activity_time < prev_week_start);

注意事项

  1. 时间偏移量修改会影响所有基于逻辑时间的功能,请谨慎使用
  2. 时间暂停期间,获取的逻辑时间将保持在暂停时刻
  3. 注册的事件回调必须是合法的函数引用
  4. 事件回调会在异步协程中执行,处理回调逻辑时需注意线程安全

性能考虑

  1. 时间格式化和解析操作相对耗时,避免在高频循环中使用
  2. 时间偏移和暂停操作会在进程间同步并落地保存,避免频繁调用

附录:组件接口性能测试数据(每次调用平均耗时ms)(asmjit)

https://leiting.feishu.cn/wiki/FBgTwlz9zi78Czkm1hKcRuUcnyb