/** Show that mktime() may change tzname.

    To try:
      $ cc test_mktime_changes_tzname.c && ./a.out --verbose

    The command exits with non-zero exit code if the test fails.
*/
/* [CX] setenv(), tzset(), tzname[], [XSI] daylight, timezone */
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 700
#endif
/* tm.tm_gmtoff, tm.tm_zone */
#ifndef _BSD_SOURCE
#define _BSD_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h> /* asctime, ctime, gmtime, localtime, mktime */

#define QUOTE(expr) #expr
#define STR(macro) QUOTE(macro)
#define ASSERT(expr) assert_true(__FILE__, STR(__LINE__), QUOTE(expr), (expr))

static void
report_error_and_exit(const char* msg) {
  perror(msg);
  exit(EXIT_FAILURE);
}

static int nfail; /* number of failed tests */

static int
assert_true(const char* filename, const char* line, const char* str_expr, int expr) {
  if (!expr) {
    fprintf(stderr, "%s:%s: FAIL (%s) is false\n", filename, line, str_expr);
    ++nfail;
  }
  return !!expr;
}

int main(int argc, char* argv[]) {
  const int month = 7; /* July */
  int years[] = {2010, 2015};
  const char* tznames[][2] = {{"MSK", "MSD"}, {"MSK", "MSK"}};
  /* local time - utc time in seconds */
  long utc_offsets[] = {14400, 10800}; /* YYYY-07-01 */
  /* could there be a DST transition in the corresponding year? */
  int daylights[] = {1, 0};
  /* is DST in effect for the corresponding time? */
  int isdsts[] = {1, 0}; /* YYYY-07-01 */
  const size_t n = sizeof(years) / sizeof(*years);
  size_t i, j;
  int verbose = (argc > 1) && !strcmp(argv[argc-1], "--verbose");

  /* set the local timezone that corresponds to the data above */
  if (setenv("TZ", "Europe/Moscow", 1) < 0)
    report_error_and_exit("setenv");
  tzset();
  /*NOTE: tzname[1] does not correspond to the current time on some systems */

  for (i = 0; i < n; ++i) {
    struct tm tt = {0}; /* time tuple */
    char timebuf[20] = {0};
    tt.tm_year = years[i] - 1900;
    tt.tm_mon = month - 1;
    tt.tm_mday = 1;
    tt.tm_isdst = -1; /* let mktime() figure out the value */

    if (mktime(&tt) == (time_t) -1) /* don't need tm_wday == -1 sentinel */
      report_error_and_exit("mktime"); /* [CX] errno is set */

    if (verbose)
      fputs(asctime(&tt), stdout);

    ASSERT(tt.tm_isdst == isdsts[i]);
    ASSERT(!strcmp(tzname[tt.tm_isdst], tznames[i][tt.tm_isdst]));
    ASSERT(!strcmp(tt.tm_zone, tznames[i][tt.tm_isdst]));
    if (daylight != daylights[i]) {
      fprintf(stderr, __FILE__ ":" STR(__LINE__) ": "
	      "FAIL daylight: expected %d, got %d on year=%d\n",
	      daylights[i], daylight, years[i]);
      ++nfail;
    }
    for (j = 0; j < 2; ++j) /*XXX use both tzname values regardless of daylight */
      ASSERT(!strcmp(tzname[j], tznames[i][j]));

    {
      long utc_offset = -timezone + tt.tm_isdst * 3600;
      if (utc_offset != utc_offsets[i]) {
	fprintf(stderr, __FILE__ ":" STR(__LINE__) ": "
		"FAIL utc_offset: expected %ld, got %ld on %d-%02d-%02d\n",
		utc_offsets[i], utc_offset, years[i], month, 1);
	++nfail;
      }
      ASSERT(tt.tm_gmtoff == utc_offsets[i]);
    }
    if (strftime(timebuf, sizeof(timebuf), "%Z", &tt) == 0) {
      fprintf(stderr, "error: strftime returned 0");
      exit(EXIT_FAILURE);
    }
    ASSERT(!strcmp(timebuf, tznames[i][tt.tm_isdst]));

  }
  return !nfail ? EXIT_SUCCESS : EXIT_FAILURE;
}
