# Get now() with local system timezone without knowing the timezone first

**URL:** <https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665>\
**Category:** Ideas\
**Created:** [January 11, 2023, 4:58am UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665 "2023-01-11T04:58:43Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![uranusjr](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/uranusjr/32/103_2.png) [@uranusjr](https://discuss.python.org/u/uranusjr)\
**Post date:** [January 11, 2023, 4:58am UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665/1 "2023-01-11T04:58:43Z")

</div>

Currently `now()` and `now(None)` would return a naive datetime in the local system timezone (inherited from long ago), while `now(tz)` gets you an aware datetime in the timezone `tz`. What’s missing though is a way to get an _aware_ datetime in the local system timezone. This value can be obtained in a roundabout way:

```python
# Or use ZoneInfo("UTC") instead.
datetime.now(timezone.utc).astimezone()

```

Not exactly obvious, and also wastes some time re-calculating UTC offsets (not significant but technically unnecessary).

* * *

Since changing the behaviour of `now(None)` is likely off-limits, I can think of three possible interface additions; new ideas are very much welcomed as well.

1. Add a `nowtz()` that always returns an aware datetime, matching the `astimezone()` interface. The name is inspired by `timetz()`.
2. Add a special sentinel `LOCAL` so `now(LOCAL)` returns an aware datetime in the local system timezone. This can also be used in `astimezone(LOCAL)` (behaving the same as `astimezone(None)`).
3. Add a separate function to return the current local system timezone, for example `timezone.local()`, so we can write `datetime.now(timezone.local())`. This does not eliminate the UTC offset calculation, but provides a slightly more obvious interface than the currently available approach.

---

<div class="post-metadata">

**Author:** ![barry](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/barry/32/42_2.png) [@barry](https://discuss.python.org/u/barry)\
**Post date:** [January 11, 2023, 9:23pm UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665/2 "2023-01-11T21:23:00Z")

</div>

Another possibility is to add an interface to [zoneinfo](https://docs.python.org/3/library/zoneinfo.html#module-zoneinfo) which can be used to give you a `ZoneInfo` object in your local timezone. It might even make sense to allow `ZoneInfo()` to take zero arguments, defaulting to the local timezone. Assuming (though I don’t know for sure) that there are system APIs on all the major OSes to return that information.

---

<div class="post-metadata">

**Author:** ![brettcannon](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/brettcannon/32/34895_2.png) [@brettcannon](https://discuss.python.org/u/brettcannon)\
**Post date:** [January 11, 2023, 10:58pm UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665/3 "2023-01-11T22:58:52Z")

</div>

Could do `datetime.now(local=True)` and have `local` be keyword-only.

---

<div class="post-metadata">

**Author:** ![barry](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/barry/32/42_2.png) [@barry](https://discuss.python.org/u/barry)\
**Post date:** [January 12, 2023, 12:32am UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665/4 "2023-01-12T00:32:06Z")

</div>

There are definitely options at the Python API level, and they aren’t necessarily mutually exclusive \[1\]. I haven’t researched it, but what are the system level APIs to get the current timezone on Linux, macOS, and Windows? Once you have that, and can translate that into `zoneinfo` names, it should be easy to add the APIs.

* * *

1. like, you could have zero-arg `ZoneInfo()` under the hood of `datetime.now(local=True)`

---

<div class="post-metadata">

**Author:** ![uranusjr](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/uranusjr/32/103_2.png) [@uranusjr](https://discuss.python.org/u/uranusjr)\
**Post date:** [January 13, 2023, 3:56pm UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665/5 "2023-01-13T15:56:13Z")

</div>

Depends on how you define timezone. All systems have a way to return the currently active _offset_ and _timezone code_, but what exactly that timezone maps to a ZoneInfo is not guaranteed to be available. So adding an interface to ZoneInfo would not universally work (although could still be a good idea on platforms where it does work), while an interface on `datetime` (either on class `datetime` or `timezone`) would.

---

<div class="post-metadata">

**Author:** ![barry](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/barry/32/42_2.png) [@barry](https://discuss.python.org/u/barry)\
**Post date:** [January 13, 2023, 6:05pm UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665/6 "2023-01-13T18:05:36Z")

</div>

Great point.

Looks like on macOS, this would be the [relevant API](https://developer.apple.com/documentation/foundation/timezone)?

---

<div class="post-metadata">

**Author:** ![uranusjr](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/uranusjr/32/103_2.png) [@uranusjr](https://discuss.python.org/u/uranusjr)\
**Post date:** [January 14, 2023, 4:01am UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665/7 "2023-01-14T04:01:39Z")

</div>

Yes I think so. I have no idea whether that uses the same underlying system config as the C API though.

On Windows there’s `GetDynamicTimeZoneInformation` (I think this is the correct one). On Linux I think only [localtime](https://www.gnu.org/software/libc/manual/html_node/Broken_002ddown-Time.html) is guaranteed, which Python already exposes in `time.localtime()`, to return a code and an offset.

---

<div class="post-metadata">

**Author:** ![csm10495](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/csm10495/32/9731_2.png) [@csm10495](https://discuss.python.org/u/csm10495)\
**Post date:** [January 14, 2023, 5:37am UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665/8 "2023-01-14T05:37:52Z")

</div>

Does:

```auto
>>> import time
>>> time.tzname
('Pacific Standard Time', 'Pacific Daylight Time')

```

Work for getting ‘my timezone’?

---

<div class="post-metadata">

**Author:** ![eryksun](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/eryksun/32/697_2.png) [@eryksun](https://discuss.python.org/u/eryksun)\
**Post date:** [January 14, 2023, 8:02am UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665/9 "2023-01-14T08:02:46Z")

</div>

> [@uranusjr](#):
>
> On Windows there’s `GetDynamicTimeZoneInformation` (I think this is the correct one)

The C runtime’s [`_tzset()`](https://learn.microsoft.com/cpp/c-runtime-library/reference/tzset) function\[1\] calls [`GetTimeZoneInformation()`](https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-gettimezoneinformation) to compute the time zone’s UTC offset and DST bias, but only if the “TZ” environment variable isn’t set. For example:

```auto
>>> time.strftime('%z (%Z)')
'+0000 (GMT Standard Time)'
>>> os.environ['TZ'] = 'ZZZ+10:00'
>>> ctypes.CDLL('ucrtbase')._tzset()
0
>>> time.strftime('%z (%Z)')
'-1000 (ZZZ)'

```

* * *

1. For some unknown reason, Python’s `time.tzset()` has never been implemented on Windows.

---

<div class="post-metadata">

**Author:** ![uranusjr](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/uranusjr/32/103_2.png) [@uranusjr](https://discuss.python.org/u/uranusjr)\
**Post date:** [January 14, 2023, 9:47am UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665/10 "2023-01-14T09:47:38Z")

</div>

`time.tzname` tells you what timezone rule your system is configured to use, but does not tell you which timezone is active (DST or not) right now, which is needed for `now`. Another problem is the value it gives is system-dependent, may not be compatible to the IANA database, and thus cannot always be converted into a useful timezone offset.

---

<div class="post-metadata">

**Author:** ![csm10495](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/csm10495/32/9731_2.png) [@csm10495](https://discuss.python.org/u/csm10495)\
**Post date:** [January 14, 2023, 6:24pm UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665/11 "2023-01-14T18:24:31Z")

</div>

[Python daylight savings time - Stack Overflow](https://stackoverflow.com/a/2881048/3824093) shows how to tell if dst is active or not.

Combine that with my answer to get the timezone and a name lookup table of sorts and you’d probably get it working easier than trying to hit more low level apis directly.

---

<div class="post-metadata">

**Author:** ![eryksun](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/eryksun/32/697_2.png) [@eryksun](https://discuss.python.org/u/eryksun)\
**Post date:** [January 14, 2023, 9:00pm UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665/12 "2023-01-14T21:00:56Z")

</div>

> [@csm10495](#):
>
> ```auto
> >>> import time
> >>> time.tzname
> ('Pacific Standard Time', 'Pacific Daylight Time')
> 
> ```

On Windows, time zone names are non-standard (e.g. not “PST” or “PDT”), and they’re localized to the user’s preferred UI language. For example, with the preferred UI language and locale set to Japanese, check `time.tzname` and `time.strftime('%Z')`\[1\].

```auto
>>> import time, locale
>>> time.tzname
('太平洋標準時', '太平洋夏時間')

```

```auto
>>> locale.setlocale(locale.LC_CTYPE, 'ja_JP.utf8')
'ja_JP.utf8'
>>> locale.setlocale(locale.LC_TIME, 'ja_JP.utf8')
'ja_JP.utf8'
>>> time.strftime('%Z')
'太平洋標準時'

```

The C runtime provides functions to get the time zone variables that `_tzset()` computes.

```auto
>>> ucrt = ctypes.CDLL('ucrtbase', use_errno=True)
>>> timezone = ctypes.c_long()
>>> dstbias = ctypes.c_long()
>>> ucrt._get_timezone(ctypes.byref(timezone))
0
>>> ucrt._get_dstbias(ctypes.byref(dstbias))
0
>>> timezone.value
28800
>>> dstbias.value
-3600

```

* * *

1. `time.strftime()` has serious issues with the locale encoding on Windows. There wouldn’t be an issue if Python used C `wcsftime()` instead of C `strftime()` on Windows (using C `strftime()` was a work around for a bug that hasn’t existed for a very long time). I had to modify both locale categories because `time.strftime()` incorrectly decodes the result using the encoding of the `LC_CTYPE` category, but C `strftime()` encodes the native `wcsftime()` wide-character result using the `LC_TIME` encoding. I had to modify the locale categories to explicitly use Japanese because the default locale, i.e. `locale.setlocale(locale.LC_TIME, "")`, combines the user locale (e.g. “Japanese\_Japan”) with the encoding from the system locale, and my system locale is set to use 1252 as the ANSI code page. I have this crazy idea that if the runtime defaults to using the user locale, then it should default to using the code page that’s defined for that locale, i.e. code page 932 or UTF-8 for Japanese. Windows is multi-user, so it’s extremely bad design to assume that all users select a locale and preferred UI language that matches the system locale.

---

<div class="post-metadata">

**Author:** ![uranusjr](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/uranusjr/32/103_2.png) [@uranusjr](https://discuss.python.org/u/uranusjr)\
**Post date:** [January 15, 2023, 1:04am UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665/13 "2023-01-15T01:04:31Z")

</div>

I never said it’s impossible to get the timezone; I already propose a way to get a functional equivalent in the original post. The point of this thread is it’s not obvious, and using combining `localtime` with `tzname` is still much less obvious than using `astimezone`.

And if you want to use `localtime` in the first place, there is actually a `tm_zone` field to directly get the currently active timezone name, and a `tm_offset` to construct a datetime. This is what `astimezone` uses under the hood, in fact. Checking `tzname` is entirely unnecessary for this use case. (But getting the timezone name either `localtime` and `tzname` has the same IANA compatibility issue, as both Eryk and I mentioned.)

---

<div class="post-metadata">

**Author:** ![eryksun](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/eryksun/32/697_2.png) [@eryksun](https://discuss.python.org/u/eryksun)\
**Post date:** [January 15, 2023, 5:01am UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665/14 "2023-01-15T05:01:13Z")

</div>

> [@uranusjr](#):
>
> there is actually a `tm_zone` field to directly get the currently active timezone name, and a `tm_offset` to construct a datetime. This is what `astimezone` uses under the hood, in fact.

I presume you meant `tm_gmtoff`. A `tm` record in the Windows C runtime doesn’t have the `tm_zone` and `tm_gmtoff` fields that are found on BSD, macOS and Linux systems. These fields aren’t part of the POSIX standard. The [`local_timezone_from_timestamp()`](https://github.com/python/cpython/blob/206f05a46b426eb374f724f8e7cd42f2f9643bb8/Modules/_datetimemodule.c#L6058-L6118) function thus has to take the long path of calling `strftime("%Z")` to get the time zone name, and it has to manually compute the UTC offset.

On Windows, we could call [`_get_timezone()`](https://learn.microsoft.com/cpp/c-runtime-library/reference/get-timezone) to get the UTC offset. If `tm_isdst` is set in the local time, also add the DST bias from [`_get_dstbias()`](https://learn.microsoft.com/cpp/c-runtime-library/reference/get-dstbias). We should use `wcsftime(L"%Z", ...)` to get the time zone name as a wide-character string, which avoids encoding problems\[1\].

* * *

1. The time zone name from `strftime("%Z")` is potentially decoded incorrectly. The C runtime encodes the name using the `LC_TIME` encoding, but Python decodes it using the `LC_CTYPE` encoding. Even if both categories are set to the default locale, the C runtime’s default locale uses the ANSI code page of the system, while the time zone name is in the preferred UI language of whichever thread last called `_tzset()`. (The default UI language of a thread is that of the current user, but this can be overridden for the current process or current thread via `SetProcessPreferredUILanguages()` and `SetThreadPreferredUILanguages()`.) It’s by no means guaranteed that the time zone name can be encoded using the system ANSI code page.

---

<div class="post-metadata">

**Author:** ![EpicWink](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/epicwink/32/17968_2.png) [@EpicWink](https://discuss.python.org/u/EpicWink)\
**Post date:** [January 15, 2023, 7:16am UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665/15 "2023-01-15T07:16:21Z")

</div>

> [@uranusjr](#):
>
> Add a special sentinel `LOCAL` so `now(LOCAL)` returns an aware datetime in the local system timezone.

I like this option out of all the options presented:

- No exclusive parameter introduced, which helps with my mental model of the function
- Removed redundant computation
- No new methods introduced

As for the variable’s name, I prefer longer and more descriptive names (eg `USE_LOCAL`), but standard library devs seen to prefer shorter memnomics.

---

<div class="post-metadata">

**Author:** ![pganssle](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pganssle/32/245_2.png) [@pganssle](https://discuss.python.org/u/pganssle)\
**Post date:** [April 26, 2023, 4:47pm UTC](https://discuss.python.org/t/get-now-with-local-system-timezone-without-knowing-the-timezone-first/22665/16 "2023-04-26T16:47:56Z")

</div>

I wrote an article some time ago about why we cannot have a `tzinfo` object that represents the local time: [Why naÏve times are local times in Python](https://blog.ganssle.io/articles/2022/04/naive-local-datetimes.html). Unfortunately I think the design we have at the moment is the best that can be done while guaranteeing to satisfy the invariants of `datetime` objects.

It might be plausible to do something like what [tzlocal](https://github.com/regebro/tzlocal) is doing and try to figure out what IANA key applies in the local time zone, though it seems kinda tricky and like it might not always work, so it might be best to leave it to `tzlocal` to do that for us (since they can backport changes for older versions more easily when platforms change their APIs).
