bug-gnulib@gnu.org mirror (unofficial)
 help / color / mirror / Atom feed
* localtime on native Windows
  2024-02-10 11:10 ` Bruno Haible
@ 2024-02-11 12:46   ` Bruno Haible
  2024-02-13  2:02     ` Paul Eggert
  0 siblings, 1 reply; 9+ messages in thread
From: Bruno Haible @ 2024-02-11 12:46 UTC (permalink / raw)
  To: bug-gnulib; +Cc: Paul Eggert

[-- Attachment #1: Type: text/plain, Size: 1802 bytes --]

I wrote:
> For these modules, the next function to provide in an MT-safe way is
> localtime_r.

Our gmtime_r and localtime_r are MT-safe on native Windows. I ran the
test-gmtime_r-mt and test-localtime_r-mt tests for 2000 seconds each, and
they did not crash.

But the problem is that localtime() and localtime_r() on native Windows
produce nonsensical results:
  - They pretend that in France, in 2007, DST began on 2007-03-11. When in
    fact, it started on 2007-03-25.
  - The hour is wrong.
Witness: The attached program loc.c.

> On native Windows, when the 'localtime_s' function [1][2]
> is not available, such as on the older Windows versions that Emacs cares
> about, the solution is to use GetTimeZoneInformation [3].

None of the GetTimeZoneInformation APIs from the Windows DLLs works either.
They pretend that in German and French time zones, DST starts on March 5,
in all years. Witness: The attached program tzi.c.

So, there is no way around implementing a correct localtime_r, based on
tzdata, in Gnulib.

It will be useful
  - for localtime_r on native Windows,
  - for nstrftime, c_nstrftime, parse-datetime, which all take a timezone_t
    argument.

For reading tzdata: The first question is how to include tzdata in gnulib.
  - AFAICS, the main data file (without comments) is tzdata.zi and is about
    100 KB large. It can be upgraded simply by copying the newest tzdata.zi
    from a newer tzdata distribution. Including such a file in gnulib would
    be OK (re copyright, number of files, total size), right?
  - Whereas including all files from /usr/share/zoneinfo is probably not
    acceptable (> 1300 files, ca. 6 MB total size).
  - Access pattern: In a running program, very few among the time zones will
    be used. Therefore, caching in memory is essential.

Bruno

[-- Attachment #2: loc.c --]
[-- Type: text/x-csrc, Size: 1519 bytes --]

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>


/* Some common time zone name.  */

#if defined _WIN32 && !defined __CYGWIN__
/* Cf. <https://ss64.com/timezones.html>  */
# define FRENCH_TZ  "Romance Standard Time"
#else
/* Cf. <https://en.wikipedia.org/wiki/List_of_tz_database_time_zones>  */
//# define FRENCH_TZ  "Europe/Paris"
# define FRENCH_TZ  "Europe/Berlin"
#endif


static void
show_localtime_result (time_t t)
{
  struct tm *result = localtime (&t);
  printf ("%ld -> %04d-%02d-%02d %02d:%02d:%02d DST=%d\n",
          (long) t,
          result->tm_year + 1900, result->tm_mon + 1, result->tm_mday,
          result->tm_hour, result->tm_min, result->tm_sec,
          result->tm_isdst);
}

int
main (int argc, char *argv[])
{
#if defined _WIN32 && !defined __CYGWIN__
  _putenv ("TZ" "=" FRENCH_TZ);
#else
  setenv ("TZ", FRENCH_TZ, 1);
#endif

  show_localtime_result (1173578399); /* 2007-03-11 02:59:59 */
  show_localtime_result (1173578401); /* 2007-03-11 03:00:01 */

  show_localtime_result (1174784399); /* 2007-03-25 01:59:59 */
  show_localtime_result (1174784401); /* 2007-03-25 03:00:01 */

  return 0;
}
/*
glibc, Cygwin:
1173578399 -> 2007-03-11 02:59:59 DST=0
1173578401 -> 2007-03-11 03:00:01 DST=0
1174784399 -> 2007-03-25 01:59:59 DST=0
1174784401 -> 2007-03-25 03:00:01 DST=1

Native Windows:
1173578399 -> 2007-03-11 01:59:59 DST=0
1173578401 -> 2007-03-11 03:00:01 DST=1
1174784399 -> 2007-03-25 01:59:59 DST=1
1174784401 -> 2007-03-25 02:00:01 DST=1
*/

[-- Attachment #3: tzi.c --]
[-- Type: text/x-csrc, Size: 4372 bytes --]

#include <stdio.h>
#include <windows.h>
#include <timezoneapi.h>
#include <wchar.h>

/* Cf. <https://ss64.com/timezones.html>  */
# define FRENCH_TZ  "Romance Standard Time"

int main ()
{
  _putenv ("TZ=" FRENCH_TZ);

  DWORD ret;

  TIME_ZONE_INFORMATION info1;
  ret = GetTimeZoneInformation (&info1);
  printf ("GetTimeZoneInformation:\n"
          "ret = %lu  info1 =\n"
          "standard: |%ls| bias=%ld date=%4d-%02d-%02d %02d:%02d:%02d.%03d\n"
          "daylight: |%ls| bias=%ld date=%4d-%02d-%02d %02d:%02d:%02d.%03d\n",
          ret,
          info1.StandardName,
          info1.StandardBias,
          info1.StandardDate.wYear,
          info1.StandardDate.wMonth,
          info1.StandardDate.wDay,
          info1.StandardDate.wHour,
          info1.StandardDate.wMinute,
          info1.StandardDate.wSecond,
          info1.StandardDate.wMilliseconds,
          info1.DaylightName,
          info1.DaylightBias,
          info1.DaylightDate.wYear,
          info1.DaylightDate.wMonth,
          info1.DaylightDate.wDay,
          info1.DaylightDate.wHour,
          info1.DaylightDate.wMinute,
          info1.DaylightDate.wSecond,
          info1.DaylightDate.wMilliseconds);

  DYNAMIC_TIME_ZONE_INFORMATION info2;
  ret = GetDynamicTimeZoneInformation (&info2);
  printf ("GetDynamicTimeZoneInformation:\n"
          "ret = %lu  info2 = bias=%ld timezonekey=%ls dynamicdisabled=%d\n"
          "standard: |%ls| bias=%ld date=%4d-%02d-%02d %02d:%02d:%02d.%03d\n"
          "daylight: |%ls| bias=%ld date=%4d-%02d-%02d %02d:%02d:%02d.%03d\n",
          ret,
          info2.Bias,
          info2.TimeZoneKeyName,
          info2.DynamicDaylightTimeDisabled,
          info2.StandardName,
          info2.StandardBias,
          info2.StandardDate.wYear,
          info2.StandardDate.wMonth,
          info2.StandardDate.wDay,
          info2.StandardDate.wHour,
          info2.StandardDate.wMinute,
          info2.StandardDate.wSecond,
          info2.StandardDate.wMilliseconds,
          info2.DaylightName,
          info2.DaylightBias,
          info2.DaylightDate.wYear,
          info2.DaylightDate.wMonth,
          info2.DaylightDate.wDay,
          info2.DaylightDate.wHour,
          info2.DaylightDate.wMinute,
          info2.DaylightDate.wSecond,
          info2.DaylightDate.wMilliseconds);

  DYNAMIC_TIME_ZONE_INFORMATION info3i;
  TIME_ZONE_INFORMATION info3;
  {
    DWORD i;
    for (i = 0; ; i++)
      {
        if (EnumDynamicTimeZoneInformation (i, &info3i) == ERROR_SUCCESS // Link error in mingw, OK in MSVC.
            && wcscmp (info3i.TimeZoneKeyName, L"Romance Standard Time") == 0)
          break;
      }
  }
  ret = GetTimeZoneInformationForYear (2007, &info3i, &info3);
  printf ("GetTimeZoneInformationForYear(2007):\n"
          "ret = %lu  info3 =\n"
          "standard: |%ls| bias=%ld date=%4d-%02d-%02d %02d:%02d:%02d.%03d\n"
          "daylight: |%ls| bias=%ld date=%4d-%02d-%02d %02d:%02d:%02d.%03d\n",
          ret,
          info3.StandardName,
          info3.StandardBias,
          info3.StandardDate.wYear,
          info3.StandardDate.wMonth,
          info3.StandardDate.wDay,
          info3.StandardDate.wHour,
          info3.StandardDate.wMinute,
          info3.StandardDate.wSecond,
          info3.StandardDate.wMilliseconds,
          info3.DaylightName,
          info3.DaylightBias,
          info3.DaylightDate.wYear,
          info3.DaylightDate.wMonth,
          info3.DaylightDate.wDay,
          info3.DaylightDate.wHour,
          info3.DaylightDate.wMinute,
          info3.DaylightDate.wSecond,
          info3.DaylightDate.wMilliseconds);

}
/* Compile:
   $CC tzi.c -Wall -D_WIN32_WINNT=_WIN32_WINNT_WIN8 -ladvapi32
 */
/* Results:

GetTimeZoneInformation:
ret = 1  info1 =
standard: |W. Europe Standard Time| bias=0 date=   0-10-05 03:00:00.000
daylight: |W. Europe Daylight Time| bias=-60 date=   0-03-05 02:00:00.000
GetDynamicTimeZoneInformation:
ret = 1  info2 = bias=-60 timezonekey=W. Europe Standard Time dynamicdisabled=0
standard: |W. Europe Standard Time| bias=0 date=   0-10-05 03:00:00.000
daylight: |W. Europe Daylight Time| bias=-60 date=   0-03-05 02:00:00.000
GetTimeZoneInformationForYear(2007):
ret = 1  info3 =
standard: |Romance Standard Time| bias=0 date=   0-10-05 03:00:00.000
daylight: |Romance Daylight Time| bias=-60 date=   0-03-05 02:00:00.000

*/

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: localtime on native Windows
  2024-02-11 12:46   ` localtime on native Windows Bruno Haible
