如何结算指定时区的订单

如果程序需要根据指定时区来处理某些业务,即服务器端要有时区概念。则有没有一套比较好的方案。

例如电商系统,以东京时间凌晨两点结算过去一天的订单总量。服务器默认时区是北京时区,数据库存储的是长整型毫秒级时间戳。

通用规范

如果需要处理的时间是当前和未来的时间,则使用长整型毫秒级时间戳进行存储和计算。

遵循存储与显示分离的策略。服务端存储整型时间戳,客户端显示格式化结果。

如:用户创建一笔订单,服务器以自己的时间为基准生成长整型毫秒级时间戳作为该笔订单的创建时间,然后存入数据库。用户查看该笔订单时,服务器从数据库中取出该笔订单并返回,客户端根据所在时区格式化成指定时间格式。

探索方案

先给出一个公式:

指定日期时间 - 偏移量 - 绝对起始时间常量 = 时间戳

xxxx年xx日xx时xx分xx秒 - 偏移几个小时如+8:00 - 1970年01月01日00时00分00秒 = 时间戳

除去绝对起始时间常量外,已知任意两个变量可以求出第三个变量

考虑,东京时间的2019年8月15日当天的时间戳范围是多少,与北京时间的2019年8月15日的时间戳范围是一样的吗?

//东京2019年8月15日0时0分0秒的时间戳:
ZonedDateTime.parse("2019-08-15T00:00:00.000+09:00").toInstant().toEpochMilli();
//得出  1565794800000

//北京2019年8月15日0时0分0秒的时间戳:
ZonedDateTime.parse("2019-08-15T00:00:00.000+08:00").toInstant().toEpochMilli();
//得出  1565798400000

可以看出,两个不同时区的起始时间的时间戳是不同的。可以想象,不论用哪个时区,只要时间过一毫秒,时间戳就加1。而东京先看到太阳,此刻时间戳为a。而北京要过一小时后才看到太阳,此刻时间戳记为b。而a加一小时才等于b,所以a要比b小。

结论:同一个本地时间,时区偏移量越大的,其时间戳越小。

经过上面的讨论。可以形成一个思路去解决上面遇到的问题:

  1. 使用cron表达式,指定时区指定时间执行任务。

  2. 任务开始,计算东京时间2019年8月15日0时0分0秒的时间戳作为起始时间,计算东京时间2019年8月15日23点59分59秒的时间戳作为终止时间。扫描表中创建时间在这两个范围内的订单。则扫描到的订单均为东京时间2019年8月15日生成的。

注意点

  1. 此类逻辑处理完全依赖于业务是否考虑使用指定时区。如果不指定,则默认使用服务器的本地时可以解决。如果指定,则需要完整的考虑时区问题。

  2. 如果业务有时区问题,需要发布公告完善的指明业务支持的时区问题,规避非指定时区的人使用服务时造成的困扰。