datetime: округление/усечение числа цифр в микросекундах
В настоящее время я регистрирую материал и использую свой собственный форматтер с пользовательским formatTime()
:
def formatTime(self, _record, _datefmt):
t = datetime.datetime.now()
return t.strftime('%Y-%m-%d %H:%M:%S.%f')
Моя проблема в том, что микросекунды, %f
, представляют собой шесть цифр. Можно ли как-нибудь выплевывать меньше цифр, как первые три цифры микросекунд?
Ответы
Ответ 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-часовой формат
Спасибо,