Запись времени в базу данных с помощью model.DateTimeField() в Python Django, часовой пояс UTC отображается в базе данных некорректно
settings.py
TIME_ZONE = "Asia/Taipei"
USE_I18N = True
USE_TZ = True
models.py
from django.db import models
class ApiLog(models.Model):
endpoint = models.CharField(max_length=255)
method = models.CharField(max_length=10)
request_body = models.TextField()
response_body = models.TextField()
timestamp = models.DateTimeField()
class Meta:
db_table = 'api_log'
managed = False # 若資料表為手動建立,將 managed 設為 False
def __str__(self):
return f"{self.method} {self.endpoint} at {self.timestamp}"
views.py
def simple_api(request):
endpoint = request.path
method = request.method
request_body = request.body.decode('utf-8') if request.body else ""
if method == 'GET':
response_data = {'message': 'This is a GET request'}
elif method == 'POST':
try:
data = json.loads(request.body)
response_data = {'message': 'Data received', 'data': data}
except json.JSONDecodeError:
response_data = {'error': 'Invalid JSON format'}
else:
response_data = {'error': 'Method not allowed'}
response_body = json.dumps(response_data)
# def UTC+8
tz = pytz.timezone('Asia/Taipei')
# get utc+8 time
current_utc_plus_8_time = timezone.now().astimezone(tz)
print("Current UTC+8 Time:", current_utc_plus_8_time)
# record ApiLog
ApiLog.objects.create(
endpoint=endpoint,
method=method,
request_body=request_body,
response_body=response_body,
timestamp=current_utc_plus_8_time
)
return JsonResponse(response_data)
Исходя из этой схемы, я ожидаю, что записанная временная метка в базе данных будет иметь формат «yyyy-MM-DD HH:mm:ss.ssssss+08:00.»
Однако при прямом запросе с помощью оператора SELECT в базе данных результат преобразуется в UTC+0. Я пробовал несколько методов решения этой проблемы.
Например:
- На Java, используя mybatis
mapper
@Mapper
public interface ApiLogMapper {
@Insert("INSERT INTO api_log (endpoint, method, request_body, response_body, timestamp) " +
"VALUES (#{endpoint}, #{method}, #{requestBody}, #{responseBody}, #{timestamp})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insertApiLog(ApiLog apiLog);
}
контроллер
@PostMapping("/message")
public ResponseEntity<String> handleMessage(@RequestBody String message) {
// def UTC+8 OffsetDateTime
OffsetDateTime dateTimeUTC8 = OffsetDateTime.now(ZoneOffset.ofHours(8));
String response = "message";
// record ApiLog
ApiLog log = new ApiLog();
log.setEndpoint("/api/chatbox/message");
log.setMethod("POST");
log.setRequestBody(message);
log.setResponseBody(response);
log.setTimestamp(dateTimeUTC8);
System.out.println(log.getTimestamp());
apiLogMapper.insertApiLog(log);
return ResponseEntity.ok(response);
}
этот метод корректно отображает результаты UTC+8 в базе данных.
- В Python использование SQL-скрипта для вставки также отображает результаты в UTC+8.
def log_api_call(endpoint: str, method: str, request_body: str,
response_body: str):
connection = get_connection()
cursor = connection.cursor()
# def utc+8 timezone
timezone_utc8 = pytz.timezone('Asia/Taipei')
utc8_time = datetime.now(timezone_utc8)
print(utc8_time)
query = """
INSERT INTO api_log (endpoint, method, request_body, response_body, timestamp)
VALUES (?, ?, ?, ?, ? AT TIME ZONE 'Taipei Standard Time' )
"""
cursor.execute(query, (
endpoint, method, request_body, response_body, utc8_time))
connection.commit()
cursor.close()
connection.close()
@app.post("/example-endpoint")
async def example_endpoint(request: Request):
request_body = await request.json()
response_body = {"message": "This is a response"}
response_body_str = json.dumps(response_body)
# record APILog
log_api_call(
endpoint="/example-endpoint",
method="POST",
request_body=json.dumps(request_body),
response_body=response_body_str
)
return response_body
дополнительная информация: mcr.microsoft.com/mssql/server:2022-lates TZ=Asia/Taipei
SELECT SYSDATETIMEOFFSET() AS CurrentDateTimeWithOffset;
CurrentDateTimeWithOffset |
-----------------------------+
2024-11-14 10:25:44.588 +0800|
SELECT CURRENT_TIMEZONE() AS TimeZone;
TimeZone |
------------------+
(UTC+08:00) Taipei|
table
create table api_log
(
id bigint identity
primary key,
endpoint nvarchar(255),
method nvarchar(10),
request_body nvarchar(max),
response_body nvarchar(max),
timestamp datetimeoffset
)
go
Есть ли способ добиться этого во фреймворке Django?
Как описано в вопросе, я ожидаю, что время, отображаемое в базе данных, будет соответствовать часовому поясу UTC, в котором оно было записано.
если время AP UTC+8 равно 2024-11-14 09:53:31.916319**+08:00**
, тогда временная метка Select DB должна быть 2024-11-14 09:53:31.916319**+08:00**
Когда вы храните дату/время в Django, она всегда преобразуется в время utc+0.
Это кажется намеренным и может привести к преобразованию времени из часового пояса Django во время utc, установленное в настройках.
from django.utils import timezone
timezone.now() # utc +0 time
timezone.localtime(timezone.now()) # utc +{timzeon in settings} time