Задать вопрос
PavelK
@PavelK

Как быстро округлить timestamp до секунд, минут, часов, дней и т.д.?

Было: 1679644808 (24.03.23 8:00:08), округляем до месяца (01.03.23 0:00:00). Если с округлением времени всё относительно легко, трудности начинаются датой, т.к. там нужно знать високосные года. в контексте C++11.
  • Вопрос задан
  • 789 просмотров
Подписаться 2 Простой 11 комментариев
Решения вопроса 2
jcmvbkbc
@jcmvbkbc
"I'm here to consult you" © Dogbert
Я думаю, что пара gmtime + timegm -- это простое, понятное, сравнительно портабельное и довольно быстрое решение:
#include <time.h>

enum time_floor_level {
    YEAR, MONTH, DAY, HOUR, MINUTE,
};

time_t time_floor(time_t t, enum time_floor_level level)
{
    struct tm tm;

    gmtime_r(&t, &tm);

    switch (level) {
    case YEAR:
        tm.tm_mon = 0;
    case MONTH:
        tm.tm_mday = 1;
    case DAY:
        tm.tm_hour = 0;
    case HOUR:
        tm.tm_min = 0;
    case MINUTE:
        tm.tm_sec = 0;
    }
    return timegm(&tm);
}
Ответ написан
Комментировать
@svistkovr
для дат timestamp>0 довольно простая математика.
#include <stdio.h>
#include <time.h>
int main(){

    time_t all_seconds = time(NULL);
    printf("временная метка - количество секунд с 1970-01-01 00:00:00 +0000 в четверг : %lu\n",all_seconds);

    double all_minutes = all_seconds/60.0;
    double all_hours = all_minutes/60.0;
    double all_days = all_hours/24.0;
    double all_years = all_days/(365.0+0.2425); //с учётом високосного
    //timestamp  начинается с 1970-01-01 00:00:00 +0000 в четверг
    long years_l = (int)all_years;
    long year = years_l + 1970;
    printf("год : %ld ",year);
    bool is_leap_year = !((year % 4 != 0) || ((year % 100 == 0) && (year % 400 != 0)));//високосный каждый 4 но не кратен 100

    if(is_leap_year){
        printf("високосный\n");
    }else{
        printf("не високосный\n");
    }

    double days_from_year_start = all_years - years_l;
    int day_in_month[12]={31,28,31,30,31,30,31,31,30,31,30,31};
    int month=0;
    double day_d=days_from_year_start*(365.0+(is_leap_year?1:0));
    int day=(int)day_d;
    int day_in_month_t;
    do{
        day_in_month_t=day_in_month[month];
        day-=day_in_month_t;
        if(is_leap_year){
            if(month==1){
                day--;
            }
        }
        month++;

    }
    while((month<12) && (day>day_in_month_t));
    const char* month_name[12]={"январь","февраль","март","апрель","май","июль","июнь","август","сентябрь","октябрь","ноябрь","декабрь"};
    printf("месяц : %s\n",month_name[month]);
    day++;// сдвиг нулевого дня 0 -> 1
    printf("день : %d \n",day);

    int day_of_week = ((((int)all_days)%7)+3)%7;//сдвиг для 0 дня. 1 день 1970 был в чеверг
    const char* week[7]={"пн","вт","ср","чт","пт","сб","вс"};
    printf("день недели : %s \n",week[day_of_week]);

    int hour = ((int)all_hours)%24;
    printf("час для GMT+0 : %d \n",hour);

    int minutes = ((int)all_minutes)%60;
    printf("минуты : %d \n",minutes);

    int seconds = ((int)all_seconds)%60;
    printf("секунды : %d \n",seconds);
return 0;
}

для остальных дат надо делать пересчёт с учётом более длинных циклов и нулевого года.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
@dima20155
you don't choose c++. It chooses you
Можете использовать duration_cast до дней.В доке как раз написано, что выполняется округление.Затем обратно преобразовать в time_point, если это необходимо.
Ответ написан
@res2001
Developer, ex-admin
Думаю, для округления вплоть до дней можно просто отбрасывать остаток от деления на соответствующую константу. Константы вычислить заранее для 1 дня, 1 часа и 1 минуты.
Округление до года и месяца - по предложенной jcmvbkbc схеме.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы