3
0
Fork 0

feat: add "complete" (sic) timezone support

at least for now, this is enough to let admin define the global
default timezone for app, and override system local timezone.

eventually should support per-user timezone..some day..
This commit is contained in:
Lance Edgar 2025-12-16 22:52:33 -06:00
parent 286c683c93
commit 7fcb331806
10 changed files with 274 additions and 93 deletions

View file

@ -11,88 +11,86 @@ from pyramid import testing
from sqlalchemy import orm
from wuttjamaican.conf import WuttaConfig
from wuttjamaican.util import get_timezone_by_name
from wuttjamaican.testing import DataTestCase
from wuttaweb.forms import schema as mod
from wuttaweb.forms import widgets
from wuttaweb.testing import WebTestCase
class TestWuttaDateTime(TestCase):
class TestWuttaDateTime(WebTestCase):
def test_serialize(self):
typ = mod.WuttaDateTime()
node = colander.SchemaNode(typ)
tzlocal = get_timezone_by_name("America/Los_Angeles")
with patch.object(self.app, "get_timezone", return_value=tzlocal):
typ = mod.WuttaDateTime()
node = colander.SchemaNode(
typ, widget=widgets.WuttaDateTimeWidget(self.request)
)
result = typ.serialize(node, colander.null)
self.assertIs(result, colander.null)
# null
self.assertIs(typ.serialize(node, colander.null), colander.null)
self.assertIs(typ.serialize(node, None), colander.null)
self.assertIs(typ.serialize(node, ""), colander.null)
result = typ.serialize(node, None)
self.assertIs(result, colander.null)
# naive, UTC
result = typ.serialize(node, datetime.datetime(2024, 12, 11, 22, 33))
self.assertEqual(result, "2024-12-11T14:33:00-08:00")
result = typ.serialize(node, "")
self.assertIs(result, colander.null)
# aware, UTC
result = typ.serialize(
node,
datetime.datetime(2024, 12, 11, 22, 33, tzinfo=datetime.timezone.utc),
)
self.assertEqual(result, "2024-12-11T14:33:00-08:00")
# naive, UTC
# TODO: must override local timezone for a complete test
result = typ.serialize(node, datetime.datetime(2024, 12, 11, 22, 33))
self.assertTrue(result.startswith("2024-12-"))
# aware, local
result = typ.serialize(
node,
datetime.datetime(2024, 12, 11, 14, 33, tzinfo=tzlocal),
)
self.assertEqual(result, "2024-12-11T14:33:00-08:00")
# aware, UTC
result = typ.serialize(
node, datetime.datetime(2024, 12, 11, 22, 33, tzinfo=datetime.timezone.utc)
)
self.assertEqual(result, "2024-12-11T22:33:00+00:00")
# aware, local
result = typ.serialize(
node,
datetime.datetime(
2024,
12,
11,
22,
33,
tzinfo=datetime.timezone(-datetime.timedelta(hours=5)),
),
)
self.assertEqual(result, "2024-12-11T22:33:00-05:00")
# custom format
typ = mod.WuttaDateTime(format="%Y-%m-%d %I:%M %p")
node = colander.SchemaNode(typ)
result = typ.serialize(
node, datetime.datetime(2024, 12, 11, 22, 33, tzinfo=datetime.timezone.utc)
)
self.assertEqual(result, "2024-12-11 10:33 PM")
# custom format
typ = mod.WuttaDateTime(format="%Y-%m-%d %I:%M %p")
node = colander.SchemaNode(
typ, widget=widgets.WuttaDateTimeWidget(self.request)
)
result = typ.serialize(
node,
datetime.datetime(2024, 12, 11, 22, 33, tzinfo=datetime.timezone.utc),
)
self.assertEqual(result, "2024-12-11 02:33 PM")
def test_deserialize(self):
typ = mod.WuttaDateTime()
node = colander.SchemaNode(typ)
tzlocal = get_timezone_by_name("America/Los_Angeles")
with patch.object(self.app, "get_timezone", return_value=tzlocal):
typ = mod.WuttaDateTime()
node = colander.SchemaNode(
typ, widget=widgets.WuttaDateTimeWidget(self.request)
)
result = typ.deserialize(node, colander.null)
self.assertIs(result, colander.null)
# null
self.assertIs(typ.deserialize(node, colander.null), colander.null)
self.assertIs(typ.deserialize(node, None), colander.null)
self.assertIs(typ.deserialize(node, ""), colander.null)
result = typ.deserialize(node, None)
self.assertIs(result, colander.null)
# format #1
result = typ.deserialize(node, "2024-12-11T22:33:00")
self.assertIsInstance(result, datetime.datetime)
self.assertEqual(
result, datetime.datetime(2024, 12, 12, 6, 33, tzinfo=None)
)
result = typ.deserialize(node, "")
self.assertIs(result, colander.null)
# format #2
result = typ.deserialize(node, "2024-12-11T10:33 PM")
self.assertIsInstance(result, datetime.datetime)
self.assertEqual(
result, datetime.datetime(2024, 12, 12, 6, 33, tzinfo=None)
)
# TODO: must override local timezone for a complete test
result = typ.deserialize(node, "2024-12-11T10:33 PM")
self.assertIsInstance(result, datetime.datetime)
dt = datetime.datetime(2024, 12, 11, 22, 33)
self.assertLess(abs((result - dt).total_seconds()), 60 * 60 * 24)
self.assertIsNone(result.tzinfo)
# TODO: must override local timezone for a complete test
result = typ.deserialize(node, "2024-12-11T22:33:00")
self.assertIsInstance(result, datetime.datetime)
dt = datetime.datetime(2024, 12, 11, 22, 33)
self.assertLess(abs((result - dt).total_seconds()), 60 * 60 * 24)
self.assertIsNone(result.tzinfo)
self.assertRaises(colander.Invalid, typ.deserialize, node, "bogus")
# invalid
self.assertRaises(colander.Invalid, typ.deserialize, node, "bogus")
class TestObjectNode(DataTestCase):