@ 2024-02-13  2:02     ` Paul Eggert
  2024-02-18  2:38       ` Bruno Haible
  0 siblings, 1 reply; 9+ messages in thread
From: Paul Eggert @ 2024-02-13  2:02 UTC (permalink / raw)
  To: Bruno Haible, bug-gnulib

On 2024-02-11 04:46, Bruno Haible wrote:
> The first question is how to include tzdata in gnulib.
>    - AFAICS, the main data file (without comments) is tzdata.zi and is about
>      100 KB large. It can be upgraded simply by copying the newest tzdata.zi
>      from a newer tzdata distribution. Including such a file in gnulib would
>      be OK (re copyright, number of files, total size), right?

No problem with copyright; the file is in the public domain.

>   - Whereas including all files from /usr/share/zoneinfo is probably not
>     acceptable (> 1300 files, ca. 6 MB total size).

That can be automatically generated from tzdata.zi, by using zic, the 
source code of which is also in the public domain. This is common in 
GNU/Linux systems. If memory serves Android uses another approach: a 
single binary file indexed by timezone name.


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: localtime on native Windows
@ 2024-02-13 18:25 Brian Inglis
  2024-02-18  2:14 ` Bruno Haible
  0 siblings, 1 reply; 9+ messages in thread
From: Brian Inglis @ 2024-02-13 18:25 UTC (permalink / raw)
  To: Gnulib bugs, Bruno Haible

On Sun, 11 Feb 2024 13:46:50 +0100, Bruno Haible wrote on bug-gnulib:
> I wrote:
>> For these modules, the next function to provide in an MT-safe way is
>> localtime_r.
> 
> Our gmtime_r and localtime_r are MT-safe on native Windows. I ran the
> test-gmtime_r-mt and test-localtime_r-mt tests for 2000 seconds each, and
> they did not crash.
> 
> But the problem is that localtime() and localtime_r() on native Windows
> produce nonsensical results:
>   - They pretend that in France, in 2007, DST began on 2007-03-11. When in
>     fact, it started on 2007-03-25.
>   - The hour is wrong.
> Witness: The attached program loc.c.
> 
>> On native Windows, when the 'localtime_s' function [1][2]
>> is not available, such as on the older Windows versions that Emacs cares
>> about, the solution is to use GetTimeZoneInformation [3].
> 
> None of the GetTimeZoneInformation APIs from the Windows DLLs works either.
> They pretend that in German and French time zones, DST starts on March 5,
> in all years. Witness: The attached program tzi.c.
> 
> So, there is no way around implementing a correct localtime_r, based on
> tzdata, in Gnulib.
> 
> It will be useful
>   - for localtime_r on native Windows,
>   - for nstrftime, c_nstrftime, parse-datetime, which all take a timezone_t
>     argument.
> 
> For reading tzdata: The first question is how to include tzdata in gnulib.
>   - AFAICS, the main data file (without comments) is tzdata.zi and is about
>     100 KB large. It can be upgraded simply by copying the newest tzdata.zi
>     from a newer tzdata distribution. Including such a file in gnulib would
>     be OK (re copyright, number of files, total size), right?
>   - Whereas including all files from /usr/share/zoneinfo is probably not
>     acceptable (> 1300 files, ca. 6 MB total size).
>   - Access pattern: In a running program, very few among the time zones will
>     be used. Therefore, caching in memory is essential.

I was looking around to see if there was some way to leverage Windows UWP/.NET 
tzdata provided in:

	https://github.com/microsoft/icu

https://github.com/search?q=repo%3Amicrosoft%2Ficu%20tzdata&type=code

and noticed that some comments under:

https://learn.microsoft.com/en-ca/dotnet/core/extensions/globalization-icu#icu-on-webassembly

suggest that required data can be shrunk to ~300KB using Brotli!?

Perhaps gnulib could leverage some of the interfaces in:

	https://github.com/sillsdev/icu-dotnet

as an alternative interface to access tzdata on native Windows or .NET 5+ Core 
platforms, without building in tzdata and requiring updates?

Other options would be for some selection of generated data such as using 
zonenow.tab, supporting only the current time onward, for the 449 time zones 
currently in:

https://github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml

perhaps using CBOR format and/or Brotli compression to improve on using a zip or 
tar container format:

	470KB -> 221KB zip, 134KB tar.gz, 69KB tar.lz, 68KB tar.xz;

or using the usual built-in POSIX TZ parser and the POSIX TZ rules output by:

	tail -1 /usr/share/zoneinfo/**/*

for the selected time zones, including time zone ids and rules:

	12.6KB, 4KB zip, 3.8KB gz, 3.5KB xz/lz/bz2.

-- 
Take care. Thanks, Brian Inglis              Calgary, Alberta, Canada

La perfection est atteinte                   Perfection is achieved
non pas lorsqu'il n'y a plus rien à ajouter  not when there is no more to add
mais lorsqu'il n'y a plus rien à retirer     but when there is no more to cut
                                  -- Antoine de Saint-Exupéry



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: localtime on native Windows
  2024-02-13 18:25 localtime on native Windows Brian Inglis
@ 2024-02-18  2:14 ` Bruno Haible
  2024-02-18  5:32   ` Brian.Inglis
  0 siblings, 1 reply; 9+ messages in thread
From: Bruno Haible @ 2024-02-18  2:14 UTC (permalink / raw)
  To: bug-gnulib, Brian.Inglis

Brian Inglis wrote:
> I was looking around ... that required data can be shrunk to ~300KB using Brotli!?

Whereas the entire tzdata.zi (without comments, and with abbreviations) is
only around 100 KB. I'll definitely prefer the latter.

> Other options would be for some selection of generated data such as using 
> zonenow.tab, supporting only the current time onward

This is a simplification that one may do when working on an embedded system
or such. But not in gnulib. When the entire data file is 100 KB, it is not
a good compromise to give up correctness for dates in the past, in exchange
for further data size reduction.

> https://github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml

This file is interesting for its mapping table from Windows time zones.

Bruno





^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: localtime on native Windows
  2024-02-13  2:02     ` Paul Eggert
