feat: drop timezone, assume UTC for all datetime values in DB

per changes in wuttjamaican
This commit is contained in:
Lance Edgar 2025-12-15 16:13:55 -06:00
parent 21c037dc25
commit d13f908da5
6 changed files with 545 additions and 18 deletions

View file

@ -33,7 +33,7 @@ license = {text = "GNU General Public License v3+"}
requires-python = ">= 3.8" requires-python = ">= 3.8"
dependencies = [ dependencies = [
"SQLAlchemy<2", # TODO: should allow 2.x "SQLAlchemy<2", # TODO: should allow 2.x
"WuttaWeb>=0.23.1", "WuttaWeb>=0.24.0",
] ]
[project.optional-dependencies] [project.optional-dependencies]

View file

@ -25,7 +25,6 @@ New Order Batch Handler
""" """
# pylint: disable=too-many-lines # pylint: disable=too-many-lines
import datetime
import decimal import decimal
from collections import OrderedDict from collections import OrderedDict
@ -921,7 +920,7 @@ class NewOrderBatchHandler(BatchHandler): # pylint: disable=too-many-public-met
row.unit_price_quoted = None row.unit_price_quoted = None
row.case_price_quoted = None row.case_price_quoted = None
if row.unit_price_sale is not None and ( if row.unit_price_sale is not None and (
not row.sale_ends or row.sale_ends > datetime.datetime.now() not row.sale_ends or row.sale_ends > self.app.make_utc()
): ):
row.unit_price_quoted = row.unit_price_sale row.unit_price_quoted = row.unit_price_sale
else: else:

View file

