diff --git a/examples/flask/airbrake_middleware.py b/examples/flask/airbrake_middleware.py
index 24e3ea1..07ebd47 100755
--- a/examples/flask/airbrake_middleware.py
+++ b/examples/flask/airbrake_middleware.py
@@ -1,4 +1,4 @@
-import datetime
+import time
 from flask import g, request
 from pybrake.utils import logger
 
@@ -8,26 +8,19 @@ def setup_airbrake_middleware(app, notifier):
 
 def before_request():
   def before_request_middleware():
-    g.flask_request_start_time = datetime.datetime.now()
+    g.request_start_time = time.time()
 
   return before_request_middleware
 
 def after_request(notifier):
   def after_request_middleware(response):
-    if not hasattr(g, 'flask_request_start_time'):
-      logger.error("flask_request_start_time is empty")
-      return response
-
-    now = datetime.datetime.utcnow()
-    dur = now - g.flask_request_start_time
-    if dur == 0:
-      logger.error("processing time is not valid")
+    if not hasattr(g, 'request_start_time'):
+      logger.error("request_start_time is empty")
       return response
 
     notifier.notify_request(
-      method=request.method, route=str(request.endpoint),
-      status_code=response.status_code, time=now, ms=dur.millisecond,
-    )
+      method=request.method, route=str(request.endpoint), status_code=response.status_code,
+      start_time=g.request_start_time, end_time=time.time())
 
     return response
 
diff --git a/pybrake/routes.py b/pybrake/routes.py
index 21a14da..92a199d 100755
--- a/pybrake/routes.py
+++ b/pybrake/routes.py
@@ -1,5 +1,6 @@
 import base64
 import json
+from datetime import datetime
 from threading import Lock, Timer
 from tdigest import TDigest
 import requests
@@ -33,7 +34,7 @@ class RouteStat():
     self.count = 0
     self.sum = 0
     self.sumsq = 0
-    self.time = time.replace(second=0, microsecond=0).strftime('%Y-%m-%dT%H:%M:%SZ')
+    self.time = time_to_str(time)
     self.td = TDigest()
     self.tdigest = None
 
@@ -75,19 +76,24 @@ class RouteStats():
     stats_json = json.dumps({"routes": [stat.__dict__ for _, stat in stats.items()]})
     requests.post(self._api_url, data=stats_json, headers=self._airbrake_headers)
 
-  def notify_request(self, method='', route='', status_code=0, time=None, ms=0):
+  def notify_request(self, method='', route='', status_code=0, start_time=None, end_time=None):
     self._init()
 
-    statKey = route_stat_key(method, route, status_code, time)
+    statKey = route_stat_key(method, route, status_code, start_time)
     with self._lock:
       if statKey in self._stats:
         stat = self._stats.get(statKey)
       else:
-        stat = RouteStat(method=method, route=route, status_code=status_code, time=time)
+        stat = RouteStat(method=method, route=route, status_code=status_code, time=start_time)
         self._stats[statKey] = stat
 
+      ms = round((end_time - start_time) * 1000, 2)
       stat.add(ms)
 
+def time_to_str(time):
+  t = datetime.utcfromtimestamp(time).replace(second=0, microsecond=0)
+  return t.strftime('%Y-%m-%dT%H:%M:%SZ')
+
 def route_stat_key(method='', route='', status_code=0, time=None):
-  tm = time.replace(second=0, microsecond=0).strftime('%Y-%m-%dT%H:%M:%SZ')
-  return "{}:{}:{}:{}".format(method, route, status_code, tm)
+  time = time // 60 * 60
+  return "{}:{}:{}:{}".format(method, route, status_code, time)

Change api to accept end time
Instead of `time=, ms=` it should accept `start_time=, end_time=`
diff --git a/pybrake/test_routes.py b/pybrake/test_routes.py
index c54c3c0..d55aa48 100644
--- a/pybrake/test_routes.py
+++ b/pybrake/test_routes.py
@@ -1,4 +1,4 @@
-from datetime import datetime
+import calendar
 import json
 import requests_mock
 
