пересчет TDateTime в число месяц и год

Сузунец

✩✩✩✩✩✩✩
19 Июн 2024
18
8
42
Сузун
Всем добрый день...
В общем пишу приложение на C++ Builder, в нем с помощью компонента DateTimePicker выбирается начало и окончание отопительного сезона. Полученные числа записываются в настройки (файл *.ini) и "кладутся" в регистры Analog Input (ModbusRTU), оттуда их забирает Master (на Arduino) и сохраняет их в EEPROM. Теперь эти числа нужно пересчитать в число, месяц и год для того что-бы сравнить с текущей датой.

Может у кого завалялась функция для пересчета или ссылка где можно подсмотреть...

Предполагаю возможные ответы на форуме:
1. Передавать дату 3 числами - dd.mm.yyyy
--- передача каждой даты в таком случае займет в 3 раза больше регистров, а только настройки системы отопления и вентиляции уже используют 10 регистров (пока 10), боюсь в итоге слишком длинный диалог между компьютером и Arduino получиться.
2. Написать самому функцию:
--- написать то можно, только зачем велосипед изобретать, к тому-же возможно кому-то еще поможет эта тема
 

Сузунец

✩✩✩✩✩✩✩
19 Июн 2024
18
8
42
Сузун
Нашел библиотеку UnixTime на PlatformIO от Alex'a. Осталось только перевести время из TDateTime в UnixTime, формулу тоже в каком-то форуме нашел. Вот привожу пример:
Пример:
#include <UnixTime.h>
#include <LCD_1602_RUS.h>

UnixTime stamp(3);  // указать GMT (3 для Москвы)
LCD_1602_RUS *lcd = new LCD_1602_RUS(0x27, 16, 2);

void setup() {
    lcd->init();
    lcd->clear();
    lcd->noCursor();
    lcd->backlight();
    uint16_t tDateTime = 45536; // Время в формате TDateTime, вообще-то это число имеет тип double, в целой части - дата, в дробной - время
    uint32_t unix_time = (uint32_t)((tDateTime - 25569.16666) * 86400); // Переводим в UnixTime
   
    stamp.getDateTime(unix_time);
    // Выводим день и месяц, с выводом года у меня почему-то проблемы, но он мне как-бы и не нужен
    String message = "Дата: " + (String)stamp.day + "." + (String)stamp.month;
    lcd->home();
    lcd->print(message);
}
//---------------------------------------------------------------------------
void loop() {
   
}
//---------------------------------------------------------------------------
 

bort707

★★★★★★✩
21 Сен 2020
3,056
910
Юникс-тайм для вашей задачи "из пушки по воробьям".
Юникстайм это 32битное число, а ваши регистры какого размера? 16бит, если не ошибаюсь. То есть для каждой даты вам понадобится по два регистра.
К тому же, Юникс-тайм задает время с точностью до секунды, а вам нужны только даты.

Я бы на вашем месте придумал свой формат, что-то типа две цифры число две цифры месяц - 112 = 12 января или 1025 - 25 октября
И никакие библиотеки вообще не понадобятся.
 
Изменено:

Сузунец

✩✩✩✩✩✩✩
19 Июн 2024
18
8
42
Сузун
@bort707, хорошая идея...

  • пишу в приложении функцию, которая переводит в свой формат
  • ложу в регистр, Master его забирает
  • а на контролере другая функция переводит обратно, кам мне нужно

Спасибо за подсказку....

P.S.
Кстати, я не передаю через Modbus UnixTime... просто брал с компонента DateTimePicker дату (без времени) типа TDateTime (без дробной части, указывающей на точное время) оставалось как раз 16 бит. Но его потом на контролере необходимо было "крутить" из одного формата в другой, потом в третий, что-бы в сухом остатке получить день, месяц, год....
 
Изменено:

Геннадий П

★★★★★★✩
14 Апр 2021
1,969
632
44
Зачем придумывать свой формат? Чтобы передать дату в двух байтах можно сделать следующее:
Берем стандартный Unix Timestamp. Например 2024-06-30 13:34.49 - 1719736489
Делим без остатка на 86400 (секунд в сутках), получаем кол-во суток с начала Unix Timestamp - 19904, что уже укладывается в 2-байтовый uint16.
При приеме проделываем обратную операцию.
 

Сузунец

✩✩✩✩✩✩✩
19 Июн 2024
18
8
42
Сузун
@Геннадий П, по-сидел... по-думал... а это не то-же самое? Теже яйца, только в профиль... :)
варик со своим форматом, может и не совсем "правильно" использовать, но позволяет избавиться от "лишней" библиотеки и чуть-чуть съэкономит памяти избавивишись от "тяжелого" юникс-тайм... ну все равно спасибо
 
Изменено:

Геннадий П

★★★★★★✩
14 Апр 2021
1,969
632
44
@Сузунец, Ну, это вам решать. Я опирался на использование стандартных функций и стандартного формата.
Можно конечно сделать:
5 бит - число
4 бита - месяц
4 бита - год
остальные биты на усмотрение
 
Изменено:
  • Лойс +1
Реакции: bort707

Сузунец

✩✩✩✩✩✩✩
19 Июн 2024
18
8
42
Сузун
Вобщем пошел другим путем... МК получает данные как есть, а полученую с RTC дату пересчитывает в TDateTime и сравнивает эти числа... ;)
C++:
// Тут храним полученные с компа числа без каких либо изменений
MHSet.SeasonStart = 45545; // Начало сезона (10.9.2024)
MHSet.SeasonStop = 45778; // Окончание сезона (1.5.2025)
// Получаем данные от RTC
uint16_t year_now = 2024; // Допустим сегодня 10 сентября 2024
uint8_t month_now = 9;
uint8_t day_now = 10;

//---------------------------------------------------------------------------
// Далее "магия" математики
uint8_t days_of_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
uint16_t tDateTime_now = 0; // Сегодняшняя дата TDateTime

// Считам сколько дней прошло с 1.01.1900 включительно, и с учетом высокосных годов
tDateTime_now = ((year_now - 1900) * 365) + floor((year_now - 1900)/4);
// Добавляем к tDateTime_now дни за полные месяца (не забываем про высокосный год)
for(uint8_t m=0; m<month_now-1; m++) {
    tDateTime_now += days_of_month[m];
    if (!(year_now%4) && (m == 1)) tDateTime_now++; // если(год высокосный && февраль) tDateTime_now += день
    }
tDateTime_now += day_now; // ну и добавляем какое сегодня число
//---------------------------------------------------------------------------

// Теперь можно сравнить
if (tDateTime_now >= MHSet.SeasonStart && tDateTime_now < MHSet.SeasonStop) MHSet.HeatingSeason = true;
else MHSet.HeatingSeason = false;