@ 2024-02-18  2:38       ` Bruno Haible
  2024-02-18  4:50         ` Paul Eggert
  0 siblings, 1 reply; 9+ messages in thread
From: Bruno Haible @ 2024-02-18  2:38 UTC (permalink / raw)
  To: bug-gnulib, Paul Eggert

Paul Eggert wrote:
> > The first question is how to include tzdata in gnulib.
> >    - AFAICS, the main data file (without comments) is tzdata.zi and is about
> >      100 KB large. It can be upgraded simply by copying the newest tzdata.zi
> >      from a newer tzdata distribution. Including such a file in gnulib would
> >      be OK (re copyright, number of files, total size), right?
> 
> No problem with copyright; the file is in the public domain.
> 
> >   - Whereas including all files from /usr/share/zoneinfo is probably not
> >     acceptable (> 1300 files, ca. 6 MB total size).
> 
> That can be automatically generated from tzdata.zi, by using zic, the 
> source code of which is also in the public domain.

Thanks for the confirmation.

My current plan, in order to cover the two goals
  - localtime_r on native Windows,
  - nstrftime, c_nstrftime, parse-datetime, which all take a timezone_t
    argument,
is to write four pieces of code:
  1) code that represents the data for a single time zone in memory,
     (based on glibc's tzfile.c),
  2) code that reads a binary time zone file, for use on Unix,
     (based on glibc's tzfile.c), returning it in the format 1).
  3) code that reads a tzdata.zi, for use on Windows,
     (based on glibc's zic.c and tzfile.c), returning it in the format 1).
  4) an implementation of localtime_r for a given time zone
     (based on glibc's tzfile.c:__tzfile_compute, tzset.c:__tz_convert, and
     localtime_r).

