Skip to content

Commit

Permalink
For strftime %z, use tm_gmtoff if available
Browse files Browse the repository at this point in the history
Problem and draft patch reported by Dag-Erling Smørgrav in:
https://mm.icann.org/pipermail/tz/2024-January/033488.html
* NEWS: Mention this.
* localtime.c (EXTERN_TIMEOFF): Default to static.
(timeoff): Use EXTERN_TIMEOFF to declare; this is easier to read.
* private.h (EXTERN_TIMEOFF): New macro.
(timeoff): Define to tz_private_timeoff if it’s not public but
strftime needs it anyway.  Declare if we define it as extern.
* strftime.c (_fmt): If TM_GMTOFF is available, use timeoff
rather than mktime to determine %z.  Do not worry about UNINIT_TRAP
since we are now using a more-generous reading of POSIX.
  • Loading branch information
eggert committed Jan 13, 2024
1 parent b4ec327 commit a707253
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 7 deletions.
3 changes: 3 additions & 0 deletions NEWS
Expand Up @@ -34,6 +34,9 @@ Unreleased, experimental changes
for some timestamps in November 2422, November 2822, etc. in
America/Ciudad_Juarez. (Problem reported by Gilmore Davidson.)

strftime %z now uses tm_gmtoff if available. (Problem and draft
patch reported by Dag-Erling Smørgrav.)

Changes to build procedure

The leap-seconds.list file is now copied from the IERS instead of
Expand Down
9 changes: 6 additions & 3 deletions localtime.c
Expand Up @@ -2292,15 +2292,18 @@ timelocal(struct tm *tmp)
tmp->tm_isdst = -1; /* in case it wasn't initialized */
return mktime(tmp);
}
#else
#endif

#ifndef EXTERN_TIMEOFF
# ifndef timeoff
# define timeoff my_timeoff /* Don't collide with OpenBSD 7.4 <time.h>. */
# endif
static
# define EXTERN_TIMEOFF static
#endif

/* This function is obsolescent and may disapper in future releases.
Callers can instead use mktime_z with a fixed-offset zone. */
time_t
EXTERN_TIMEOFF time_t
timeoff(struct tm *tmp, long offset)
{
if (tmp)
Expand Down
15 changes: 14 additions & 1 deletion private.h
Expand Up @@ -756,7 +756,7 @@ struct tm *offtime(time_t const *, long);
time_t timelocal(struct tm *);
# endif
# if TZ_TIME_T || !defined timeoff
time_t timeoff(struct tm *, long);
# define EXTERN_TIMEOFF
# endif
# if TZ_TIME_T || !defined time2posix
time_t time2posix(time_t);
Expand Down Expand Up @@ -899,6 +899,19 @@ static_assert(! TYPE_SIGNED(time_t) || ! SIGNED_PADDING_CHECK_NEEDED
# define UNINIT_TRAP 0
#endif

/* localtime.c sometimes needs access to timeoff if it is not already public.
tz_private_timeoff should be used only by localtime.c. */
#if (!defined EXTERN_TIMEOFF \
&& defined TM_GMTOFF && (200809 < _POSIX_VERSION || ! UNINIT_TRAP))
# ifndef timeoff
# define timeoff tz_private_timeoff
# endif
# define EXTERN_TIMEOFF
#endif
#ifdef EXTERN_TIMEOFF
time_t timeoff(struct tm *, long);
#endif

#ifdef DEBUG
# undef unreachable
# define unreachable() abort()
Expand Down
7 changes: 4 additions & 3 deletions strftime.c
Expand Up @@ -327,11 +327,12 @@ _fmt(const char *format, const struct tm *t, char *pt,
tm.tm_mday = t->tm_mday;
tm.tm_mon = t->tm_mon;
tm.tm_year = t->tm_year;
#ifdef TM_GMTOFF
mkt = timeoff(&tm, t->TM_GMTOFF);
#else
tm.tm_isdst = t->tm_isdst;
#if defined TM_GMTOFF && ! UNINIT_TRAP
tm.TM_GMTOFF = t->TM_GMTOFF;
#endif
mkt = mktime(&tm);
#endif
/* If mktime fails, %s expands to the
value of (time_t) -1 as a failure
marker; this is better in practice
Expand Down

1 comment on commit a707253

@dag-erling
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's %s, not %z. The commit message can't be changed but the NEWS entry should be corrected.

Please sign in to comment.