Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python版本与日期格式化 #6

Open
aixcyi opened this issue Nov 14, 2023 · 0 comments
Open

Python版本与日期格式化 #6

aixcyi opened this issue Nov 14, 2023 · 0 comments
Labels
Python 3 一种最初多用于科研的弱类型脚本语言

Comments

@aixcyi
Copy link
Owner

aixcyi commented Nov 14, 2023

问题复现

from datetime import date;

print(date(2023, 11, 3).strftime('%Y年%m月%d日'))

以上代码在 Python 3.7 (Windows) 中可以复现以下报错,在其它操作系统或者更高版本就没有,更低版本则没试过。

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

这个问题是写业务时指定了DRF序列化器日期字段格式之后发现的:

from rest_framework import serializers

class MemberInfoSerializer(serializers.ModelSerializer):
    create_at = serializers.DateTimeField(format='%Y/%m/%d')
    gender = serializers.CharField(source='get_gender_display', default=None)
    birth = serializers.DateField(format='%Y年%m月%d日')

create_at 序列化时不会报错,轮到 birth 就会报 UnicodeEncodeError ,且错误的消息一致。

解决方案

0x1 指定DRF全局设置

./[project]/settings.py 中指定DRF序列化器的 默认 日期时间格式。这是最佳方案,统一的格式可以免去冗余代码,也方便前端作统一解析。

# Django 全局设置
DATE_FORMAT = '%Y-%m-%d'
TIME_FORMAT = '%H:%M:%S'
DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'

# DRF 全局设置
REST_FRAMEWORK = {
    'DATE_FORMAT': DATE_FORMAT,
    'TIME_FORMAT': TIME_FORMAT,
    'DATETIME_FORMAT': DATETIME_FORMAT,
}

0x2 手动格式化

如果必须自定义格式,但字段只读,只需要改用类方法进行序列化:

from rest_framework import serializers

class MemberInfoSerializer(serializers.ModelSerializer):
    create_at = serializers.SerializerMethodField()

    def get_create_at(self, instance) -> str:
        return instance.create_at.strftime('%Y{0}%m{1}%d{2}').format(*'年月日')

这个方案摘自Stack Overflow。注意:SerializerMethodField 会将 read_only 参数的值覆写为 True

0x3 自定义字段

如果必须自定义格式,并且字段需要读和写,那么只能自定义字段

from datetime import date, datetime
from rest_framework import serializers

class BirthdayField(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日')


class MemberInfoSerializer(serializers.ModelSerializer):
    birth = BirthdayField()

0x4 设置语言环境

如果不是用DRF的话,可以用 setlocale() 方法:

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。

@aixcyi aixcyi added the Python 3 一种最初多用于科研的弱类型脚本语言 label Nov 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Python 3 一种最初多用于科研的弱类型脚本语言
Projects
None yet
Development

No branches or pull requests

1 participant