Насколько безопасно предполагать, что time_t в секундах?

Я делаю много вычислений со временем, строю объекты времени относительно других объектов времени, добавляя секунды. Код должен работать на встроенных устройствах и серверах. Большинство документов говорят о time_t что это какой-то арифметический тип, хранящий обычно время с эпохи. Насколько безопасно предполагать, что time_t хранить количество секунд, так как что-то? Если мы можем предположить, что мы можем просто использовать сложение и вычитание, а не localtime, mktime а также difftime,

До сих пор я решил проблему с помощью constexpr bool time_tUsesSeconds, обозначая, можно ли считать, что time_t использует секунды. Если это непереносимо предположить time_t в секундах, есть ли способ автоматически инициализировать эту константу?

time_t timeByAddingSeconds(time_t theTime, int timeIntervalSeconds) {
    if (Time_tUsesSeconds){
        return theTime + timeIntervalSeconds;
    } else {
        tm timeComponents = *localtime(&theTime);
        timeComponents.tm_sec += timeIntervalSeconds;
        return mktime(&timeComponents);
    }
}

5 ответов

Решение

Факт, что это в секундах, указан в спецификации POSIX, поэтому, если вы программируете для POSIX-совместимых сред, вы можете положиться на это.

Стандарт C++ также гласит, что time_t должен быть арифметическим типом.

В любом случае, система синхронизации Unix (вторая после эпохи) будет переполнена в 2038 году. Таким образом, весьма вероятно, что до этой даты реализации C++ переключатся на другие не-int типы данных (либо 64-битные int, либо более сложные типы данных).). В любом случае, переход на 64b int нарушит двоичную совместимость с предыдущим кодом (поскольку для этого требуются большие переменные), и все должно быть перекомпилировано. Использование непрозрачных дескрипторов 32b не нарушит бинарную совместимость, вы можете изменить базовую библиотеку, и все по-прежнему работает, но time_t не будет больше времени в секундах, это будет индекс для массива раз в секундах. По этой причине предлагается использовать упомянутые вами функции для манипулирования значениями time_t и не принимать никаких time_t,

Если доступен C++11, используйте std::chrono::system_clock с to_time_t а также from_time_t преобразовать в / из std::chrono::time_point, и используйте арифметические операторы chrono.

Если в ваших расчетах используется григорианский календарь, вы можете использовать библиотеку https://github.com/HowardHinnant/date или новые возможности календаря C++20 в chrono (по сути, у них одинаковый API).

Вместо того, чтобы определить, time_t в секундах, так как time_t является арифметическим типом, вы можете вместо этого вычислить time_t значение, которое представляет одну секунду, и работать с этим. Этот ответ, который я написал ранее, объясняет метод и имеет некоторые предостережения, вот пример кода (bad_time() класс пользовательских исключений, здесь):

time_t get_sec_diff() {
    std::tm datum_day;
    datum_day.tm_sec = 0;
    datum_day.tm_min = 0;
    datum_day.tm_hour = 12;
    datum_day.tm_mday = 2;
    datum_day.tm_mon = 0;
    datum_day.tm_year = 30;
    datum_day.tm_isdst = -1;

    const time_t datum_time = mktime(&datum_day);
    if ( datum_time == -1 ) {
        throw bad_time();
    }

    datum_day.tm_sec += 1;
    const time_t next_sec_time = mktime(&datum_day);
    if ( next_sec_time == -1 ) {
        throw bad_time();
    }

    return (next_sec_time - datum_time);
}

Вы можете вызвать функцию один раз и сохранить значение в const, а затем просто использовать его всякий раз, когда вам нужно time_t второй. Я не думаю, что это будет работать в constexpr хоть.

В стандарте C или в стандарте C++ нет требований к единицам, которые time_t представляет собой. Для работы с секундами переносимо нужно использовать struct tm, Вы можете конвертировать между time_t а также struct tm с mktime а также localtime,

Мои два цента: в Windows время измеряется в секундах, но время, необходимое для перехода к следующей секунде, обычно составляет 18*54,925 мс, а иногда 19*54,925. Причина этого объясняется в этом посте.

(Отвечая на собственный вопрос)

Один ответ предполагает, что пока кто-то использует posix, time_t в секундах и арифметика на time_t должно сработать.

Второй ответ вычисляет time_t в секунду и использует его как фактор при выполнении арифметики. Но есть еще некоторые предположения о time_t сделал.

В конце концов я решил, что переносимость важнее, я не хочу, чтобы мой код молчал на каком-то встроенном устройстве. Поэтому я использовал третий способ. Это включает в себя сохранение целого числа, обозначающего время с момента запуска программы. Т.е. я определяю

 const static time_t time0 = time(nullptr);
 static tm time0Components = *localtime(&time0);

Все значения времени, используемые в программе, являются целыми числами, обозначающими разницу во времени с time0, Идти от time_t до этой дельты секунды, я использую difftime, Вернуться к time_tЯ использую что-то вроде этого:

time_t getTime_t(int timeDeltaSeconds) {
    tm components = time0Components;
    components.tm_sec += timeDeltaSeconds;
    return mktime(&components);
}

Такой подход позволяет делать такие операции, как +,- дешево, но возвращаюсь time_t дорогой. Обратите внимание, что значения дельта времени имеют смысл только для текущего запуска программы. Также обратите внимание, что time0Components должен обновляться при изменении часового пояса.

Другие вопросы по тегам