告别时区混乱:程序员必知的时间戳与时区偏移量解决方案

1、前言

针对海外服务器,部署的服务提供给世界多地的用户,客户端用户在不同时区,做数据查询统计就可能存在误差。此处介绍时区偏移量 + LocalDateTime的动态时区解决方案。

2、解决方案

  • 数据库表字段设计,数据创建时间create_time为bitint(20)时间戳
  • 客户端请求服务端时携带时区偏移量localZone
  • 服务端根据时区偏移量计算当地时间,比对数据库中的时间戳来获取指定时间范围内的数据。

3、实现

3.1 数据表结构

AGENT 智能体请求日志记录表结构,此处把入库的时间create_time、update_time设计为时间戳。

CREATE TABLE `agent_log` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    `user_id` varchar(32) NOT NULL COMMENT '用户ID',
    `client_id` varchar(64) NOT NULL COMMENT '客户端ID',
    `total_count` int(11) DEFAULT 0 COMMENT '总次数',
    `available_count` int(11) DEFAULT 0 COMMENT '可用对话次数',
    `package_name` varchar(80) NOT NULL COMMENT '产品包',
    `question` text COMMENT '问题',
    `reply` text COMMENT '回复',
    `request_json` mediumtext DEFAULT NULL COMMENT '接口调用请求内容',
    `response_text` mediumtext DEFAULT NULL COMMENT '接口调用响应结果',
    `create_time` bigint(20) NOT NULL COMMENT '创建时间',
    `update_time` bigint(20) NOT NULL COMMENT '修改时间',
    PRIMARY KEY (`id`),
    KEY `idk_package_name_user_id` (`package_name`, `user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='AGENT记录'

3.2 客户端传输时区偏移量

客户端可以将时区偏移量计算出来,传给服务端。
如下所示,传时区偏移xx毫秒过去,示例
例如,对于Asia/Shanghai 时区,其偏移量可能是28800000毫秒(即东八区相对于0时区的偏移量)

val calendar = Calendar.getInstance();
val zone = calendar.timeZone.getOffset(calendar.timeInMillis);

3.3 计算动态时区的时间戳

(1)通过LocalDateTime、LocalTime可以计算出当地时间处于早上、中午、晚上。

    Integer localZone = request.getLocalZone();
    Long currentTime = DateUtils.getCurrentTime();
    LocalDateTime localDateTime = Instant.ofEpochMilli(currentTime).atZone(ZoneOffset.ofTotalSeconds(localZone / 1000)).toLocalDateTime();
    LocalTime time = localDateTime.toLocalTime();
    LocalTime morningStart = LocalTime.of(6, 0);
    LocalTime noonStart = LocalTime.of(12, 0);
    LocalTime eveningStart = LocalTime.of(18, 0);
    Boolean isMorning = time.isAfter(morningStart) && time.isBefore(noonStart);
    Boolean isNoon = time.isAfter(noonStart) && time.isBefore(eveningStart);
    log.info(isMorning ? "早上" : (isNoon ? "下午" : "晚上"));

(2)计算出当地时间的 0点 00:00:00 和 24点23:59:59 的时间戳

    Long currentTime = DateUtils.getCurrentTime();
    Integer localZone = request.getLocalZone();
    // 计算当天的起始时间和结束时间
    LocalDateTime localDateTime = Instant.ofEpochMilli(currentTime).atZone(ZoneOffset.ofTotalSeconds(localZone * 60)).toLocalDateTime();
    LocalDate localDate = localDateTime.toLocalDate();
    LocalDateTime startOfDay = localDate.atStartOfDay();
    LocalDateTime endOfDay = localDate.atTime(23, 59, 59);
    ZonedDateTime startOfDayZoned = startOfDay.atZone(ZoneOffset.ofTotalSeconds(localZone / 1000));
    ZonedDateTime endOfDayZoned = endOfDay.atZone(ZoneOffset.ofTotalSeconds(localZone / 1000));
    long startOfDayTimestamp = startOfDayZoned.toInstant().toEpochMilli();
    long endOfDayTimestamp = endOfDayZoned.toInstant().toEpochMilli();
    log.info("查询起始时间戳:" + startOfDayTimestamp + "," + endOfDayTimestamp);

3.4 动态时区查询当地一天的数据量

SELECT count(1)
FROM table_name
WHERE 1 = 1
  and create_time between #{startOfDayTimestamp} and #{endOfDayTimestamp}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

niaonao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值