Alain Guibert <[EMAIL PROTECTED]> writes:

> (1) Libc 5.4.33 own mktime() produces wrong by some minutes results for
> all summer dates when tm_isdst is forced to false 0. Wget's
> mktime_from_utc() forces tm_isdst=0 at a stage, and produces wrong by
> some minutes result only for one hour, beginning at DST transition plus
> one hour.
>
> (2) Replacing wget's mktime_from_utc() by a TZ=GMT0 mktime() scheme.
> Solves problem one, is faster, may seem cleaner (no discontinuities to
> support), but introduces portability issues. Still at discussion.
>
> (3) When wget's cmpt.c:mktime() is forced to override platform's
> mktime(), then mktime_from_utc() produces wrong results for two hours,
> beginning at to-DST transition plus two hours. Wrong by an hour less, or
> totally wrong. Even on platforms not affected by problem one.

Am I completely off the mark, or can mktime_from_utc (or in fact
timegm) be replaced by something as simple as the following?  Or is it
too good to be true?

#ifndef HAVE_TIMEGM
/* timegm is a GNU extension, but lately also available on *BSD and
   possibly elsewhere. */

/* Inverse of gmtime: converts struct tm to time_t, assuming the data
   in tm is UTC rather than local timezone.  This implementation
   returns the number of seconds since 1970-01-01, converted to
   time_t.  */

#define IS_LEAP(year)                                                   \
    ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))

time_t
timegm (struct tm *t)
{
  static const unsigned short int month_to_days[][13] = {
    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
  };
  unsigned long secs;
  int days;

  /* Only handles years between 1970 and 2099. */
  if (t->tm_year < 70 || t->tm_year > 129)
    return (time_t) -1;

  days = 365 * (t->tm_year - 70);
  /* Take into account leap years between 1970 and t->tm_year-1; all
     years divisible by four between 1968 and 2100 should be leap.  */
  days += (t->tm_year - 1 - 68) / 4;
  if (t->tm_mon < 0 || t->tm_mon >= 12)
    return (time_t) -1;
  days += month_to_days[IS_LEAP (1900 + t->tm_year)][t->tm_mon];
  days += t->tm_mday - 1;

  secs = days * 86400 + t->tm_hour * 3600 + t->tm_min * 60 + t->tm_sec;
  return (time_t) secs;
}
#endif /* HAVE_TIMEGM */

Reply via email to