利用Python脚本实现固定格式时间差值精确计算(Decimal)
前些天测试程序,需要记录下程序不同模块的各自运行时间。
由于各个模块之间有各自的关联,运行时间会受到数据量大小的影响,所以只能一步一步运行并记录下时间再根据需要进行差值计算。
时间格式如此: XX(小时):XX(分钟):XX.XXX(秒) [注:秒精确到千分位]
但个别程序由于数据量过大,运行十分缓慢,需要记录的时间差值也很多,重复过程繁琐。
虽说算差值不是什么难事,都是些简单的数值计算,但是在面对上百个需要测试的程序,我实在是懒得去一个一个算。
正所谓“懒惰为人类进步的阶梯(误)”,因此,我萌生了使用Python编写一个可以进行时间差计算的小程序的想法。(2333...)
一、确定思路
1.在本程序中,我采用的是统一单位的方法。即先将时间统一为秒,再对其进行减法运算。那么我们只需要将这个过程通过代码复现即可。
2.在经过差值计算后,我们仍需要将结果重新格式化为原规定格式(XX:XX:XX.XXX)
3.鉴于需要重复使用,可以考虑加入循环,特定条件退出运算
二、拟定程序结构
本程序有五大部分:
(1).计算主函数
(2).格式化函数
(3).单位转换函数
(4).程序主函数
(5).调用主函数
三、编写程序
但是,在开始之前,我们需要知晓一件事。
我们程序在进行差值计算时,会精确到秒的千分位,按理说我们定义浮点数据类型即可。
然而,在计算机中,采用浮点数进行计算无法达到“精确到xx位小数点”的精度。
为什么呢?我们实验一下:
首先定义两个浮点数
然后我们进行加法运算
这里就出了问题,答案并不是我们想要的6.3,而是不知什么鬼的6.300000000000001。
这里面最重要的原因就是,计算机采用二进制存储数据,使用浮点数并不能很好的表示十进制数据,会有一些"小误差"。
(这里(包括这两个数4.2 2.1) 借鉴了http://www.hongweipeng.com/index.php/archives/1356/,栖迟大佬)
那我们如何才能做到十进制精确计算呢?
答案就是使用Python内置模块decimal,它可以帮助我们把浮点数转化为十进制类型,并且支持所有常用数学运算
示例:
1 from decimal import Decimal 2 3 a = Decimal('4.2') 4 b = Decimal('2.1') 5 6 print(a + b)
结果:
我们发现,结果不再是鬼畜的6.300000000000001,是正常的6.3。
而且可以观察到,经过Decimal转化得到的是一个Decimal对象,使用时需要注意,在转化浮点数时,需要其字符串化。
那么解决了精确运算的问题,我们现在可以进行程序的编写了。
1.首先是计算主函数:
我们进行时间差计算,需要两个参数,起始时间和结束时间
然后将两参数单位统一,得到统一单位后的时间差值,再对其进行格式化以及格式的还原即可
本程序中计算主程序即是对参数单位进行统一的函数,代码如下:
(当然在程序开头要声明引入Decimal)
1 def Time_Cal(): # 定义函数 2 print("----------------XX:XX:XX----------------") # 打印分行符 3 # 程序运行后在这里输入起始时间和结束时间 4 start_time = input("Enter your Start Time: ") 5 stop_time = input("Enter your Stop Time: ") 6 # 使用split方法对输入的字符串以“:”为分隔符进行分割处理,并存为列表 7 start_list = start_time.split(':') 8 stop_list = stop_time.split(':') 9 # 再通过不同单位划分三个部分,分别从列表获取小时、分钟、秒的值 10 start_hour = Decimal(start_list[0]) 11 start_minu = Decimal(start_list[1]) 12 start_sec = Decimal(start_list[2]) 13 stop_hour = Decimal(stop_list[0]) 14 stop_minu = Decimal(stop_list[1]) 15 stop_sec = Decimal(stop_list[2]) 16 # 这里就是对获取到的各个部分的值进行单位统一(基本单位为秒) 17 value_start = start_hour * 3600 + start_minu * 60 + start_sec 18 value_stop = stop_hour * 3600 + stop_minu * 60 + stop_sec 19 return value_stop,value_start 20 # 返回单位统一后的起始时间和结束时间,此时这两个参数均以秒数为单位
2.格式化函数:
这里格式化的原因是因为在我们计算结束后,有些位置的值为各位数,因标准格式(XX:XX:XX.XXX)规范,这里直接对缺位处进行补零
但这里有三点要考虑,即:小时位是否需要补零、分钟位是否需要补零、秒位是否需要补零
所以我们需要对小时、分钟、秒三部分进行判断:
同样,当时间值只有分钟秒数甚至只有秒数时也要进行类似判断。
那么在这种情况下,我们则优先对秒数处格式化函数进行定义,这样在后面判断分钟和小时是否需要补零时可以直接调用函数,减少代码量。
代码:
1 def S_Formatting(len_s,s):
2 # 这里我们先定义对秒处进行格式化函数
3 if len_s < 2: # 判断秒数位是否为个位数
4 s_sec = '0' + s # 个位则补零
5 else:
6 s_sec = s # 反之则直接向下传递
7 return s_sec # 返回秒数位置的最终值
之后对分钟和小时处判断,直接调用即可:
对分钟:
1 def M_Formatting(len_m,m,len_s,s): # 注意这里参数要包含秒数位的值 2 if len_m < 2: 3 s_minu = '0' + m 4 s_sec = S_Formatting(len_s,s) # 判断秒数位时直接调用即可 5 else: 6 s_minu = m 7 s_sec = S_Formatting(len_s,s) 8 return s_minu,s_sec # 返回分钟和秒数的最终值
对小时:
1 def H_Formatting(len_h,h,len_m,m,len_s,s): # 参数同样要包含分钟和秒数值 2 if len_h < 2: 3 s_hour = '0' + h 4 s_minu,s_sec = M_Formatting(len_m,m,len_s,s) # 调用分钟格式化函数 5 6 else: 7 s_hour = h 8 s_minu,s_sec = M_Formatting(len_m,m,len_s,s) 9 return s_hour,s_minu,s_sec # 返回小时、分钟、秒数的最终值
3.单位转换函数:
单位转换函数是在单位统一后,格式化之前,对传入的起始时间和结束时间进行差值运算后,通过秒数值的大小判断分钟或者秒数位是否需要进位。
并在转换单位后调用格式化函数并将其组合为规定格式(XX:XX:XX.XXX)返回最终值。
代码:
1 def Switch(value_stop,value_start): # 这里传入转化为秒的起始时间和结束时间 2 D_value = Decimal(value_stop - value_start) # 计算得到时间差,为达到精确计算,调用Decimal方法 3 # 从这里开始对是否需要进位进行判断 4 if D_value >= 3600: # 这里是小时位需要进位的情况 5 D_hour = int(D_value) # 取整,去掉小数点 6 hour = D_hour // 3600 # 同样取整,做除 7 D_minu = D_hour - hour * 3600 # 得到包含分钟位的值 8 minu = D_minu // 60 # 取整,做除 9 sec = D_value - hour * 3600 - minu * 60 # 用时间差减去得到的小时和分钟就是秒数的值 10 11 # 调用格式化函数 12 s_hour,s_minu,s_sec = H_Formatting(len(str(hour)),str(hour),len(str(minu)),str(minu),len(str(int(sec))),str(sec)) 13 print("The Time Difference is: " + s_hour + ':' + s_minu + ':' + s_sec) # 组合为规范格式输出 14 elif 60 <= D_value < 3600: # 这里是分钟位进位的情况,小时值为零 15 minu = D_value // 60 16 sec = D_value - minu * 60 17 s_minu,s_sec = M_Formatting(len(str(minu)),str(minu),len(str(int(sec))),str(sec)) 18 print("The Time Difference is: " + '00:' + s_minu + ':' + s_sec) 19 else: # 仅有秒数值的情况 20 sec = D_value 21 s_sec = S_Formatting(len(str(int(sec))),str(sec)) 22 print("The Time Difference is: " + '00:' + '00:' + s_sec)
最后编写主函数调用函数即可。
运行结果:
1 from decimal import Decimal # 调用Decimal模块 2 3 def Time_Cal(): 4 print("----------------XX:XX:XX----------------") 5 start_time = input("Enter your Start Time: ") 6 stop_time = input("Enter your Stop Time: ") 7 start_list = start_time.split(':') 8 stop_list = stop_time.split(':') 9 start_hour = Decimal(start_list[0]) 10 start_minu = Decimal(start_list[1]) 11 start_sec = Decimal(start_list[2]) 12 stop_hour = Decimal(stop_list[0]) 13 stop_minu = Decimal(stop_list[1]) 14 stop_sec = Decimal(stop_list[2]) 15 value_start = start_hour * 3600 + start_minu * 60 + start_sec 16 value_stop = stop_hour * 3600 + stop_minu * 60 + stop_sec 17 return value_stop,value_start 18 19 def S_Formatting(len_s,s): 20 if len_s < 2: 21 s_sec = '0' + s 22 else: 23 s_sec = s 24 return s_sec 25 26 def M_Formatting(len_m,m,len_s,s): 27 if len_m < 2: 28 s_minu = '0' + m 29 s_sec = S_Formatting(len_s,s) 30 else: 31 s_minu = m 32 s_sec = S_Formatting(len_s,s) 33 return s_minu,s_sec 34 35 def H_Formatting(len_h,h,len_m,m,len_s,s): 36 if len_h < 2: 37 s_hour = '0' + h 38 s_minu,s_sec = M_Formatting(len_m,m,len_s,s) 39 else: 40 s_hour = h 41 s_minu,s_sec = M_Formatting(len_m,m,len_s,s) 42 return s_hour,s_minu,s_sec 43 44 def Switch(value_stop,value_start): 45 D_value = Decimal(value_stop - value_start) 46 if D_value >= 3600: 47 D_hour = int(D_value) 48 hour = D_hour // 3600 49 D_minu = D_hour - hour * 3600 50 minu = D_minu // 60 51 sec = D_value - hour * 3600 - minu * 60 52 53 s_hour,s_minu,s_sec = H_Formatting(len(str(hour)),str(hour),len(str(minu)),str(minu),len(str(int(sec))),str(sec)) 54 print("The Time Difference is: " + s_hour + ':' + s_minu + ':' + s_sec) 55 elif 60 <= D_value < 3600: 56 minu = D_value // 60 57 sec = D_value - minu * 60 58 s_minu,s_sec = M_Formatting(len(str(minu)),str(minu),len(str(int(sec))),str(sec)) 59 print("The Time Difference is: " + '00:' + s_minu + ':' + s_sec) 60 else: 61 sec = D_value 62 s_sec = S_Formatting(len(str(int(sec))),str(sec)) 63 print("The Time Difference is: " + '00:' + '00:' + s_sec) 64 65 def main(): # 定义主函数 66 running = True 67 print("Hello ~~ !") 68 while running: # 通过循环方便输入和计算 69 value_stop,value_start = Time_Cal() # 调用计算主函数 70 Switch(value_stop,value_start) # 调用单位转换函数 71 action = input("Go on ? Y/N:") 72 if action == 'Y' or action == 'y': 73 running = True 74 elif action == 'N' or action == 'n': 75 running = False 76 print("Goodbye~~") 77 78 if __name__ == '__main__': # 调用主函数 79 main()