说明
由于时间处理在算网中起到了更重要的作用:不仅是进行时间计算,而是成为架构规范的一部分,因此需要对ATimer进行升级,成为一个基础依赖对象。
内容
1 设计
ATimer的作用更多的是进行即时计算,ATimer2将拓展为基础运行工具,带来的变化:
- 1 静态参数固化。对象将设定一个固定的静态参数,而不是即时生成。
- 2 对象化同时微服务化。既是对象,也是微服务。通过GlobalFunc进行统一调用
- 3 更多周期。按秒、分钟、天、周、月、年构成更完整的偏移周期。
因为太阳历的偏差,导致了需要在闰年补一天,所以没办法进行简单的推算来计算时间。而且每个月还有大小月,规则会比较麻烦。
所以我想先通过推断生成一个基本时间轴,通过基本时间轴可以进行大致的定位,然后再通过推算来处理具体的时间。
所以第一步是生成基本时间轴,第二步是进行时间的对齐与推断。
每个月都有1号。
假设我们获得了每个月1号的时间戳,那么就可以进行时间推断了。生成这样一个字典就屏蔽了平闰年与大小月的差异。
{'1970-01': -28800.0,
'1970-02': 2649600.0,
'1970-03': 5068800.0,
...
1.1 字符转时间戳
标准时间格式为yy-mm-dd HH:MM:SS,例如 2000-01-01 00:00:00。
只要将年月目标分yymon提取出来,查表就可以得到其月基准时间戳 month_ts,剩下的天、时、分、秒可以按照固定规则映射 day_ts = day * 86400 + hour * 3600 + minute * 60 + seconds
。
一个合法的日期表达,其时间戳必然小于下一个月的基准时间戳。
当然,这个约束可以在标准时间格式转换时加以确认。
1.2 时间戳转字符
选择比时间戳小的最近一个月基准时间戳,获取年月基准时间戳。然后将当前时间戳与月基准时间戳取差值,这个差值按照固定规则推算出日,时,分,秒。对应的字符则为月基准字符 + 推算字符。
2 开发
从实用的角度出发,时间轴覆盖范围为1970~2970, 这样的范围属于足够用且轴长度不会太长的样子。
2.1 月基准日历
第一步需要推算按天的日历。
既然确定了年份,那么按照平闰年生成千年历。然后以第一天作为基准(可以在这里考虑时区差),按照86400递增获取每一天的时间戳。
然后将1号的部分抽取出来,就形成了月基准时间轴。容易知道这个轴大约是12000的长度,很小的尺寸。在具体计算时,还可以根据要面对的问题临时抽取更小的轴进行计算。
三个入参:基准年、偏移年和时区
生成千年的天列表
base_year = 1970
next_years = 1000
# 千年历的制作
ty_calendar = []
for some_year in list(range(base_year , base_year +next_years)):
if is_ryear(some_year):
day_list = b_list
else:
day_list = a_list
tem_day_list = ['%s-%s' % (some_year, some_day) for some_day in day_list ]
ty_calendar = ty_calendar + tem_day_list
生成一个frame
# 日表
ty_frame = pd.DataFrame()
ty_frame['date'] = list(ty_s)
ty_frame['day'] = list(range(len(ty_frame)))
ty_frame['seconds'] = ty_frame['day'] * 86400
ty_frame['tz8_seconds'] = ty_frame['seconds'] - 8 * 3600
筛选后生成年月为基准的表
sel = ty_frame['date'].apply(lambda x: True if x.endswith('-01') else False)
ty_yymon_df = ty_frame[sel]
得到一个基于年月的有序字典
yymon_dict = cl.OrderedDict(zip(list(ty_yymon_df['date']),list(ty_yymon_df['tz8_seconds'])))
2.2 转换
2.2.1 将字符转为时间戳
# 分离月 + 日:时:分:秒
def depart_mon_ddhhmmss(dt_str):
pos = dt_str.rfind('-')
yymon = dt_str[:pos]+'-01'
ddhhmmss = dt_str[pos+1:]
return yymon, ddhhmmss
def ddhhmmss2s(dhms_str):
_d, _hms = dhms_str.split()
_h,_m,_s = _hms.split(':')
return 86400*(int(_d)-1) + 3600 *(int(_h)) + 60 *(int(_m)) + int(_s)
# 1 字符转时间戳
some_str = '2023-01-24 17:44:44'
yymon, dhms = depart_mon_ddhhmmss(some_str)
ts = yymon_dict[yymon] + ddhhmmss2s(dhms)
1674553484
2.2.2 将时间戳转为字符
# 时间戳的轴,长度 12000
time_axis = np.array(list(yymon_dict.values()))
dt_axis = list(yymon_dict.keys())
pos= max(np.where(time_axis <= ts)[0])
base_dt = dt_axis[pos]
base_ts = time_axis[pos]
gap_ts = ts - base_ts
def gapts2day(some_ts):
_d = some_ts//86400
_res1 = some_ts % 86400
_h = _res1 //3600
_res2 = _res1 % 3600
_m = _res2 //60
_s = _res2 % 60
return "-{:02d} {:02d}:{:02d}:{:02d}".format( _d + 1,_h,_m,_s)
char_ts = base_dt[:7] + gapts2day(gap_ts)
'2023-01-24 17:44:44'
转换功能是最基础的功能,在大规模时间转换上,应该会有不错的表现,后续可以验证一下。就在时间戳的转换和还原上,用pandas的apply进行简单的函数并行调用进行实验。未来如果效率需要,是可以将要转换的列直接用矩阵方式进行计算的,这样会快很多。
下一个核心功能的就是偏移计算了,偏移计算分为两类:
- 1 已知某一个时间,需要知道之前或者之后的时间。
- 2 已知两个时间,需要知道他们之间的差距。
这部分先写到这里吧。