This should fulfil Gnulib's needs (code under LGPLv2+, small number
of source files, small number and size of data files to include).

Note that this will have to wait for a few weeks. I have more urgent work on
gettext to do first.

Note also: The above approach will need locking, in order to protect caches
(in 1) the data for a single time zone, in 3) the contents of tzdata.zi).

Bruno





^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: localtime on native Windows
  2024-02-18  2:38       ` Bruno Haible
@ 2024-02-18  4:50         ` Paul Eggert
  2024-02-18 14:38           ` Bruno Haible
  0 siblings, 1 reply; 9+ messages in thread
From: Paul Eggert @ 2024-02-18  4:50 UTC (permalink / raw)
  To: Bruno Haible; +Cc: bug-gnulib

On 2024-02-17 18:38, Bruno Haible wrote:
> Note also: The above approach will need locking, in order to protect caches
> (in 1) the data for a single time zone, in 3) the contents of tzdata.zi).

The idea I had was to go more in the NetBSD / TZDB direction. These 
implementations don't need either locks or lock-free caching algorithms, 
because it's the caller's responsibility to cache.


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: localtime on native Windows
  2024-02-18  2:14 ` Bruno Haible
@ 2024-02-18  5:32   ` Brian.Inglis
  0 siblings, 0 replies; 9+ messages in thread
From: Brian.Inglis @ 2024-02-18  5:32 UTC (permalink / raw)
  To: Bruno Haible, bug-gnulib

On 2024-02-17 19:14, Bruno Haible wrote:
> Brian Inglis wrote:
>> I was looking around ... that required data can be shrunk to ~300KB using Brotli!?
> 
> Whereas the entire tzdata.zi (without comments, and with abbreviations) is
> only around 100 KB. I'll definitely prefer the latter.
> 
>> Other options would be for some selection of generated data such as using
>> zonenow.tab, supporting only the current time onward
> 
> This is a simplification that one may do when working on an embedded system
> or such. But not in gnulib. When the entire data file is 100 KB, it is not
> a good compromise to give up correctness for dates in the past, in exchange
> for further data size reduction.
> 
>> https://github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml
> 
> This file is interesting for its mapping table from Windows time zones.

My point was that this data is already built into Windows, and accessible via 
its ICU API, so gnulib does not need to include, update, and compile tzdata to 
support Windows.

It just has to adapt its localization, as does Cygwin, which uses the Windows 
zones to look up tz ids, which it uses to load tzdata to provide POSIX 
interfaces, using otherwise mostly BSD code adapted from tzcode, required by 
newlib to allow commercial and other uses by RedHat and other embedded vendors.

-- 
Take care. Thanks, Brian Inglis              Calgary, Alberta, Canada

La perfection est atteinte                   Perfection is achieved
non pas lorsqu'il n'y a plus rien à ajouter  not when there is no more to add
mais lorsqu'il n'y a plus rien à retirer     but when there is no more to cut
                                 -- Antoine de Saint-Exupéry


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: localtime on native Windows
  2024-02-18  4:50         ` Paul Eggert
@ 2024-02-18 14:38           ` Bruno Haible
  2024-02-18 19:05             ` Paul Eggert
  0 siblings, 1 reply; 9+ messages in thread
From: Bruno Haible @ 2024-02-18 14:38 UTC (permalink / raw)
  To: Paul Eggert; +Cc: bug-gnulib

Paul Eggert wrote:
> > Note also: The above approach will need locking, in order to protect caches
> > (in 1) the data for a single time zone, in 3) the contents of tzdata.zi).
> 
> The idea I had was to go more in the NetBSD / TZDB direction. These 
> implementations don't need either locks or lock-free caching algorithms, 
> because it's the caller's responsibility to cache.

I don't understand what you mean:
  - How can the caller do the caching for something internal to the callee?
    I've never seen callbacks being used for that purpose. I've once seen
    a calling convention where the caller allocates memory for a cache and
    passes it to the callee; but that was for a case where the cache's
    lifetime ends when the callee returns.
  - The NetBSD code [1][2] does locking in the top-level functions (tzset,
    mktime, strftime). I guess that it is because NetBSD uses global variables
    (tzname, timezone, daylight, altzone).

For Gnulib, I intend to avoid global variables of this kind — they belong
in data allocated for each time zone — and therefore move the locking to
lower levels. In the frequent case that a timezone is used for the second
time or later, no locking will be needed at all; this will thus be faster
than what NetBSD does.

