Replies: 4 comments 1 reply
-
You are on the right track with your approach to handling timezones. However, you are correct that SQLite does not have native support for storing timezone-aware timestamps. Instead, SQLite will convert the timestamp to its local time before storing it. If you choose to split the timestamp into two columns, you can define them as separate sa.Column objects in your SQLModel class, like this: class TransactionBase(SQLModel):
# other columns here
transaction_ts: datetime
transaction_tz: str In this case, you will need to modify your data validation code to validate both the timestamp and the timezone, and you will need to handle timezones correctly when retrieving the data from the database. As for which approach is better, it depends on your specific use case. Storing a naive UTC timestamp is simpler, but it requires that your application handles timezones correctly when retrieving the data. Storing the timestamp and timezone separately requires more validation and handling code, but it allows for more flexibility in handling timezone. |
Beta Was this translation helpful? Give feedback.
-
Thanks @8thgencore for your answer! Assuming that I want to split the into a timestamp and a timezone: how can I handle an input provided as |
Beta Was this translation helpful? Give feedback.
-
@drorata Here's an example of how you can split the input into a timestamp and timezone in your from datetime import datetime, timezone, timedelta
import re
from sqlmodel import SQLModel, Column
class TransactionBase(SQLModel):
transaction_ts: datetime = Column(nullable=False)
transaction_tz: str = Column(nullable=False)
def __init__(self, **data):
timestamp_str, tz_str = self._split_timestamp_tz(data['transaction_ts'])
timestamp = datetime.fromisoformat(timestamp_str).replace(tzinfo=timezone.utc)
super().__init__(**{'transaction_ts': timestamp, 'transaction_tz': tz_str})
@staticmethod
def _split_timestamp_tz(timestamp_str: str):
# extract timestamp and timezone information from input string using regex
match = re.match(r'(?P<timestamp>.+)\+(?P<tz_offset>\d{2}):(?P<tz_offset_min>\d{2})$', timestamp_str)
if not match:
raise ValueError("Invalid timestamp format")
timestamp_str = match.group('timestamp')
tz_offset = int(match.group('tz_offset'))
tz_offset_min = int(match.group('tz_offset_min'))
tz_delta = timedelta(hours=tz_offset, minutes=tz_offset_min)
tz_str = f"+{tz_offset:02d}:{tz_offset_min:02d}"
if tz_offset < 0:
tz_str = f"-{abs(tz_offset):02d}:{tz_offset_min:02d}"
tz_delta = timedelta(hours=-tz_offset, minutes=tz_offset_min)
return timestamp_str, tz_str
def to_dict(self):
return {"transaction_ts": self.transaction_ts.isoformat(), "transaction_tz": self.transaction_tz} In this example, we define two columns in the Note that this implementation assumes that the input timestamp is in ISO 8601 format with a timezone offset. If the input format can vary, you may need to modify the regular expression or add additional parsing logic to handle different formats. |
Beta Was this translation helpful? Give feedback.
-
For the sake of completeness, here is an attempt on using UTC timestamps: from datetime import datetime, timezone
from typing import Optional
from loguru import logger
from pydantic import validator
from sqlmodel import Field, SQLModel
SUPPORTED_TIME_FORMATS = [
"%Y-%m-%d %H:%M:%S %z",
"%Y-%m-%d %z",
]
class TransactionBase(SQLModel):
transaction_utc_ts: datetime = Field(
default=...,
title="The UTC timestamp of the transaction",
)
class Transaction(TransactionBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
class TransactionCreate(TransactionBase):
@validator("transaction_utc_ts", pre=True)
def ensure_dt_format(cls, v):
if isinstance(v, datetime):
if v.tzinfo:
logger.debug(f"Creating DT: {v}")
return v.astimezone(timezone.utc)
raise ValueError(f"Provided datetime {v} doesn't have a time zone")
# Assume v is a string and trying to parse it
for dt_format in SUPPORTED_TIME_FORMATS:
try:
dt = datetime.strptime(v, dt_format)
if not dt.tzinfo:
raise ValueError(f"No timezone parsed for DT {v}")
logger.debug(f"Creating DT: {dt}")
return dt.astimezone(timezone.utc)
except ValueError:
pass
raise ValueError(
f"{v} - unsupported date format. Available formats are: {SUPPORTED_TIME_FORMATS}"
)
class TransactionRead(TransactionBase):
id: int I guess that the only confusing part here, is that the field name |
Beta Was this translation helpful? Give feedback.
-
First Check
Commit to Help
Example Code
Description
My objective is to have a
datetime
field which is consistent in terms of timezone. To that end, I believe that the following points have to be considered:SQLModel
/pydantic
have to parse the timezone and validate itIn the example code above, I present my attempt to tackle this. To that end, I'm trying to follow the recommendations in the multiple-models section of the docs. I'm also trying to use the suggestion in this comment.
The API part is implemented as follows:
The questions...
With the above, I can achieve:
This leaves me with the following questions:
sa.Column(sa.DateTime(timezone=True), nullable=False)
in the definition ofTransactionBase
. Is it correct? In this case, I assume I "define" that the content of thetransaction_ts
is supposed/expected to be UTC.SQLModel
. Any idea?Operating System
macOS
Operating System Details
No response
SQLModel Version
0.0.8
Python Version
3.11
Additional Context
No response
Beta Was this translation helpful? Give feedback.
All reactions