【问题标题】:datetime: Round/trim number of digits in microsecondsdatetime:以微秒为单位的舍入/修剪位数
【发布时间】:2012-06-14 19:38:57
【问题描述】:

目前我正在记录一些东西,我正在使用我自己的格式化程序和自定义formatTime()

def formatTime(self, _record, _datefmt):
    t = datetime.datetime.now()
    return t.strftime('%Y-%m-%d %H:%M:%S.%f')

我的问题是微秒 %f 是六位数。有没有办法吐出更少的数字,比如微秒的前三位?

【问题讨论】:

    标签: python datetime formatter


    【解决方案1】:

    最简单的方法是使用切片来切掉微秒的最后三位:

    def format_time():
        t = datetime.datetime.now()
        s = t.strftime('%Y-%m-%d %H:%M:%S.%f')
        return s[:-3]
    

    我强烈建议只切碎。我曾经写过一些记录代码,它对时间戳进行舍入而不是截断,当舍入更改最后一位时,我发现它实际上有点令人困惑。有定时代码在某个时间戳停止运行,但由于四舍五入,存在具有该时间戳的日志事件。切碎更简单、更可预测。

    如果你想实际地对数字进行四舍五入而不是简单地切分,那就需要更多的工作,但并不可怕:

    def format_time():
        t = datetime.datetime.now()
        s = t.strftime('%Y-%m-%d %H:%M:%S.%f')
        head = s[:-7] # everything up to the '.'
        tail = s[-7:] # the '.' and the 6 digits after it
        f = float(tail)
        temp = "{:.03f}".format(f)  # for Python 2.x: temp = "%.3f" % f
        new_tail = temp[1:] # temp[0] is always '0'; get rid of it
        return head + new_tail
    

    显然你可以用更少的变量来简化上面的内容;我只是想让它很容易理解。

    【讨论】:

    • 你的取整方法对我不起作用,它会产生这个结果:'2015-04-12 17:09:020.588',秒数无效。
    • 我在 python 2.7.6 和 python 3.4.0 中测试了你的代码。复制并粘贴您的代码,每次结果都相同。您的秒区有 3 位数字。我正在查看代码,您的浮点数(f 变量)似乎在小数点前的前导 0(例如:0.367)上附加为字符串。秒区域中的前导 0 是它开始失败的地方。
    • @NuclearPeon 我很抱歉。您发现了一个真正的错误,这证明我在发布之前没有实际测试过。我已经编辑了代码来修复这个错误,它现在应该对你有用。感谢您提请我注意。
    • 这不起作用。当微秒值恰好为零时,秒值会被截断,例如:'2007-12-24T11:32'。
    • 当微秒至少为 999500 时,它会截断而不是向上取整。您可以在定义 f: if f >= 1.0: return (t + datetime.timedelta(milliseconds=1)).strftime('%Y-%m-%d %H:%M:%S.000') 后解决此问题(否则像以前一样继续)。
    【解决方案2】:

    从 Python 3.6 开始,该语言内置了此功能:

    def format_time():
        t = datetime.datetime.now()
        s = t.isoformat(timespec='milliseconds')
        return s
    

    【讨论】:

      【解决方案3】:

      编辑:正如下面 cmets 中的一些人指出的那样,当微秒值达到 999500 时,此解决方案(以及上面的解决方案)的四舍五入会带来问题,因为 999.5 会四舍五入为 1000(溢出)。

      没有重新实现 strftime 以支持我们想要的格式(由舍入引起的潜在溢出需要传播到秒,然后是分钟等),只截断到前 3 位数字要简单得多在接受的答案中概述,或使用类似:

      '{:03}'.format(int(999999/1000))

      -- 下面保留原始答案--

      在我的例子中,我试图用格式化为“ddd”的毫秒来格式化一个日期戳。我最终用来获得毫秒的解决方案是使用datetime 对象的microsecond 属性,将其除以1000.0,必要时用零填充,并用格式对其进行舍入。它看起来像这样:

      '{:03.0f}'.format(datetime.now().microsecond / 1000.0)
      # Produces: '033', '499', etc.
      

      【讨论】:

      • 微秒是 0 devrant.io/rants/466105
      • @alkuzad:正如您所提到的,它不可能正好是一百万,但是当微秒至少为 999500 时,format 会将其四舍五入为 1000。您可以将 500 微秒添加到日期时间而不是四舍五入并截断以获得相同的效果,同时自动将汇总滚动到分钟、小时、天、月或年。或者只是截断,如果半毫秒对你来说并不重要:'{:03}'.format(datetime.now().microsecond // 1000)
      【解决方案4】:

      此方法应始终返回与此完全相同的时间戳(有或没有时区,具体取决于输入 dt 对象是否包含时区):

      2016-08-05T18:18:54.776+0000

      它将datetime 对象作为输入(您可以使用datetime.datetime.now() 生成)。要在我的示例输出中获得时区,您需要 import pytz 并传递 datetime.datetime.now(pytz.utc)

      import pytz, datetime
      
      
      time_format(datetime.datetime.now(pytz.utc))
      
      def time_format(dt):
          return "%s:%.3f%s" % (
              dt.strftime('%Y-%m-%dT%H:%M'),
              float("%.3f" % (dt.second + dt.microsecond / 1e6)),
              dt.strftime('%z')
          )   
      

      我注意到上面的一些其他方法会省略尾随零,如果有一个(例如,0.870 变成了0.87),这给我输入这些时间戳的解析器造成了问题。这种方法没有这个问题。

      【讨论】:

      • 谢谢,这对我最有效。我唯一更新的是将"Z" 硬编码到主字符串模板中,而不是使用"%z"
      • @Eric Herot:我很欣赏这个答案。 '2020-05-31T14:07:3:172Z' 而不是 '2020-05-31T14:07:03.172Z' ,是否可以将前导零添加到分钟?
      • @Eric Herot:我明白了,返回 "%s:%06.3f%s" % ( dt.strftime('%Y-%m-%dT%H:%M'), float("%06.3f" % (dt.second + dt.microsecond / 1e6)), dt.strftime('Z') )
      • @StackGuru 奇怪的是,您将使用该格式字符串获得非零填充分钟。根据文档 (docs.python.org/3/library/…),它应该默认以这种方式工作。
      • 像其他几个解决方案一样,这不能正确舍入: time_format(datetime.datetime(2020, 1, 1, 23, 59, 59, 999999)) 返回 '2020-01-01T23:59 :60.000'
      【解决方案5】:

      您可以从微秒中减去当前日期时间。

      d = datetime.datetime.now()
      current_time = d - datetime.timedelta(microseconds=d.microsecond)
      

      这会将2021-05-14 16:11:21.916229 变成2021-05-14 16:11:21

      【讨论】:

      • 正在删除微秒,问题是别的
      • 问题说给round or *trim* the microseconds。我的答案是从当前日期时间中减少微秒。
      【解决方案6】:

      一个适用于所有情况的简单解决方案:

      def format_time():
          t = datetime.datetime.now()
          if t.microsecond % 1000 >= 500:  # check if there will be rounding up
              t = t + datetime.timedelta(milliseconds=1)  # manually round up
          return t.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
      

      基本上,您首先对日期对象本身进行手动舍入,然后您可以安全地修剪微秒。

      【讨论】:

        【解决方案7】:

        这是我使用正则表达式的解决方案:

        import re
        
        # Capture 6 digits after dot in a group.
        regexp = re.compile(r'\.(\d{6})')
        def to_splunk_iso(dt):
            """Converts the datetime object to Splunk isoformat string."""
            # 6-digits string.
            microseconds = regexp.search(dt.isoformat()).group(1)
            return regexp.sub('.%d' % round(float(microseconds) / 1000), dt.isoformat())
        

        【讨论】:

          【解决方案8】:

          此方法允许灵活的精度,如果您指定的精度过高,则会消耗整个微秒值。

          def formatTime(self, _record, _datefmt, precision=3):
              dt = datetime.datetime.now()
              us = str(dt.microsecond)
              f = us[:precision] if len(us) > precision else us
              return "%d-%d-%d %d:%d:%d.%d" % (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, int(f))
          

          该方法实现了四舍五入到小数点后三位:

          import datetime
          from decimal import *
          
          def formatTime(self, _record, _datefmt, precision='0.001'):
              dt = datetime.datetime.now()
              seconds = float("%d.%d" % (dt.second, dt.microsecond))
              return "%d-%d-%d %d:%d:%s" % (dt.year, dt.month, dt.day, dt.hour, dt.minute,
                                       float(Decimal(seconds).quantize(Decimal(precision), rounding=ROUND_HALF_UP)))
          

          我故意避免使用strftime 方法,因为我不希望在不重新验证的情况下修改完全序列化的日期时间对象。如果您想进一步修改它,这种方式还可以显示日期内部。

          在舍入示例中,请注意Decimal 模块的精度是基于字符串的。

          【讨论】:

            【解决方案9】:

            根据Pablojim 评论修复建议的解决方案:

            from datetime import datetime
            
            dt = datetime.now()
            
            dt_round_microsec = round(dt.microsecond/1000) #number of zeroes to round   
            dt = dt.replace(microsecond=dt_round_microsec)
            

            【讨论】:

            • 这会在 59.5->59.999' 秒内失败,因为它会尝试将秒字段设置为 60。
            • @Pablojim 修复并删除了 log10 作为函数,根本不需要它。它真的把所有事情都四舍五入到几秒钟。谢谢
            【解决方案10】:

            如果曾经想获得星期几(即“星期日)”以及结果,那么切片“[:-3]”将不起作用。到时候你可以一起去,

            dt = datetime.datetime.now()
            print("{}.{:03d} {}".format(dt.strftime('%Y-%m-%d %I:%M:%S'), dt.microsecond//1000, dt.strftime("%A")))
            
            #Output: '2019-05-05 03:11:22.211 Sunday'
            

            %H - 24 小时制

            %I - 12 小时制

            谢谢,

            【讨论】:

              【解决方案11】:

              在这里添加我的两分钱,因为这种方法将允许您编写微秒格式,就像您在 c 样式中的浮点数一样。它利用了他们都使用%f

              import datetime
              import re
              
              def format_datetime(date, format):
                  """Format a ``datetime`` object with microsecond precision.
                     Pass your microsecond as you would format a c-string float.
                     e.g "%.3f"
              
                     Args:
                          date (datetime.datetime): You input ``datetime`` obj.
                          format (str): Your strftime format string.
              
                      Returns:
                          str: Your formatted datetime string.
                  """
                  # We need to check if formatted_str contains "%.xf" (x = a number) 
                  float_format = r"(%\.\d+f)"
                  has_float_format = re.search(float_format, format)
                  if has_float_format:
                      # make microseconds be decimal place. Might be a better way to do this
                      microseconds = date.microsecond
                      while int(microseconds):  # quit once it's 0
                          microseconds /= 10
                      ms_str = has_float_format.group(1) % microseconds
                      format = re.sub(float_format, ms_str[2:], format)
                  return date.strftime(format)
              
              print(datetime.datetime.now(), "%H:%M:%S.%.3f")
              # '17:58:54.424'
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2020-12-07
                • 2019-08-08
                • 1970-01-01
                • 2015-09-16
                • 2011-11-05
                • 2011-08-22
                • 1970-01-01
                • 2015-08-31
                相关资源
                最近更新 更多