Skip to content

date.strftime and datetime.strftime format dates wrong in some locales. #130528

Open

Description

@petergraham

Bug report

Bug description:

date.strftime and datetime.strftime format dates wrong in some locales. For example, when the locale is Bulgaria the '%A' format string returns an incorrect value.

>>> locale.setlocale(locale.LC_TIME, 'bg')    # 'bg' is Bulgaria
>>> date_str = datetime.date(2025, 2, 25).strftime('%A') # %A is locale's day of the week
>>> date_str
'âòîðíèê'
>>> date_str.encode('cp1252').decode('cp1251')  # I think we're interpreting a cp1251 string as cp1252
'вторник'  # Google tells me this is Tuesday in Bulgarian

This also fails with the '%x' format string

I've only tested this on Windows.

My guess is that CPython is making a narrow character API call and using the wrong code page. It should be using the wide character call instead.

CPython versions tested on:

3.13

Operating systems tested on:

Windows

Activity

  1. picnixz commented on Feb 25, 2025

    @picnixz
  2. added
    stdlibStandard Library Python modules in the Lib/ directory
    on Feb 25, 2025
  3. picnixz commented on Feb 25, 2025

    @picnixz
    Member

    Ok, second question. I think what's being returned with %A is encoded with something that is not necessarily useful for the LC_TIME language. CP-1252 is a legacy default format on Windows according to Wikipedia, and I assume that it's as if we had "no localized encoding".

    @serhiy-storchaka you're more of an expert in this case, is it the expected behaviour? namely, should we expect the caller to actually perform the encode/decode themselves or is it something that's wrong with datetime?

  4. tomasr8 commented on Feb 25, 2025

    @tomasr8
    Member

    I think locale.getlocale(locale.LC_TIME)[1] should return the encoding used by strftime

  5. serhiy-storchaka commented on Feb 25, 2025

    @serhiy-storchaka
    Member

    Oh-oh, it is yet one issue similar to #69998 and bpo-28604. The workaround at he user side is to set LC_CTYPE to the same value as LC_TIME. We can fix this at Python side, but at the cost of making strftime() slower and potentially more thread-unsafe (it is already not thread-safe in sense that it can fail if the environment is changed in other thread, but now it will be able to break other threads by changing the environment).

  6. added
    3.12only security fixes
    3.13only security fixes
    3.14bugs and security fixes
    on Feb 25, 2025
  7. donbarbos commented on Feb 25, 2025

    @donbarbos
    Contributor

    I reproduce your case on Linux, so first of all, if you don't have locale (you can check it with locale -a command), you will get locale.Error: unsupported setting, but all available locales were utf8 so I think there was no error:

    >>> locale.setlocale(locale.LC_TIME, 'ka_GE.utf8')
    'ka_GE.utf8'
    >>> datetime.date(2025, 2, 25).strftime('%A')
    'სამშაბათი' # Tuesday
    >>> locale.setlocale(locale.LC_TIME, 'ka_GE')
    Traceback (most recent call last):
      File "<python-input-8>", line 1, in <module>
        locale.setlocale(locale.LC_TIME, 'ka_GE')
        ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
      File "./lib/python3.13/locale.py", line 615, in setlocale
        return _setlocale(category, locale)
    locale.Error: unsupported locale setting

    (I checked the behavior on the main branch and also version 3.13)

  8. StanFromIreland commented on Mar 21, 2025

    @StanFromIreland
    Member
    >>> locale.setlocale(locale.LC_TIME, 'bg_BG')
    'bg_BG'
    >>> print(datetime.date(2025, 2, 25).strftime('%A'))
    вторник
    >>> locale.setlocale(locale.LC_TIME, 'bg_BG.UTF-8')
    'bg_BG.UTF-8'
    >>> print(datetime.date(2025, 2, 25).strftime('%A'))
    вторник
    >>> locale.setlocale(locale.LC_TIME, 'bg_BG.cp1251')
    'bg_BG.cp1251'
    >>> print(datetime.date(2025, 2, 25).strftime('%A'))
    вторник

    Unable to reproduce on Linux, maybe this has something to do with how Windows is configured? That seems to be the case for other similar issues.

  9. serhiy-storchaka commented on Oct 17, 2025

    @serhiy-storchaka
    Member

    I got it. PC/pyconfig.h was removed in 79dad03 (gh-111650) and restored in 986c367 (gh-133779). Currently only 3.13 does not have it. HAVE_WCSFTIME is defined in PC/pyconfig.h, if it is not defined, strftime() is used instead of wcsftime(). I suspect it affects other code, we don't know how much.

    I think this is a serious regression. PC/pyconfig.h should be restored in the 3.13 branch.

    cc @zooba, @Yhg1s

  10. added and removed
    3.12only security fixes
    3.14bugs and security fixes
    on Oct 17, 2025
  11. deleted a comment from on Oct 18, 2025
  12. zooba commented on Oct 20, 2025

    @zooba
    Member

    I think this is a serious regression. PC/pyconfig.h should be restored in the 3.13 branch.

    In 3.13 it gets generated during build, in order to try and support free-threading. But that was a nightmare, so we rolled it back for 3.14 (so you can't just use Py_GIL_DISABLED when building an extension module - whoever is building has to provide it, potentially by checking sysconfig).

    PC/pyconfig.h.in should be there, and it gets used with virtually no modification. Trust me when I say the entire build would be failing if it weren't there. As far as I'm aware, nobody has changed HAVE_WCFSTIME.

    We can fix this at Python side, but at the cost of making strftime() slower and potentially more thread-unsafe (it is already not thread-safe in sense that it can fail if the environment is changed in other thread, but now it will be able to break other threads by changing the environment).

    Perhaps the fix here is to use create_locale and strftime_l and explicitly pass the locale just to that call? No need to modify the global state if you're making the call directly.

  13. serhiy-storchaka commented on Oct 20, 2025

    @serhiy-storchaka
    Member

    Ah, it turned out, there is other relavant difference between 3.13 and 3.14. The use of wcsftime() on Windows was intentionally disabled in bpo-10653/gh-54862. It was enabled again in #125658. The original issue with wcsftime() was fixed in Windows 10 v2004 (build 19041).

    Since we don't support Windows older than Windows 10 v2004, perhaps #125658 can be backported. cc @Yhg1s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.13only security fixesOS-windowsextension-modulesC modules in the Modules dirstdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions