Отличается ли c mktime для Windows и GNU/Linux?

следующий код:

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

    static const char * wday_abb_names[] =
    {
        "Mon",
        "Tue",
        "Wed",
        "Thu",
        "Fri",
        "Sat",
        "Sun",
    };

    static void mb_setenv(const char *name, const char *value)
    {
    #if !(defined _WIN32) || defined HAVE_SETENV
        setenv(name, value, 1);
    #else
        int len = strlen(name)+1+strlen(value)+1;
        char *str = malloc(len);
        sprintf(str, "%s=%s", name, value);
        putenv(str);
    #endif
    }

    static void mb_unsetenv(const char *name)
    {
    #if !(defined _WIN32) || defined HAVE_SETENV
        unsetenv(name);
    #else
        int len = strlen(name)+2;
        char *str = malloc(len);
        sprintf(str, "%s=", name);
        putenv(str);
                    free(str);
    #endif
    }

    time_t mb_timegm(struct tm *tm)
    {
        time_t ret;
        char *tz;

        tz = getenv("TZ");
        mb_setenv("TZ", "");
        tzset();
        ret = mktime(tm);
        if (tz)
        {
            mb_setenv("TZ", tz);
        }
        else
        {
            mb_unsetenv("TZ");
        }
        tzset();
        return ret;
    }

    time_t get_test_time()
    {
        struct tm msg_time;
        msg_time.tm_isdst = 0;
        msg_time.tm_wday = 4;
        msg_time.tm_mon = 5;
        msg_time.tm_mday = 16;
        msg_time.tm_hour = 4;
        msg_time.tm_min = 53;
        msg_time.tm_sec = 0;
        msg_time.tm_year = 111; //2011 - 1900
        time_t retval = mb_timegm(&msg_time);
        printf("final msg_time = %ld\n", retval);
        return retval;
    }

    void print_time(const char *msg, struct tm *t)
      {
        printf("%s %s, %02d.%02d.%2d %2d:%02d\n", msg,
               wday_abb_names[t->tm_wday],  t->tm_mday, t->tm_mon, t->tm_year,
               t->tm_hour, t->tm_min);
      }

    int main()
    {
        printf( "=== ENVIRON ===\n");
        printf("TZ = %s\n", getenv("TZ"));
        time_t now;
        struct tm l, g;
        time(&now);
        l = *localtime(&now);
        g = *gmtime(&now);

        print_time("Local time :", &l);
        print_time("utc        :", &g);
        printf("=== END ENVIRON ===\n\n");

        time_t tt = get_test_time();
        printf("fix test (16.6.2011 04:53) --> %s\n", ctime(&tt));

        printf("done.\n");
        return 0;
    }

работая в GNU/Linux, он производит:

=== ENVIRON ===
TZ = (null)
Local time : Sat, 24.05.111 14:20
utc        : Sat, 24.05.111 12:20
=== END ENVIRON ===

final msg_time = 1308199980
fix test (16.6.2011 04:53) --> Thu Jun 16 06:53:00 2011

done.

работает на Win7, выдает:

=== ENVIRON ===
TZ = (null)
Local time : Sat, 24.05.111 14:25
utc        : Sat, 24.05.111 12:25
=== END ENVIRON ===

final msg_time = 1308196380
fix test (16.6.2011 04:53) --> Thu Jun 16 05:53:00 2011

done.

Обе системы имеют часовой пояс UTC + 1, включая DST (что делает UTC + 2 в действии), и обе системы вообще не имеют проблем со временем, за исключением отображаемой разницы.

Как видите, в «final msg_time» не хватает ровно 3600 секунд, так что в ctime это не проблема.

Может ли кто-нибудь объяснить мне, почему mktime ведет себя по-разному в GNU/Linux и Windows, или как это исправить?

Изменить:
Обе системы (после вызова tzset()) сообщают tzname[0] = CET, tzname[1] = CEST, дневной свет = 1, часовой пояс = -3600.


person Nils    schedule 24.06.2011    source источник
comment
ctime устанавливается после вызова переменной tzname. Вы можете проверить, равны ли они впоследствии в обеих системах.   -  person flolo    schedule 24.06.2011
comment
flolo: я добавил значение tzname. Однако - time_t msg_time отличается, поэтому я сомневаюсь, что проблема в ctime.   -  person Nils    schedule 24.06.2011


Ответы (2)


Мой mb_timegm был основан на коде, указанном в man 3 timegm, и в нем было указано, что "set the TZ environment variable to UTC" для этого вызывается setenv("TZ", "");.

Однако - это не работает на окнах.

Вместо этого использование setenv("TZ", "UTC"); (или, в приведенном выше случае, mb_setenv) устраняет проблему.

person Nils    schedule 24.06.2011

Я предполагаю, основываясь на предоставленной вами информации, где действует летнее время, что вам нужно будет установить msg_time.tm_isdst в get_test_time() на значение 1, а не 0. Это может быть проблемой учета пропущенного часа. Либо так, либо вы можете установить его на -1 и позволить системе попытаться выяснить, находитесь ли вы в летнем времени или нет для данного входного значения.

person Jason    schedule 24.06.2011
comment
Джейсон, нет, модификация tm_isdst изменяет результат, но не делает результат gnu/linux таким же, как у окон. На самом деле get_test_time должен возвращать time_t в формате utc, поэтому я использую mb_timegm (замену timegm) вместо timelocal. - person Nils; 25.06.2011