Hourly task is not hourly

Hey, Im trying to run a certain function periodically every hour but in reality it gets offset by 1.5minute every hour, which means that i lose 1 hour every 40 hours and the calculations arent every 60minutes but every 61.5 minutes. Im using application task to do this

#define ONE_HOUR_UPDATE_INTERVAL (60 * 60 * 1000)//60min

void application_task(){
    ...//here i run all the calculations, save them/send them by radio
    twr_scheduler_plan_current_relative(ONE_HOUR_UPDATE_INTERVAL);
}

Could you help me how to solve this

The scheduler is not meant as a precise interval. Especially in the longer periods like 1 hour.

Use RTC module that holds exact and more precise time.
https://sdk.hardwario.com/group__twr__rtc.html

And docs

(note that you need to subtract/add value 1900 to the save year as examples in green tips boxes in the link above)

Currently you need to check the current time by polling, so for example your application_task() should be run every few seconds and compare RTC time with expected time.
STM32 contains also alarm registers that triggers IRQs, but this is not implemented in SDK, you will need to use STM32 HAL functions for that.

The RTC keeps counting even between reboots as long as Core Module has some power (USB/battery module)

Thank you for response,
I tried

#include <application.h>
#include "stm32l0xx_hal.h"

#define BATTERY_UPDATE_INTERVAL     (1 * 60 * 1000)
// Button instance
twr_button_t button;

twr_scheduler_task_id_t task_id_one_hour;

// Set up a timer to trigger an interrupt every hour
TIM_HandleTypeDef htim;

void receive_mqtt(uint64_t *id, const char *topic, void *value, void *param);

// subscribe table, format: topic, expect payload type, callback, user param
static const twr_radio_sub_t subs[] = {
    {"pepego/order/hey", TWR_RADIO_SUB_PT_INT, receive_mqtt, (void *) true}
};

void receive_mqtt(uint64_t *id, const char *topic, void *value, void *param)
{
    twr_log_info("receive_mqtt triggered.");
    twr_log_info("New URL set to %s.", (char*)value);
}

// Event handlers to publish battery voltage when either button is pressed or a battery events happens
void press(twr_button_t *modul, twr_button_event_t udalost, void *parametry){
    (void) modul;
    (void) parametry;
    float voltage;
    if (udalost == TWR_BUTTON_EVENT_PRESS){
        twr_log_info("button");
        twr_module_battery_get_voltage(&voltage);
        twr_radio_pub_battery(&voltage);
    }
}

void battery_event_handler(twr_module_battery_event_t event, void *event_param)
{
    (void) event_param;
    float voltage;
    if (event == TWR_MODULE_BATTERY_EVENT_UPDATE)
    {
        if (twr_module_battery_get_voltage(&voltage))
        {
            twr_radio_pub_battery(&voltage);
        }
    }
}

void hourlyFunction(void){
    bool parameter = true;
    twr_log_debug("HENLO");
    twr_radio_pub_bool("update_request", &parameter); // send message "true" to MQTT to trigger return message
    //twr_scheduler_unregister(task_id_one_hour);
}

void TIM2_IRQHandler() {
  HAL_TIM_IRQHandler(&htim);
}

// Timer callback function
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
  if (htim->Instance == TIM2) {
    hourlyFunction();
    //task_id_one_hour=twr_scheduler_register(hourlyFunction, NULL, twr_tick_get());
  }
}

void application_init(void)
{
    // Initialize logging
    twr_log_init(TWR_LOG_LEVEL_DUMP, TWR_LOG_TIMESTAMP_ABS);
    // Initialize Radio
    //twr_radio_init(TWR_RADIO_MODE_NODE_LISTENING); 
    twr_radio_init(TWR_RADIO_MODE_NODE_SLEEPING); 
    twr_radio_set_rx_timeout_for_sleeping_node(500); // radio will be turned on for receiving a return message, time in milliseconds
    twr_radio_set_subs((twr_radio_sub_t *) subs, sizeof(subs)/sizeof(twr_radio_sub_t));
    twr_radio_pairing_request("pepego", FW_VERSION);

    // Initialize button
    twr_button_init(&button, TWR_GPIO_BUTTON, TWR_GPIO_PULL_DOWN, 0);
    twr_button_set_event_handler(&button, press, NULL);
  

    // Initialize battery
    twr_module_battery_init();
    twr_module_battery_set_event_handler(battery_event_handler, NULL);
    twr_module_battery_set_update_interval(BATTERY_UPDATE_INTERVAL);

    // Initialize HAL library
    HAL_Init();

    // Initialize system clock
    //SystemClock_Config();

    htim.Instance = TIM2;
    htim.Init.Prescaler = 31999; // 1ms resolution
    htim.Init.Period = 59999; // 1 hour period
    htim.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_Base_Init(&htim);

    __HAL_TIM_ENABLE_IT(&htim, TIM_IT_UPDATE);
    // Enable timer interrupt
    HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM2_IRQn);

    // Start the timer
    HAL_TIM_Base_Start_IT(&htim);

    twr_log_info("application_init started");
}

i also had to comment out TIM2_IRQHandler(void){…} function in twr_timer.c and also void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){…} function in twr_ws2812b.c, doesnt seem that it broke anything, but its still not working.
Also im trying to send MQTT response from Playground to every message from press() function. Playground publishes the response but i cant receive it in my Core and do something with it, maybe its wrong implementation but this i all i could find in sdk/docs/forum or hackster.io.
I was also thinking about using mqtt to synchronize the rtc_clock every few often.