Вычислить текущий ДЕНЬ, относительно заданного

Thorn
Offline
Зарегистрирован: 07.11.2014

Вобщем задачка простая. Есть часы на ds3231 - они показывают время и дату (текущую) без библы:

//=====Rtc_Clock*
#define DS3231_I2C_ADDRESS 104 
byte seconds, minutes, hours, day, date, month, year;
byte decToBcd (byte val){return((val/10*16)+(val%10));}

//=========== Обработка показаний часов
void timeRtc(){
  Wire.beginTransmission(DS3231_I2C_ADDRESS);        //104 is DS3231 device address
  Wire.write(0x00);                                  //Start at register 0
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);           //Request seven bytes
  if(Wire.available()){ 
    seconds = Wire.read();                           //Get second
    minutes = Wire.read();                           //Get minute
    hours   = Wire.read();                           //Get hour
    day     = Wire.read();
    date    = Wire.read();
    month   = Wire.read();                           //Get month
    year    = Wire.read();
    seconds = (((seconds & B11110000)>>4)*10 + (seconds & B00001111));   //Convert BCD to decimal
    minutes = (((minutes & B11110000)>>4)*10 + (minutes & B00001111));
    hours   = (((hours & B00110000)>>4)*10 + (hours & B00001111));       //Convert BCD to decimal (assume 24 hour mode)
    day     = (day & B00000111); // 1-7
    date    = (((date & B00110000)>>4)*10 + (date & B00001111));         //Convert BCD to decimal  1-31
    month   = (((month & B00010000)>>4)*10 + (month & B00001111));       //msb7 is century overflow
    year    = (((year & B11110000)>>4)*10 + (year & B00001111));
  }
}

//=========== Обработка установки часов
void setRtc(byte seconds, byte minutes, byte hours, byte day, byte date, byte month, byte year){
  Wire.beginTransmission(DS3231_I2C_ADDRESS);        //104 is DS3231 device address
  Wire.write(0x00);                                  //Start at register 0
  Wire.write(decToBcd(seconds));
  Wire.write(decToBcd(minutes));
  Wire.write(decToBcd(hours));
  Wire.write(decToBcd(day));
  Wire.write(decToBcd(date));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.endTransmission();
}

и есть три ячейки eeprom в которых сохраняется начальный день (два знака) месяц и год (также два знака).

Подскажите как посчитать какой к примеру сегодня день если начальный 16.03.18. Пробовал тупо вычитанеим но сами понимаете правильно лиш в пределах одного МЕСЯЦА, если в разные то ерунда. Если в разные годы - то разнца :) в домолнительный -1 день :)

Выручайте. Время по часам есть dec так и BCD :) а вот стартовое только в dec

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Как вариант: сконвертировать нужную дату в unixtime, из unixtime - вычислить все компоненты. Например (выкусил из одного проекта, ни на что не претендую):

const uint8_t daysArray [] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 };
//--------------------------------------------------------------------------------------------------------------------------------------
uint32_t DateTimeData::unixtime(void) const
{
  uint32_t u;

  u = time2long(date2days(this->year, this->month, this->day), this->hour, this->minute, this->second);
  u += 946684800; // + 01.01.2000 00:00:00 unixtime

  return u;
}
//--------------------------------------------------------------------------------------------------------------------------------------
uint16_t DateTimeData::date2days(uint16_t _year, uint8_t _month, uint8_t _day) const
{
    _year = _year - 2000;

    uint16_t days16 = _day;

    for (uint8_t i = 1; i < _month; ++i)
    {
        days16 += pgm_read_byte(daysArray + i - 1);
    }

    if ((_month == 2) && isLeapYear(_year))
    {
        ++days16;
    }

    return days16 + 365 * _year + (_year + 3) / 4 - 1;
}
//--------------------------------------------------------------------------------------------------------------------------------------
long DateTimeData::time2long(uint16_t days, uint8_t hours, uint8_t minutes, uint8_t seconds) const
{    
    return ((days * 24L + hours) * 60 + minutes) * 60 + seconds;
}
//--------------------------------------------------------------------------------------------------------------------------------------
DateTimeData DateTimeData::fromUnixtime(uint32_t time)
{
  DateTimeData result;

  uint8_t year;
  uint8_t month, monthLength;
  unsigned long days;

  result.second = time % 60;
  time /= 60; // now it is minutes
  result.minute = time % 60;
  time /= 60; // now it is hours
  result.hour = time % 24;
  time /= 24; // now it is days
  
  year = 0;  
  days = 0;
  while((unsigned)(days += (isLeapYear(year) ? 366 : 365)) <= time) {
    year++;
  }
  result.year = year + 1970; // year is offset from 1970 
  
  days -= isLeapYear(year) ? 366 : 365;
  time  -= days; // now it is days in this year, starting at 0
  
  days=0;
  month=0;
  monthLength=0;
  for (month=0; month<12; month++) 
  {
    if (month==1) 
    { // february
      if (isLeapYear(year)) 
      {
        monthLength=29;
      } 
      else 
      {
        monthLength=28;
      }
    } 
    else 
    {
      monthLength = pgm_read_byte(daysArray + month);
    }
    
    if (time >= monthLength) 
    {
      time -= monthLength;
    } 
    else 
    {
        break;
    }
  }
  result.month = month + 1;  // jan is month 1  
  result.day = time + 1;     // day of month  

    int dow;
    byte mArr[12] = {6,2,2,5,0,3,5,1,4,6,2,4};
    dow = (result.year % 100);
    dow = dow*1.25;
    dow += result.day;
    dow += mArr[result.month-1];
    
    if (isLeapYear(result.year) && (result.month<3))
     dow -= 1;
     
    while (dow>7)
     dow -= 7;

   result.dayOfWeek = dow;

  return result;
}
//--------------------------------------------------------------------------------------------------------------------------------------
bool DateTimeData::isLeapYear(uint16_t year)
{
  return (year % 4 == 0);
}
//--------------------------------------------------------------------------------------------------------------------------------------

Как-то так.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А что нужно? Количество дней между двумя датами? Да?

Thorn
Offline
Зарегистрирован: 07.11.2014

угу - завтра с утра и попробую ваш кусочек к своему примерить, спасибо :)

Thorn
Offline
Зарегистрирован: 07.11.2014

ЕвгенийП пишет:

А что нужно? Количество дней между двумя датами? Да?

Да , именно количество дней (время установки неимеет значения), установка и отсчёт имеено дней.

b707
Offline
Зарегистрирован: 26.05.2017

минутный поиск в гугле... "честный Си" без библиотек

static int days[12+1] = {
    0,
    31,
    31+ 28,
    31+ 28+ 31,
    31+ 28+ 31+ 30,
    31+ 28+ 31+ 30+ 31,
    31+ 28+ 31+ 30+ 31+ 30,
    31+ 28+ 31+ 30+ 31+ 30+ 31,
    31+ 28+ 31+ 30+ 31+ 30+ 31+ 31,
    31+ 28+ 31+ 30+ 31+ 30+ 31+ 31+ 30,
    31+ 28+ 31+ 30+ 31+ 30+ 31+ 31+ 30+ 31,
    31+ 28+ 31+ 30+ 31+ 30+ 31+ 31+ 30+ 31+ 30,
    31+ 28+ 31+ 30+ 31+ 30+ 31+ 31+ 30+ 31+ 30+ 31
};

static int dayS[12+1] = {
    0,
    31,
    31+ 29,
    31+ 29+ 31,
    31+ 29+ 31+ 30,
    31+ 29+ 31+ 30+ 31,
    31+ 29+ 31+ 30+ 31+ 30,
    31+ 29+ 31+ 30+ 31+ 30+ 31,
    31+ 29+ 31+ 30+ 31+ 30+ 31+ 31,
    31+ 29+ 31+ 30+ 31+ 30+ 31+ 31+ 30,
    31+ 29+ 31+ 30+ 31+ 30+ 31+ 31+ 30+ 31,
    31+ 29+ 31+ 30+ 31+ 30+ 31+ 31+ 30+ 31+ 30,
    31+ 29+ 31+ 30+ 31+ 30+ 31+ 31+ 30+ 31+ 30+ 31
};

bool isLeapYear(int year)
{
    return (year % 4) == 0 && (year % 100) != 0 || (year % 400) == 0;
}

int getDays(int year,int mon,int day)
{
    return year * 365 +
           (year + 3) / 4 - (year + 99) / 100 + (year + 399) / 400 +
           (isLeapYear(year)? dayS: days)[mon-1] + day - 1;
}

