public inbox for libc-help@sourceware.org
 help / color / mirror / Atom feed
From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
To: noloader@gmail.com,
	"libc-help@sourceware.org" <libc-help@sourceware.org>
Subject: Re: Converting time from timezone string to another timezone time
Date: Tue, 2 Feb 2021 15:23:25 -0300	[thread overview]
Message-ID: <4e309a02-0398-4679-f74b-4d45a665ae6a@linaro.org> (raw)
In-Reply-To: <CAH8yC8kRTbiSNdEQSYcHAVRzMQPpH+tZJ-x_hGpe0VwgnQLvmA@mail.gmail.com>



On 29/01/2021 05:11, Jeffrey Walton via Libc-help wrote:
> Hi Everyone,
> 
> I'm having a heck of a time converting a time from a timezone string
> to another timezone time. The best I seem to be able to do is adjust
> for DST using strptime, mktime and localtime. For example:
> 
>   * given: '15 Jan 2021 01:24:55 -0800 (PST)'
>   * expected: '15 Jan 2021 04:24:55 -0500 (EST)' (or similar, as long
> as it includes 04:24:55)
>   * my best result: '15 Jan 00:24:55 2021' (seems to be a DST adj of PST time)
> 
> I've been through the manual at
> https://www.gnu.org/software/libc/manual/html_node/Calendar-Time.html
> but I can't seem to find the sequence of calls to perform the actions.
> I've also been through the libc FAQ at
> https://sourceware.org/glibc/wiki/FAQ (there's one topic on
> timezones).
> 
> So I have two questions, but I only need one answered (whichever is
> easier to answer):
> 
> 1. Where is the discussion/faq on how to convert from timezone string
> to another timezone time?
> 
> 2. What is wrong with this code (error checking omitted)?
> 
>     const char pst_timestring[] = '15 Jan 2021 01:24:55 -0800 (PST)';
>     struct tm gmail_tm[1];
>     strptime(pst_timestring, "%d %b %Y %T %z", gmail_tm);
> 
>     // Already set, but just in case. It is -0500 (EST)
>     char my_timezone[] = "TZ=America/New_York";
>     putenv(my_timezone);
> 
>     time_t utc_time;
>     utc_time = mktime(gmail_tm);
> 
>     struct tm local_tm[1];
>     local_tm = localtime(&utc_time);
> 
>     // This prints a time that was adjusted by 1 hour, not 3 hours
> (expected from -0800 to -0500)
>     printf(..., asctime(local_tm));
>     // Output is similar to '15 Jan 00:24:55 2021'
> 
> Somewhere there is a non-obvious problem with the code stitched
> together from the man pages.
> 

I think what you might like is something like:

---
$ cat test.c
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

static void
set_tz (const char *tz)
{ 
  char buf[128];
  snprintf (buf, sizeof (buf), "TZ=%s", tz);
  putenv (buf);
  tzset ();
}

static struct tm
convert_time (const char *origtm, const char *dsttm, const char *timestr)
{ 
  struct tm tm, ttm;

  set_tz (origtm);
  strptime (timestr, "%d %b %Y %T", &tm);

  time_t t = mktime (&tm);

  set_tz (dsttm);
  localtime_r (&t, &ttm);
  return ttm;
}

int main (int argc, char *argv[])
{ 
  const char timestr[] = "15 Jan 2021 01:24:55";
  const char origtm[] = "Europe/Berlin";
  const char dsttm[] = "America/New_York";

  struct tm tm = convert_time (origtm, dsttm, timestr);

  char buffer[256];
  strftime (buffer, sizeof (buffer), "%d %b %Y %T", &tm);

  printf ("%s: %s\n%s: %s\n",
          origtm, timestr,
          dsttm, buffer);

  return 0;
}
$ gcc -D_GNU_SOURCE test.c && ./a.out
Europe/Berlin: 15 Jan 2021 01:24:55
America/New_York: 14 Jan 2021 23:24:55
---

AFAIK there is not easy way to accomplish it without setting the current TZ,
create a struct tm, getting a canonical UNIX time_t from it, reset TZ, and
convert. Also keep in mind this has some corner cases regarding thread safety,
and I am not sure if plays well on every input and timezones.

The coreutils date command uses a different strategy, where it uses the
gnulib parse-datetime module [1].  It does something as:

  char const *tzstring = getenv ("TZ");
  timezone_t tz = tzalloc (tzstring);
  [...]
  parse_datetime2 (&when, datestr, NULL, parse_datetime_flags, tz, tzstring);
  show_date (format, when, tz);

And parse_datetime2 does all the magic internally.  It also seems to be
thread-safe and do not require messing with TZ environment variable.

[1] https://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob;f=lib/parse-datetime.y;h=b8a832fcd8f70a0df7b6104e1bacc4e0495bf01f;hb=HEAD

      parent reply	other threads:[~2021-02-02 18:23 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-01-29  8:11 Jeffrey Walton
2021-02-02 14:33 ` Jeffrey Walton
2021-02-02 18:23 ` Adhemerval Zanella [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4e309a02-0398-4679-f74b-4d45a665ae6a@linaro.org \
    --to=adhemerval.zanella@linaro.org \
    --cc=libc-help@sourceware.org \
    --cc=noloader@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).