Насколько безопасно предполагать, что 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 должен обновляться при изменении часового пояса.