Skip to content

Commit 9721b45

Browse files
hownowstephenclaude
andcommitted
Fix _datetime_to_timestamp to correctly convert tz-aware datetimes
Previously, dt.replace(tzinfo=timezone.utc) silently discarded any existing timezone info, treating e.g. a UTC-5 datetime as if it were UTC. Now tz-aware datetimes use astimezone() for proper conversion, while naive datetimes continue to be assumed UTC for backward compat. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4a98d69 commit 9721b45

2 files changed

Lines changed: 22 additions & 2 deletions

File tree

customerio/client_base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ def _sanitize_value(self, value):
123123
return value
124124

125125
def _datetime_to_timestamp(self, dt):
126+
if dt.tzinfo is not None:
127+
return int(dt.astimezone(timezone.utc).timestamp())
126128
return int(dt.replace(tzinfo=timezone.utc).timestamp())
127129

128130
def _stringify_list(self, customer_ids):

tests/test_customerio.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import json
22
import socket
33
import unittest
4-
from datetime import datetime
4+
from datetime import datetime, timedelta, timezone
55
from functools import partial
66

77
import urllib3
@@ -562,12 +562,30 @@ def test_unsuppress_call(self):
562562
self.cio.unsuppress(None)
563563

564564
def test_sanitize(self):
565-
from datetime import timezone
565+
data_in = dict(dt=datetime(2009, 2, 13, 23, 31, 30, 0, timezone.utc))
566+
data_out = self.cio._sanitize(data_in)
567+
self.assertEqual(data_out, dict(dt=1234567890))
566568

569+
def test_sanitize_naive_datetime(self):
570+
"""Naive datetimes are assumed UTC (backward compatible)."""
571+
data_in = dict(dt=datetime(2009, 2, 13, 23, 31, 30))
572+
data_out = self.cio._sanitize(data_in)
573+
self.assertEqual(data_out, dict(dt=1234567890))
574+
575+
def test_sanitize_aware_utc_datetime(self):
576+
"""Tz-aware UTC datetimes produce the correct timestamp."""
567577
data_in = dict(dt=datetime(2009, 2, 13, 23, 31, 30, 0, timezone.utc))
568578
data_out = self.cio._sanitize(data_in)
569579
self.assertEqual(data_out, dict(dt=1234567890))
570580

581+
def test_sanitize_aware_non_utc_datetime(self):
582+
"""Tz-aware non-UTC datetimes are converted, not silently replaced."""
583+
# 2009-02-13 18:31:30 at UTC-5 is 2009-02-13 23:31:30 UTC
584+
tz_minus_5 = timezone(timedelta(hours=-5))
585+
data_in = dict(dt=datetime(2009, 2, 13, 18, 31, 30, 0, tz_minus_5))
586+
data_out = self.cio._sanitize(data_in)
587+
self.assertEqual(data_out, dict(dt=1234567890))
588+
571589
def test_ids_are_encoded_in_url(self):
572590
self.cio.http.hooks = dict(
573591
response=partial(

0 commit comments

Comments
 (0)