@ -0,0 +1,531 @@
"""drop time zones
Revision ID: 9f2ff39f4743
Revises: fd8a2527bd30
Create Date: 2025-12-15 15:14:38.281566
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import wuttjamaican.db.util
from sqlalchemy.dialects import postgresql
from wuttjamaican.util import make_utc
# revision identifiers, used by Alembic.
revision: str = "9f2ff39f4743"
down_revision: Union[str, None] = "fd8a2527bd30"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# sideshow_batch_neworder.created
op.add_column(
"sideshow_batch_neworder",
sa.Column("created_new", sa.DateTime(), nullable=True),
)
sideshow_batch_neworder = sa.sql.table(
"sideshow_batch_neworder",
sa.sql.column("uuid"),
sa.sql.column("created"),
sa.sql.column("created_new"),
)
cursor = op.get_bind().execute(sideshow_batch_neworder.select())
for row in cursor.fetchall():
op.get_bind().execute(
sideshow_batch_neworder.update()
.where(sideshow_batch_neworder.c.uuid == row.uuid)
.values({"created_new": make_utc(row.created)})
)
op.drop_column("sideshow_batch_neworder", "created")
op.alter_column(
"sideshow_batch_neworder",
"created_new",
new_column_name="created",
nullable=False,
existing_type=sa.DateTime(),
existing_nullable=True,
)
# sideshow_batch_neworder.executed
op.add_column(
"sideshow_batch_neworder",
sa.Column("executed_new", sa.DateTime(), nullable=True),
)
sideshow_batch_neworder = sa.sql.table(
"sideshow_batch_neworder",
sa.sql.column("uuid"),
sa.sql.column("executed"),
sa.sql.column("executed_new"),
)
cursor = op.get_bind().execute(sideshow_batch_neworder.select())
for row in cursor.fetchall():
if row.executed:
op.get_bind().execute(
sideshow_batch_neworder.update()
.where(sideshow_batch_neworder.c.uuid == row.uuid)
.values({"executed_new": make_utc(row.executed)})
)
op.drop_column("sideshow_batch_neworder", "executed")
op.alter_column(
"sideshow_batch_neworder",
"executed_new",
new_column_name="executed",
existing_type=sa.DateTime(),
existing_nullable=True,
)
# sideshow_batch_neworder_row.modified
op.add_column(
"sideshow_batch_neworder_row",
sa.Column("modified_new", sa.DateTime(), nullable=True),
)
sideshow_batch_neworder_row = sa.sql.table(
"sideshow_batch_neworder_row",
sa.sql.column("uuid"),
sa.sql.column("modified"),
sa.sql.column("modified_new"),
)
cursor = op.get_bind().execute(sideshow_batch_neworder_row.select())
for row in cursor.fetchall():
if row.modified:
op.get_bind().execute(
sideshow_batch_neworder_row.update()
.where(sideshow_batch_neworder_row.c.uuid == row.uuid)
.values({"modified_new": make_utc(row.modified)})
)
op.drop_column("sideshow_batch_neworder_row", "modified")
op.alter_column(
"sideshow_batch_neworder_row",
"modified_new",
new_column_name="modified",
existing_type=sa.DateTime(),
existing_nullable=True,
)
# sideshow_batch_neworder_row.sale_ends
op.add_column(
"sideshow_batch_neworder_row",
sa.Column("sale_ends_new", sa.DateTime(), nullable=True),
)
sideshow_batch_neworder_row = sa.sql.table(
"sideshow_batch_neworder_row",
sa.sql.column("uuid"),
sa.sql.column("sale_ends"),
sa.sql.column("sale_ends_new"),
)
cursor = op.get_bind().execute(sideshow_batch_neworder_row.select())
for row in cursor.fetchall():
if row.sale_ends:
op.get_bind().execute(
sideshow_batch_neworder_row.update()
.where(sideshow_batch_neworder_row.c.uuid == row.uuid)
.values({"sale_ends_new": make_utc(row.sale_ends)})
)
op.drop_column("sideshow_batch_neworder_row", "sale_ends")
op.alter_column(
"sideshow_batch_neworder_row",
"sale_ends_new",
new_column_name="sale_ends",
existing_type=sa.DateTime(),
existing_nullable=True,
)
# sideshow_customer_pending.created
op.add_column(
"sideshow_customer_pending",
sa.Column("created_new", sa.DateTime(), nullable=True),
)
sideshow_customer_pending = sa.sql.table(
"sideshow_customer_pending",
sa.sql.column("uuid"),
sa.sql.column("created"),
sa.sql.column("created_new"),
)
cursor = op.get_bind().execute(sideshow_customer_pending.select())
for row in cursor.fetchall():
op.get_bind().execute(
sideshow_customer_pending.update()
.where(sideshow_customer_pending.c.uuid == row.uuid)
.values({"created_new": make_utc(row.created)})
)
op.drop_column("sideshow_customer_pending", "created")
op.alter_column(
"sideshow_customer_pending",
"created_new",
new_column_name="created",
nullable=False,
existing_type=sa.DateTime(),
existing_nullable=True,
)
# sideshow_order.created
op.add_column(
"sideshow_order",
sa.Column("created_new", sa.DateTime(), nullable=True),
)
sideshow_order = sa.sql.table(
"sideshow_order",
sa.sql.column("uuid"),
sa.sql.column("created"),
sa.sql.column("created_new"),
)
cursor = op.get_bind().execute(sideshow_order.select())
for row in cursor.fetchall():
op.get_bind().execute(
sideshow_order.update()
.where(sideshow_order.c.uuid == row.uuid)
.values({"created_new": make_utc(row.created)})
)
op.drop_column("sideshow_order", "created")
op.alter_column(
"sideshow_order",
"created_new",
new_column_name="created",
nullable=False,
existing_type=sa.DateTime(),
existing_nullable=True,
)
# sideshow_order_item.sale_ends
op.add_column(
"sideshow_order_item",
sa.Column("sale_ends_new", sa.DateTime(), nullable=True),
)
sideshow_order_item = sa.sql.table(
"sideshow_order_item",
sa.sql.column("uuid"),
sa.sql.column("sale_ends"),
sa.sql.column("sale_ends_new"),
)
cursor = op.get_bind().execute(sideshow_order_item.select())
for row in cursor.fetchall():
if row.sale_ends:
op.get_bind().execute(
sideshow_order_item.update()
.where(sideshow_order_item.c.uuid == row.uuid)
.values({"sale_ends_new": make_utc(row.sale_ends)})
)
op.drop_column("sideshow_order_item", "sale_ends")
op.alter_column(
"sideshow_order_item",
"sale_ends_new",
new_column_name="sale_ends",
existing_type=sa.DateTime(),
existing_nullable=True,
)
# sideshow_order_item_event.occurred
op.add_column(
"sideshow_order_item_event",
sa.Column("occurred_new", sa.DateTime(), nullable=True),
)
sideshow_order_item_event = sa.sql.table(
"sideshow_order_item_event",
sa.sql.column("uuid"),
sa.sql.column("occurred"),
sa.sql.column("occurred_new"),
)
cursor = op.get_bind().execute(sideshow_order_item_event.select())
for row in cursor.fetchall():
op.get_bind().execute(
sideshow_order_item_event.update()
.where(sideshow_order_item_event.c.uuid == row.uuid)
.values({"occurred_new": make_utc(row.occurred)})
)
op.drop_column("sideshow_order_item_event", "occurred")
op.alter_column(
"sideshow_order_item_event",
"occurred_new",
new_column_name="occurred",
nullable=False,
existing_type=sa.DateTime(),
existing_nullable=True,
)
# sideshow_product_pending.created
op.add_column(
"sideshow_product_pending",
sa.Column("created_new", sa.DateTime(), nullable=True),
)
sideshow_product_pending = sa.sql.table(
"sideshow_product_pending",
sa.sql.column("uuid"),
sa.sql.column("created"),
sa.sql.column("created_new"),
)
cursor = op.get_bind().execute(sideshow_product_pending.select())
for row in cursor.fetchall():
op.get_bind().execute(
sideshow_product_pending.update()
.where(sideshow_product_pending.c.uuid == row.uuid)
.values({"created_new": make_utc(row.created)})
)
op.drop_column("sideshow_product_pending", "created")
op.alter_column(
"sideshow_product_pending",
"created_new",
new_column_name="created",
nullable=False,
existing_type=sa.DateTime(),
existing_nullable=True,
)
def downgrade() -> None:
# sideshow_product_pending.created
op.add_column(
"sideshow_product_pending",
sa.Column("created_old", sa.DateTime(timezone=True), nullable=True),
)
sideshow_product_pending = sa.sql.table(
"sideshow_product_pending",
sa.sql.column("uuid"),
sa.sql.column("created"),
sa.sql.column("created_old"),
)
cursor = op.get_bind().execute(sideshow_product_pending.select())
for row in cursor.fetchall():
op.get_bind().execute(
sideshow_product_pending.update()
.where(sideshow_product_pending.c.uuid == row.uuid)
.values({"created_old": row.created})
)
op.drop_column("sideshow_product_pending", "created")
op.alter_column(
"sideshow_product_pending",
"created_old",
new_column_name="created",
nullable=False,
existing_type=sa.DateTime(timezone=True),
existing_nullable=True,
)
# sideshow_order_item_event.occurred
op.add_column(
"sideshow_order_item_event",
sa.Column("occurred_old", sa.DateTime(timezone=True), nullable=True),
)
sideshow_order_item_event = sa.sql.table(
"sideshow_order_item_event",
sa.sql.column("uuid"),
sa.sql.column("occurred"),
sa.sql.column("occurred_old"),
)
cursor = op.get_bind().execute(sideshow_order_item_event.select())
for row in cursor.fetchall():
op.get_bind().execute(
sideshow_order_item_event.update()
.where(sideshow_order_item_event.c.uuid == row.uuid)
.values({"occurred_old": row.occurred})
)
op.drop_column("sideshow_order_item_event", "occurred")
op.alter_column(
"sideshow_order_item_event",
"occurred_old",
new_column_name="occurred",
nullable=False,
existing_type=sa.DateTime(timezone=True),
existing_nullable=True,
)
# sideshow_order_item.sale_ends
op.add_column(
"sideshow_order_item",
sa.Column("sale_ends_old", sa.DateTime(timezone=True), nullable=True),
)
sideshow_order_item = sa.sql.table(
"sideshow_order_item",
sa.sql.column("uuid"),
sa.sql.column("sale_ends"),
sa.sql.column("sale_ends_old"),
)
cursor = op.get_bind().execute(sideshow_order_item.select())
for row in cursor.fetchall():
if row.sale_ends:
op.get_bind().execute(
sideshow_order_item.update()
.where(sideshow_order_item.c.uuid == row.uuid)
.values({"sale_ends_old": row.sale_ends})
)
op.drop_column("sideshow_order_item", "sale_ends")
op.alter_column(
"sideshow_order_item",
"sale_ends_old",
new_column_name="sale_ends",
existing_type=sa.DateTime(timezone=True),
existing_nullable=True,
)
# sideshow_order.created
op.add_column(
"sideshow_order",
sa.Column("created_old", sa.DateTime(timezone=True), nullable=True),
)
sideshow_order = sa.sql.table(
"sideshow_order",
sa.sql.column("uuid"),
sa.sql.column("created"),
sa.sql.column("created_old"),
)
cursor = op.get_bind().execute(sideshow_order.select())
for row in cursor.fetchall():
op.get_bind().execute(
sideshow_order.update()
.where(sideshow_order.c.uuid == row.uuid)
.values({"created_old": row.created})
)
op.drop_column("sideshow_order", "created")
op.alter_column(
"sideshow_order",
"created_old",
new_column_name="created",
nullable=False,
existing_type=sa.DateTime(timezone=True),
existing_nullable=True,
)
# sideshow_customer_pending.created
op.add_column(
"sideshow_customer_pending",
sa.Column("created_old", sa.DateTime(timezone=True), nullable=True),
)
sideshow_customer_pending = sa.sql.table(
"sideshow_customer_pending",
sa.sql.column("uuid"),
sa.sql.column("created"),
sa.sql.column("created_old"),
)
cursor = op.get_bind().execute(sideshow_customer_pending.select())
for row in cursor.fetchall():
op.get_bind().execute(
sideshow_customer_pending.update()
.where(sideshow_customer_pending.c.uuid == row.uuid)
.values({"created_old": row.created})
)
op.drop_column("sideshow_customer_pending", "created")
op.alter_column(
"sideshow_customer_pending",
"created_old",
new_column_name="created",
nullable=False,
existing_type=sa.DateTime(timezone=True),
existing_nullable=True,
)
# sideshow_batch_neworder_row.sale_ends
op.add_column(
"sideshow_batch_neworder_row",
sa.Column("sale_ends_old", sa.DateTime(timezone=True), nullable=True),
)
sideshow_batch_neworder_row = sa.sql.table(
"sideshow_batch_neworder_row",
sa.sql.column("uuid"),
sa.sql.column("sale_ends"),
sa.sql.column("sale_ends_old"),
)
cursor = op.get_bind().execute(sideshow_batch_neworder_row.select())
for row in cursor.fetchall():
if row.sale_ends:
op.get_bind().execute(
sideshow_batch_neworder_row.update()
.where(sideshow_batch_neworder_row.c.uuid == row.uuid)
.values({"sale_ends_old": row.sale_ends})
)
op.drop_column("sideshow_batch_neworder_row", "sale_ends")
op.alter_column(
"sideshow_batch_neworder_row",
"sale_ends_old",
new_column_name="sale_ends",
existing_type=sa.DateTime(timezone=True),
existing_nullable=True,
)
# sideshow_batch_neworder_row.modified
op.add_column(
"sideshow_batch_neworder_row",
sa.Column("modified_old", sa.DateTime(timezone=True), nullable=True),
)
sideshow_batch_neworder_row = sa.sql.table(
"sideshow_batch_neworder_row",
sa.sql.column("uuid"),
sa.sql.column("modified"),
sa.sql.column("modified_old"),
)
cursor = op.get_bind().execute(sideshow_batch_neworder_row.select())
for row in cursor.fetchall():
if row.modified:
op.get_bind().execute(
sideshow_batch_neworder_row.update()
.where(sideshow_batch_neworder_row.c.uuid == row.uuid)
.values({"modified_old": row.modified})
)
op.drop_column("sideshow_batch_neworder_row", "modified")
op.alter_column(
"sideshow_batch_neworder_row",
"modified_old",
new_column_name="modified",
existing_type=sa.DateTime(timezone=True),
existing_nullable=True,
)
# sideshow_batch_neworder.executed
op.add_column(
"sideshow_batch_neworder",
sa.Column("executed_old", sa.DateTime(timezone=True), nullable=True),
)
sideshow_batch_neworder = sa.sql.table(
"sideshow_batch_neworder",
sa.sql.column("uuid"),
sa.sql.column("executed"),
sa.sql.column("executed_old"),
)
cursor = op.get_bind().execute(sideshow_batch_neworder.select())
for row in cursor.fetchall():
if row.executed:
op.get_bind().execute(
sideshow_batch_neworder.update()
.where(sideshow_batch_neworder.c.uuid == row.uuid)
.values({"executed_old": row.executed})
)
op.drop_column("sideshow_batch_neworder", "executed")
op.alter_column(
"sideshow_batch_neworder",
"executed_old",
new_column_name="executed",
existing_type=sa.DateTime(timezone=True),
existing_nullable=True,
)
# sideshow_batch_neworder.created
op.add_column(
"sideshow_batch_neworder",
sa.Column("created_old", sa.DateTime(timezone=True), nullable=True),
)
sideshow_batch_neworder = sa.sql.table(
"sideshow_batch_neworder",
sa.sql.column("uuid"),
sa.sql.column("created"),
sa.sql.column("created_old"),
)
cursor = op.get_bind().execute(sideshow_batch_neworder.select())
for row in cursor.fetchall():
op.get_bind().execute(
sideshow_batch_neworder.update()
.where(sideshow_batch_neworder.c.uuid == row.uuid)
.values({"created_old": row.created})
)
op.drop_column("sideshow_batch_neworder", "created")
op.alter_column(
"sideshow_batch_neworder",
"created_old",
new_column_name="created",
nullable=False,
existing_type=sa.DateTime(timezone=True),
existing_nullable=True,
)

View file

@ -24,12 +24,11 @@
Data models for Customers Data models for Customers
""" """
import datetime
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy import orm from sqlalchemy import orm
from wuttjamaican.db import model from wuttjamaican.db import model
from wuttjamaican.util import make_utc
from sideshow.enum import PendingCustomerStatus from sideshow.enum import PendingCustomerStatus
@ -175,9 +174,9 @@ class PendingCustomer( # pylint: disable=too-few-public-methods
) )
created = sa.Column( created = sa.Column(
sa.DateTime(timezone=True), sa.DateTime(),
nullable=False, nullable=False,
default=datetime.datetime.now, default=make_utc,
doc=""" doc="""
Timestamp when the customer record was created. Timestamp when the customer record was created.
""", """,

View file

@ -24,13 +24,12 @@
Data models for Orders Data models for Orders
""" """
import datetime
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy import orm from sqlalchemy import orm
from sqlalchemy.ext.orderinglist import ordering_list from sqlalchemy.ext.orderinglist import ordering_list
from wuttjamaican.db import model from wuttjamaican.db import model
from wuttjamaican.util import make_utc
class OrderMixin: # pylint: disable=too-few-public-methods class OrderMixin: # pylint: disable=too-few-public-methods
@ -266,7 +265,7 @@ class OrderItemMixin: # pylint: disable=too-few-public-methods
) )
sale_ends = sa.Column( sale_ends = sa.Column(
sa.DateTime(timezone=True), sa.DateTime(),
nullable=True, nullable=True,
doc=""" doc="""
End date/time for the sale in effect, if any. End date/time for the sale in effect, if any.
@ -403,9 +402,9 @@ class Order( # pylint: disable=too-few-public-methods,duplicate-code
) )
created = sa.Column( created = sa.Column(
sa.DateTime(timezone=True), sa.DateTime(),
nullable=False, nullable=False,
default=datetime.datetime.now, default=make_utc,
doc=""" doc="""
Timestamp when the order was created. Timestamp when the order was created.
@ -596,9 +595,9 @@ class OrderItemEvent(model.Base): # pylint: disable=too-few-public-methods
) )
occurred = sa.Column( occurred = sa.Column(
sa.DateTime(timezone=True), sa.DateTime(),
nullable=False, nullable=False,
default=datetime.datetime.now, default=make_utc,
doc=""" doc="""
Date and time when the event occurred. Date and time when the event occurred.
""", """,

View file

@ -24,12 +24,11 @@
Data models for Products Data models for Products
""" """
import datetime
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy import orm from sqlalchemy import orm
from wuttjamaican.db import model from wuttjamaican.db import model
from wuttjamaican.util import make_utc
from sideshow.enum import PendingProductStatus from sideshow.enum import PendingProductStatus
@ -265,9 +264,9 @@ class PendingProduct( # pylint: disable=too-few-public-methods
) )
created = sa.Column( created = sa.Column(
sa.DateTime(timezone=True), sa.DateTime(),
nullable=False, nullable=False,
default=datetime.datetime.now, default=make_utc,
doc=""" doc="""
Timestamp when the product record was created. Timestamp when the product record was created.
""", """,