@@ -16,10 +16,11 @@ def test_route_stat():
     url = "http://test.com/api/v5/projects/1/routes-stats"
     m.post(url, json=_request_callback)
 
-    tm = datetime(2000, 1, 1, 0, 0)
-    route_stats.notify_request("GET", "ping", 200, tm, 123)
-    route_stats.notify_request("GET", "pong", 200, tm, 123)
-    route_stats.notify_request("GET", "pong", 200, tm, 123)
+    start_time = calendar.timegm((2000, 1, 1, 0, 0, 0, 0, 0, 0))
+    end_time = start_time + 0.123
+    route_stats.notify_request("GET", "ping", 200, start_time, end_time)
+    route_stats.notify_request("GET", "pong", 200, start_time, end_time)
+    route_stats.notify_request("GET", "pong", 200, start_time, end_time)
 
     route_stats._thread.join()
     assert m.call_count == 1

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest
3.7
test-requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
accumulation-tree==0.6.4
asgiref==3.7.2
astroid==2.15.8
certifi @ file:///croot/certifi_1671487769961/work/certifi
charset-normalizer==3.4.1
click==8.1.8
dill==0.3.7
Django==3.2.25
exceptiongroup==1.2.2
Flask==2.2.5
idna==3.10
importlib-metadata==6.7.0
iniconfig==2.0.0
isort==5.11.5
itsdangerous==2.1.2
Jinja2==3.1.6
lazy-object-proxy==1.9.0
MarkupSafe==2.1.5
mccabe==0.7.0
packaging==24.0
platformdirs==4.0.0
pluggy==1.2.0
py-cpuinfo==9.0.0
-e git+https://github.com/airbrake/pybrake.git@7562d51f992eb45295f986da2c7682f9df66238b#egg=pybrake
pylint==2.17.7
pytest==7.4.4
pytest-benchmark==4.0.0
pytz==2025.2
pyudorandom==1.0.0
requests==2.31.0
requests-mock==1.12.1
sqlparse==0.4.4
tdigest==0.5.2.2
tomli==2.0.1
tomlkit==0.12.5
typed-ast==1.5.5
typing_extensions==4.7.1
urllib3==2.0.7
Werkzeug==2.2.3
wrapt==1.16.0
zipp==3.15.0

name: pybrake
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - accumulation-tree==0.6.4
      - asgiref==3.7.2
      - astroid==2.15.8
      - charset-normalizer==3.4.1
      - click==8.1.8
      - dill==0.3.7
      - django==3.2.25
      - exceptiongroup==1.2.2
      - flask==2.2.5
      - idna==3.10
      - importlib-metadata==6.7.0
      - iniconfig==2.0.0
      - isort==5.11.5
      - itsdangerous==2.1.2
      - jinja2==3.1.6
      - lazy-object-proxy==1.9.0
      - markupsafe==2.1.5
      - mccabe==0.7.0
      - packaging==24.0
      - platformdirs==4.0.0
      - pluggy==1.2.0
      - py-cpuinfo==9.0.0
      - pylint==2.17.7
      - pytest==7.4.4
      - pytest-benchmark==4.0.0
      - pytz==2025.2
      - pyudorandom==1.0.0
      - requests==2.31.0
      - requests-mock==1.12.1
      - sqlparse==0.4.4
      - tdigest==0.5.2.2
      - tomli==2.0.1
      - tomlkit==0.12.5
      - typed-ast==1.5.5
      - typing-extensions==4.7.1
      - urllib3==2.0.7
      - werkzeug==2.2.3
      - wrapt==1.16.0
      - zipp==3.15.0
prefix: /opt/conda/envs/pybrake

pybrake/test_routes.py::test_route_stat
examples/flask/airbrake_middleware.py pybrake/routes.py
examples/flask/airbrake_middleware.py pybrake/routes.py