Bruno

[1] http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/lib/libc/time/localtime.c?rev=1.141&only_with_tag=MAIN
[2] http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/lib/libc/time/strftime.c?rev=1.55





^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: localtime on native Windows
  2024-02-18 14:38           ` Bruno Haible
@ 2024-02-18 19:05             ` Paul Eggert
  0 siblings, 0 replies; 9+ messages in thread
From: Paul Eggert @ 2024-02-18 19:05 UTC (permalink / raw)
  To: Bruno Haible; +Cc: bug-gnulib

On 2024-02-18 06:38, Bruno Haible wrote:
>    - How can the caller do the caching for something internal to the callee?

The caller calls tzalloc, gets a timezone object, and calls tzfree when 
it no longer needs that timezone object.

>   - The NetBSD code [1][2] does locking in the top-level functions (tzset,
>     mktime, strftime). I guess that it is because NetBSD uses global variables
>     (tzname, timezone, daylight, altzone).

This is for backward compatibility, and is required for POSIX 
compatibility. It has obvious problems with threads. New code should not 
use these vestigial interfaces. They should use localtime_z and 
mktime_z. (There should be no need for strftime_z.)

https://data.iana.org/time-zones/theory.html#vestigial

> For Gnulib, I intend to avoid global variables of this kind — they belong
> in data allocated for each time zone — and therefore move the locking to
> lower levels. In the frequent case that a timezone is used for the second
> time or later, no locking will be needed at all; this will thus be faster
> than what NetBSD does.

You're right to avoid the global variables, but I don't see why tzalloc 
needs to lock, even in MS-Windows. Perhaps there could be an "I'll do 
the caching" layer above tzalloc, but the lower-level code need not lock 
at all.


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2024-02-18 19:06 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-02-13 18:25 localtime on native Windows Brian Inglis
2024-02-18  2:14 ` Bruno Haible
2024-02-18  5:32   ` Brian.Inglis
  -- strict thread matches above, loose matches on Subject: below --
2024-02-09 17:43 MT-unsafe time modules Bruno Haible
2024-02-10 11:10 ` Bruno Haible
2024-02-11 12:46   ` localtime on native Windows Bruno Haible
2024-02-13  2:02     ` Paul Eggert
2024-02-18  2:38       ` Bruno Haible
2024-02-18  4:50         ` Paul Eggert
2024-02-18 14:38           ` Bruno Haible
2024-02-18 19:05             ` Paul Eggert

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).