Python实现ParseDuration-支持解析字符串格式的时间单位,例如将小时或者分钟数转换为秒

python的time模块不支持单独将字符串格式的分钟数和小时数转换为秒,比如将“5m”转换为“300”(秒),不支持将“0.2h5.1m12.123s”转换为“1038.123”(秒)。

但是这种字符串格式的分钟数或者小时数写法,在一些配置或者用户交互中还是比较有用的。

为了实现这种功能,通过模拟golang的`time`模块中的`ParseDuration(s string) (duration, error)`函数,使用Python代码实现如下:

#-*- coding:utf-8 -*-
import sys
import logging

reload(sys)                      # reload 才能调用 setdefaultencoding 方法
sys.setdefaultencoding('utf-8')  # 设置 'utf-8'

from cfg import config

class TimeTool(object):
    def __init__(self):
        handler = logging.StreamHandler()
        formatter = logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
        handler.setFormatter(formatter)
        self.logger = logging.getLogger("HistoryReader")
        self.logger.addHandler(handler)
        self.logger.setLevel(config.LOG_LEVEL)
        self.Nanosecond  = 1
        self.Microsecond = 1000 * self.Nanosecond
        self.Millisecond = 1000 * self.Microsecond
        self.Second      = 1000 * self.Millisecond
        self.Minute      = 60 * self.Second
        self.Hour        = 60 * self.Minute
        self.unitMap = {
                            "ns": int(self.Nanosecond),
                            "us": int(self.Microsecond),
                            "µs": int(self.Microsecond), # U+00B5 = micro symbol
                            "μs": int(self.Microsecond), # U+03BC = Greek letter mu
                            "ms": int(self.Millisecond),
                            "s":  int(self.Second),
                            "m":  int(self.Minute),
                            "h":  int(self.Hour),
        }
        pass

    def leadingInt(self, s):
        x, rem, err = int(0), str(""), "time: bad [0-9]*"
        i = 0
        while i < len(s):
            c = s[i]
            if c < '0' or c > '9':
                break
            #print x
            if x > (1 << 63-1)/10:
                #print "x > (1 << 63-1)/10 => %s > %s" %(x, (1 << 63-1)/10)
                return 0, "", err
            x = x * 10 + int(c) - int('0')
            if x < 0:
                #print "x < 0 => %s < 0" %(x)
                return 0, "", err
            i+=1
        return x, s[i:], None

    def leadingFraction(self, s):
        x, scale, rem = int(0), float(1), ""
        i, overflow = 0, False
        while i < len(s):
            c = s[i]
            if c < '0' or c > '9':
                break
            if overflow:
                continue
            if x > (1<<63-1)/10 :
                overflow = True
                continue
            y = x*10 + int(c) - int('0')
            if y < 0:
                overflow = True
                continue
            x = y
            scale *= 10
            i += 1
        return x, scale, s[i:]

    """
    将小时,分钟,转换为秒
    比如: 5m 转换为 300秒;5m20s 转换为320秒
    time 单位支持:"ns", "us" (or "µs"), "ms", "s", "m", "h"
    """
    def ParseDuration(self, s):
        if s == "" or len(s) < 1:
            return 0

        orig = s
        neg = False
        d = float(0)

        if s != "":
            if s[0] == "-" or s[0] == "+":
                neg = s[0] == "-"
                s = s[1:]

        if s == "0" or s == "":
            return 0

        while s != "":
            v, f, scale = int(0), int(0), float(1)

            print "S: %s" %s
            # the next character must be [0-9.]
            if not (s[0] == "." or '0' <= s[0] and s[0] <= '9'):
                self.logger.error("time1: invalid duration %s, s:%s" % (orig, s))
                return 0

            # Consume [0-9]*
            pl = len(s)
            v, s, err = self.leadingInt(s)
            if err != None:
                self.logger.error("time2, invalid duration %s" %orig)
                return 0
            pre = pl != len(s)

            # consume (.[0-9]*)?
            post = False
            if s != "" and s[0] == ".":
                s = s[1:]
                pl = len(s)
                f, scale, s = self.leadingFraction(s)
                post = pl != len(s)
            if not pre and not post:
                self.logger.error("time3, invalid duration %s" %orig)
                return 0

            # Consume unit.
            i = 0
            while i < len(s):
                c = s[i]
                if c == '.' or '0' <= c and c <= '9':
                    break
                i+=1
            if i == 0:
                self.logger.error("time4: unkonw unit in duration: %s" %orig)
                return 0
            print "s:%s, i:%s, s[:i]:%s" %(s, i, s[:i])
            u = s[:i]
            s = s[i:]
            if not self.unitMap.has_key(u):
                self.logger.error("time5: unknow unit %s in duration %s" %(u, orig))
                return 0
            unit = self.unitMap[u]
            if v > (1<<63-1)/unit:
                self.logger.error("time6: invalid duration %s" %orig)
                return 0
            v *= unit
            if f > 0 :
                v += int(float(f) * (float(unit) / scale))
                if v < 0:
                    self.logger.error("time7: invalid duration %s" %orig)
                    return 0
            d += v
            if d < 0 :
                self.logger.error("time8: invalid duration %s" %orig)
                return 0

        if neg :
            d = -d
        return float(d)

 

使用实例:

#-*- coding:utf-8 -*-
from timeTools import TimeTool

if __name__ == "__main__":
    tools = TimeTool()
    s = tools.ParseDuration("1m20.123s")
    print s

默认打印单位是“Nanosecond”,如果想要打印单位为“second”的话,只需要将parse结果除以`TimeTool.Second`即可,例如:

```python

tools = TimeTool()

s = tools.ParseDuration("1m20.123s")
print s/tools.Second

```

原文地址:https://www.cnblogs.com/yzhch/p/9098764.html