C ++: пол unix отметка времени до UT C месяц - PullRequest
1 голос
/ 10 июля 2020

Предположим, у меня есть unix timetsamp

int timestamp=1594386202;

Я хочу написать функцию

int floor_to_month_utc(int timestamp);

, которая выравнивает временную метку до начала соответствующего месяца (в UT C). В этом примере функция должна удовлетворять требованиям 1593561600 == floor_to_month_utc(1594386202), потому что 1594386202 соответствует 2020-07-10T13:03:22+00:00 UT C времени, а 1593561600 - началу соответствующего месяца 2020-07-01T00:00:00+00:00.

Как я могу это сделать в C ++? (На самом деле мне также нужно ceil от текущей отметки времени до отметки времени следующего месяца, но я думаю, что могу написать это сам, если вы дадите мне подсказку, как реализовать функцию пола)

Ответы [ 2 ]

1 голос
/ 10 июля 2020

Как Алан Бертлс указывает в комментариях, это легко сделать на C ++ 20:

#include <chrono>

int
floor_to_month_utc(int timestamp)
{
    using namespace std::chrono;

    sys_seconds tp{seconds{timestamp}};
    year_month_day ymd = floor<days>(tp);
    sys_seconds result = sys_days{ymd.year()/ymd.month()/1};
    return result.time_since_epoch().count();
}

Пояснение:

Первый шаг - поместить ввод в систему типов <chrono>. Подразумевается, что timestamp - это количество секунд с 1970-01-01 00:00:00 UT C (исключая дополнительные секунды). Это также известно как Unix Time .

std::chrono::sys_seconds - это тип C ++ 20, который соответствует этой семантике. Итак, sys_seconds tp{seconds{timestamp}}; сначала преобразует timestamp в длительность seconds, а затем в time_point sys_seconds.

Далее нужно понимать, что это календарное вычисление , а не хронологическое вычисление. Используемый календарь - это гражданский календарь, который смоделирован на C ++ 20 <chrono>. Итак, следующий шаг - преобразовать time_point tp в день в гражданском календаре:

year_month_day ymd = floor<days>(tp);

floor<days> просто усекает точность до tp до days (делая это количество дней с 1970-01-01 00:00:00 UT C).

ymd - это структура данных {year, month, day}, преобразованная из days -precision time_point и имеет тип std::chrono::year_month_day. И ymd, и floor<days>(tp) содержат точно такую ​​же информацию. Но информация хранится в двух разных структурах данных: {year, month, day} vs {count of days}.

Следующий шаг - найти первый день года и месяца, на которые ссылается ymd. Это просто выражение ymd.year()/ymd.month()/1.

Его можно преобразовать обратно в структуру данных {count of days} с помощью:

sys_days{ymd.year()/ymd.month()/1}

std::chrono::sys_days - это просто псевдоним типа для типа выражение floor<days>(tp), которое имеет тип:

time_point<system_clock, days>

Затем структура данных sys_days неявно преобразуется в seconds -точность, и целая сумма извлекается и возвращается из этой структуры данных.

A превью библиотеки C ++ 20 <chrono> находится здесь и может использоваться с C ++ 11/14/17. Чтобы перенести вышеуказанную функцию в эту библиотеку предварительного просмотра, просто добавьте "date/date.h" и using namespace date;. "date/date.h" является библиотекой только для заголовков и поэтому не требует установки.

Это можно сделать следующим образом:

std::cout << floor_to_month_utc(1594386202) << '\n';

, что выводит:

1593561600

Поучительно сравнить приведенный выше код с этим ответом , который дает другой результат:

1593626076

Причина, по которой результат отличается, восходит к различию между календарные вычислений и хронологические вычислений. Календарные вычисления выполняются относительно некоторого календаря (гражданского, китайского, юлианского и т. Д.). В то время как хронологические вычисления оперируют только фиксированными (регулярными) единицами времени. Календарные месяцы и годы не имеют регулярной длины.

Библиотека C ++ 20 <chrono> может выполнять хронологические вычисления с months и years. Иногда это правильный ответ, когда речь идет о физических или биологических процессах, которые растягиваются на месяцы или годы. Таким процессам наплевать на календари, созданные руками человека. Таким образом, в этом случае имеет смысл иметь дело со средней продолжительностью 1094 * месяцев и лет.

Этот оператор:

floor<months>(sys_seconds{seconds{timestamp}})

просто делит время с момента Unix Время эпоха в обычные, четные месяцы, причем каждый месяц длится ровно 2 629 746 секунд (средняя продолжительность гражданского месяца). Итак, этот ответ верен, если кто-то желает хронологического вычисления с гипотетическими единообразными месяцами (совершенно другой календарь, если хотите).

0 голосов
/ 10 июля 2020

РЕДАКТИРОВАТЬ: Это неправильное хронологическое вычисление.


Как было предложено в комментариях, используя заголовок `date.h` Говарда Хиннанта, следующее, похоже, делает то, что я хочу (используя int64_t, чтобы избежать ошибки 2038):
#include"date.h"

int64_t floor_to_month_utc(int64_t timestamp){
    using namespace std::chrono;
    const auto dp = date::floor<date::months>(system_clock::time_point(seconds(timestamp)));
    return duration_cast<seconds>(dp.time_since_epoch()).count();
}
...