date
格式化失败
发布于 | 2023-11-14 23:32 | ||
保质期 | 2026-11 | ||
新鲜度 | 61% |
复现
python
from datetime import date
print(date(2023, 11, 3).strftime('%Y年%m月%d日'))
以上代码在 Windows 下 Python 3.7 中可以复现,在其它操作系统或者更高版本就没有。
错误如下:
text
Traceback (most recent call last):
File "C:\Program Files\Python37\lib\code.py", line 90, in runcode
exec(code, self.locals)
File "<input>", line 1, in <module>
UnicodeEncodeError: 'locale' codec can't encode character '\u5e74' in position 2: encoding error
这个问题是写业务时指定了 Django REST Framework 序列化器日期字段格式之后发现的:
python
from rest_framework import serializers
class MemberInfoSerializer(serializers.ModelSerializer):
create_at = serializers.DateTimeField(format='%Y/%m/%d')
gender = serializers.BooleanField()
birth = serializers.DateField(format='%Y年%m月%d日')
在 create_at
序列化时不会报错,轮到 birth
就会报 UnicodeEncodeError
,且错误的消息一致。
解决
1、指定全局设置
在 ./[project]/settings.py
中指定DRF的 默认 日期时间格式,并去掉序列化器字段中的 format
参数,这是最佳方案。
python
REST_FRAMEWORK = {
'DATE_FORMAT': '%Y-%m-%d',
'TIME_FORMAT': '%H:%M:%S',
'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S',
}
2、手动格式化
如果必须自定义格式,但字段只读,只需要改用类方法进行序列化:
python
from rest_framework import serializers
class MemberInfoSerializer(serializers.ModelSerializer):
birth = serializers.SerializerMethodField()
def get_birth(self, instance) -> str:
return instance.birth.strftime('%Y{0}%m{1}%d{2}').format(*'年月日')
这个方案摘自Stack Overflow。注意:SerializerMethodField
会将 read_only
参数的值覆写为 True
。
3、自定义字段
如果必须自定义格式,并且字段需要允许读和写,那么只能自定义字段:
python
from datetime import date, datetime
from rest_framework import serializers
class MeowDateField(serializers.Field):
def to_representation(self, value: date) -> str:
return value.strftime('%Y{0}%m{1}%d{2}').format(*'年月日')
def to_internal_value(self, value: str) -> date:
return datetime.strptime(value, '%Y年%m月%d日')
4、设置语言环境
如果不是用DRF的话,可以用 setlocale()
方法:
python
from contextlib import contextmanager
import datetime
import locale
@contextmanager
def localize(local_name: str, lc_var=locale.LC_ALL):
orgin_local = locale.getlocale()
try:
yield locale.setlocale(lc_var, local_name)
finally:
locale.setlocale(lc_var, orgin_local)
with localize('zh'):
print(datetime.datetime.now().strftime('%Y年%m月%d日'))
这个方案搬运自Stack Overflow,不过我没有实际在业务中使用,你需要参阅国际化服务这个标准库来了解可能潜在的bug。