Подтвердить что ты не робот

datetime: округление/усечение числа цифр в микросекундах

В настоящее время я регистрирую материал и использую свой собственный форматтер с пользовательским formatTime():

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

Моя проблема в том, что микросекунды, %f, представляют собой шесть цифр. Можно ли как-нибудь выплевывать меньше цифр, как первые три цифры микросекунд?

4b9b3361

Ответ 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')
    tail = s[-7:]
    f = round(float(tail), 3)
    temp = "%.3f" % f
    return "%s%s" % (s[:-7], temp[1:])

Ответ 2

Редактировать: Как указывалось в комментариях ниже, округление этого решения (и приведенного выше) создает проблемы, когда значение микросекунды достигает 999500, а 999,5 округляется до 1000 (переполнение).

Если не считать переопределение strftime для поддержки нужного нам формата (потенциальное переполнение, вызванное округлением, должно быть распространено до секунд, затем минут и т.д.), Гораздо проще просто усечь первые 3 цифры, как указано в принятый ответ, или используя что-то вроде:

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

- Оригинальный ответ сохранен ниже -

В моем случае я пытался отформатировать метку даты с миллисекундами, отформатированными как "ddd". Решение, которое я использовал для получения миллисекунд, заключалось в том, чтобы использовать атрибут microsecond объекта datetime, разделить его на 1000.0, дополнить его нулями, если необходимо, и округлить в формате. Это выглядит так:

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

Ответ 3

Этот метод должен всегда возвращать временную метку, которая выглядит точно так же (с или без часового пояса в зависимости от того, содержит ли объект 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), и это вызывало проблемы для синтаксического анализатора, в который я загружал эти временные метки. Этот метод не имеет этой проблемы.

Ответ 4

Этот метод допускает гибкую точность и будет потреблять все значение микросекунды, если вы укажете слишком большую точность.

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 специально, потому что я бы предпочел не изменять полностью сериализованный объект datetime без его повторной проверки. Этот способ также показывает внутреннюю дату, если вы хотите изменить ее далее.

В примере округления обратите внимание, что точность основана на строках для модуля Decimal.

Ответ 5

Вот мое решение, использующее regexp:

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())

Ответ 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

Исправление предлагаемого решения на основе комментариев Паблоима:

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)

Ответ 8

Если однажды вы хотите получить день недели (т.е. "Воскресенье") вместе с результатом, то нарезка "[: -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-часового формата

% я - за 12-часовой формат

Спасибо,