void main()
{
    printf("%d",getDays(1990,2,3) - getDays(1984,10,27));
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Более короткое и менее ресурсоёмкое решение.

Заодно и дни недели считает. Если интересна теория, то вот здесь.

//
// Функция определяет количество дней, прошедших с Рождества Христова
// до указанного дня. Параметры: d (1-31), m (1-12), y (1-...)
// На самом деле она считает так, как будто всё это время 
// действовал григорианский каледарь, но для большинства
// расчётов это не важно
//
// ПРИМЕРЫ:
// 1. Определить количество дней между датами 02.04.2018 и 01.02.2018
//		dayFromXmass(2,4,2018) - dayFromXmass(1,2,2018)
//		Результат - 60
// 2. Определить день недели для даты 02.04.2018
//		dayFromXmass(2,4,2018) % 7 // 0-понедельник - 6-воскресенье
//		Результат - 0 (понедельник)
//
long dayFromXmass(const int d, const int m, const int y) {
	static const int daysOfYear[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
	const int y1 = y-1;
	return 365L*y1+y1/4-y1/100+y1/400+daysOfYear[m-1]+d-1+((!(y&3))&&((y%100)||(!(y%400))))*(m > 2);
}

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Вау!  ЕвгенийП, спасибо большущее, заберу себе вкапилку.  

Thorn
Offline
Зарегистрирован: 07.11.2014

ЕвгенийП пишет:

Более короткое и менее ресурсоёмкое решение.

Заодно и дни недели считает. Если интересна теория, то вот здесь.

Начал читать и в самом начале нужная формула плюс ваш код и вуаля... Три строки а насколько просто.

Я конечно попробовал под свои нужды своим разумением НО работает !!!! Респект Вам.


//=====Data incubation*
int dateIn, monthIn, yearIn;         //Дата закладки яиц (начало инкубации)
int dateOut, monthOut, yearOut;      //Дата предварительного вылупления (окончание инкубации)
int  endData;                        //Текущий день инкубации (расчётный от 21 дня всего)
static const int daysOfYear[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
long tIn;                             //Количество дней от РХ
long tOut;                            //Количество дней от РХ и начала инкубации

.....

//=========== Обработка Дней инкубации (прошедших)
void endincubation(){
  EEPROM.begin(512);              //Начало работы с EEPROM модуля esp8266
  dateIn=EEPROM.read(41);         //Считывание начала периода инкубации, день
  monthIn=EEPROM.read(42);        //Считывание начала периода инкубации, месяц
  yearIn=EEPROM.read(43);         //Считывание начала периода инкубации, год
const int y1 = year-1;
  tIn= 365L*y1+y1/4-y1/100+y1/400+daysOfYear[monthIn-1]+dateIn-1+((!(yearIn&3))&&((yearIn%100)||(!(yearIn%400))))*(monthIn > 2);
  tOut= 365L*y1+y1/4-y1/100+y1/400+daysOfYear[month-1]+date-1+((!(year&3))&&((year%100)||(!(year%400))))*(month > 2);
  endData=tOut-tIn;   
}

Всем огромное спасибо за отклик, сам нескоробы нашёл нужное (искал нетам вовсе, ближе к таймерам)

b707
Offline
Зарегистрирован: 26.05.2017

Thorn пишет:

Всем огромное спасибо за отклик, сам нескоробы нашёл нужное (искал нетам вовсе, ближе к таймерам)

в качестве благодарности за решение может поясните нам логику... почему "искал в таймерах"?

Тот код, что выше кода Евгения - я нашел в Гугле за 1 минуту по самому тупому запросу "разность между двумя датами". Почему вы не пошли этим путем?

 

UPDATE... вчитался в тему, вопрос снимается. Вы даже для форума сумели сформулировать задачу только к пятому сообщению...

Учитесь выражать свои мысли и ставить задачи правильно - тогда и форумы не нужны будут. Если правильно спрашивать - все ищется в гугле за пару минут.

Thorn
Offline
Зарегистрирован: 07.11.2014

Я искал по пути... получить дату от текущей прибавлением....., и всё в этом духе. С помощью библиотек часиков такое можно было, но хотел именно математически посчитать. В основном примеры попадались именно отсчёта, тоесть задано время\дата - выполнить  функцию по истечении millis. Тут море, суточные и недельные.... А вот посчитать именно кол-во дней между двумя датами - ниодного примера (за исключением перевода в unix час.

А понадобилось знать имеено количество прошедших дней - для изменения параметров инкубации. С 1 по 14 - высокая температура, низкая влажность, частое перемешивание, редкое проветривание.... и в конце, низкая температура, однократное перемешивание, высокая влажность, частое проветривание.

Само собой это  и руками настраиваю в вэбинтерфейсе но вдруг подзабыл - тут сравниваются условия, если записанные в диапазоне ДОСТАТОЧНОГО то не менять, а если ниже то изменить к  минимально НЕОБХОДИМЫМ.

Осталось паруд ней до вылупления - и тут я обещался выложить проект инкубатора с фото и видеоматериаллом, малоли кому пригодится.

b707
Offline
Зарегистрирован: 26.05.2017

ну вот опять... к чему этот поток слов?

Thorn
Offline
Зарегистрирован: 07.11.2014

Ок, я understood

SLKH
Offline
Зарегистрирован: 17.08.2015

ЕвгенийП пишет:

 


long dayFromXmass(const int d, const int m, const int y) {
	static const int daysOfYear[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
	const int y1 = y-1;
	return 365L*y1+y1/4-y1/100+y1/400+daysOfYear[m-1]+d-1+((!(y&3))&&((y%100)||(!(y%400))))*(m > 2);
}

 

А можно объяснить "const" в параметрах функции?

b707
Offline
Зарегистрирован: 26.05.2017

SLKH пишет:

[А можно объяснить "const" в параметрах функции?

Не Евгений, но попробую - а потом он поправит, если что.

"const" в параметрах ообщает компилятору, что вы не собираетесь менять этот параметр внутри функции. Таким образом, компилятору не нужно предпринимать специальных действий, чтобы защитить внешние переменные от изменения внутри, например, сохранять копии - что может быть весьма накладно, особенно если переменные - составных типов типа String

В общем, правила хорошего стиля рекоменуют ВСЕГДА ставить "const". если переменная внутри функции не меняется

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

И не только функции. Это же касается любых констант. Чаще всего это не влияет ни на что, но иногда позволяет луче соптимизировать. А потому: не собираешься изменять - скажи об этом компилятору. нет-нет, да и пригодится.

Кстати, нам не актуально (в силу реализации gcc) но в некоторых компиляторах можно и результат функции обхывать const. Это, например, запретит использовать вызов функции в левой части оператора присваивания.

SLKH
Offline
Зарегистрирован: 17.08.2015

b707 пишет:

SLKH пишет:

[А можно объяснить "const" в параметрах функции?

Не Евгений, но попробую - а потом он поправит, если что.

"const" в параметрах ообщает компилятору, что вы не собираетесь менять этот параметр внутри функции. Таким образом, компилятору не нужно предпринимать специальных действий, чтобы защитить внешние переменные от изменения внутри,

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

 

 

Цитата:
например, сохранять копии - что может быть весьма накладно, особенно если переменные - составных типов типа String
Передача ссылок на массивы - отдельная тема, к обсуждаемой функции, имхо, не относится.

Цитата:
В общем, правила хорошего стиля рекоменуют ВСЕГДА ставить "const". если переменная внутри функции не меняется
Тогда почему этим правилом массово пренебрегают? Уж в стандартных отлаженных библиотеках и примерах эта конструкция должна мельтешить сплошь и рядом, не? И в готовых больших проектах?

Меня терзают смутные сомненья...

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

SLKH пишет:

Тогда почему этим правилом массово пренебрегают? Уж в стандартных отлаженных библиотеках и примерах эта конструкция должна мельтешить сплошь и рядом, не? И в готовых больших проектах?

Меня терзают смутные сомненья...

Чисто мое мнение.

Это сделано для того, что бы при компиляции, если вы изменяете такую перемнную, компилятор вам про это рассказал. То есть это такой самоконтроль.

В реальности же  в процедуру передается копия переменной, которую вы можете править сколько угодно, по окончании процедуры она просто уничтожается.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Не, ну понятное дело, что параметры передуются по значению. Я же писал в #15 для чего нужны const, причём не только в параметрах, а и при описаниях всего чего угодно (параметр - разновидность описания). Не собираешься изменять - пиши const, просто хорошая привычка. И случайно не изменишь, и, может быть, оптимизации поможешь.