Главная » Статьи » Полезная информация

Вычисление разницы дат. Считаем дни.

  Выполняя небольшой проект, столкнулся с необходимостью вести в устройстве подсчет количества пройденных суток. Время и дата брались с микросхемы DS1307, а сам период подсчета был небольшой (не больше 30 дней) и счетчик был организован на суммировании отработанных часов. Каждый час данные сохранялись в EEPROM на случай перебоя с питанием. И конечно же во время отсутствия питания часы не прибавлялись, что приводило бы к неточностям подсчета. В принципе все устраивало, так как питание устройства предусматривает наличие генератора, который запускается в течении пары минут. Но стало интересно как организовать программно расчет пройденного времени по разнице дат. Это сразу решило бы много проблем, в том числе и заморочки со стабильным питанием, да и ресурс EEPROM не безграничен.  Результатом своей работы я и хочу сегодня поделиться с вами.

 

 Алгоритм расчета строится на вычислении двух значений. Первое значение (Ds) - это количество дней между первой датой (когда запустили счетчик) и окончанием года. Второе значение (Dd) - количество дней от второй даты (когда проверяем счетчик) и окончанием года. Узнав эти значения мы легко можем высчитать разницу между датами (D).  Для случая если даты находятся в одном году приведу поясняющую иллюстрацию.

 В случае же если даты относятся к разным годам значение Dd считается не от конца года, а от начала. И при необходимости добавляется количество дней смежных лет.

 

 Итак, код для расчета ниже. Занимает в памяти около 1,5 кб и учитывает високосные годы (правда в упрощенном виде, считая високосными все года кратные четырём).

Dim D As Word                            'количество пройденных дней

Dim Ds As Word                           'вспомогательные переменные
Dim Dd As Word
Dim Dt As Byte
Dim Dy As Byte
Dim Dx As Byte
Dim Nn As Byte
Dim Dn As Word
Dim Dm As Word

Dim D0 As Byte                           'число пуска
Dim M0 As Byte                           'месяц пуска
Dim Y0 As Byte                           'год пуска

Dim D1 As Byte                           'число проверки
Dim M1 As Byte                           'месяц проверки
Dim Y1 As Byte                           'год проверки


'значения переменных в качестве примера
'число/месяц/год запуска счетчика
D0 = 1
M0 = 8
Y0 = 16

'число/месяц/год просмотра
D1 = 2
M1 = 10
Y1 = 51


'///начало вычислений

'В цикле идет вычисление двух величин:
'Ds-оставшееся кол-во дней в году с момента запуска счетчика
'Dd-оставшееся кол-во дней в году от момента просмотра

Do
 Incr Nn
  If Nn = 1 Then
   Dy = Y0 / 4                           'вычисляем високосный год
   Dn = D0
   Dm = M0
  Elseif Nn = 2 Then
   Dy = Y1 / 4
   Dn = D1
   Dm = M1
  End If

    Dy = Dy * 4                          'если год поделился без остатка тогда он високосный

  If Dm = 1 Then
    Dd = 31 - Dn
     If Dy = Y1 Then                     'если год високосный
      Dd = Dd + 335
     Else
      Dd = Dd + 334
     End If
  Elseif Dm = 2 Then
     If Dy = Y1 Then
      Dd = 29 - Dn
     Else
      Dd = 28 - Dn
     End If
      Dd = Dd + 306
  Elseif Dm = 3 Then
    Dd = 31 - Dn
    Dd = Dd + 275
  Elseif Dm = 4 Then
    Dd = 30 - Dn
    Dd = Dd + 245
  Elseif Dm = 5 Then
    Dd = 31 - Dn
    Dd = Dd + 214
  Elseif Dm = 6 Then
    Dd = 30 - Dn
    Dd = Dd + 184
  Elseif Dm = 7 Then
    Dd = 31 - Dn
    Dd = Dd + 153
  Elseif Dm = 8 Then
    Dd = 31 - Dn
    Dd = Dd + 122
  Elseif Dm = 9 Then
    Dd = 30 - Dn
    Dd = Dd + 92
  Elseif Dm = 10 Then
    Dd = 31 - Dn
    Dd = Dd + 61
  Elseif Dm = 11 Then
    Dd = 30 - Dn
    Dd = Dd + 31
  Elseif Dm = 12 Then
    Dd = 31 - Dn
  End If

   If Nn = 1 Then
    Ds = Dd
   End If


Loop Until Nn = 2
Nn = 0

'вычисляем пройденные дни
Dt = Y1 - Y0                             'считаем сколько лет между запуском и проверкой
If Dt = 0 Then                           'если старт счетчика и проверка в одном году
  D = Ds - Dd
Elseif Dt = 1 Then                       'если разница один год

  If Dy = Y1 Then                        'если год проверки високосный
   Dd = 366 - Dd
  Else
   Dd = 365 - Dd
  End If
  D = Ds + Dd
Else                                     'если прошло больше одного года
 Dt = Y0
 Incr Dt
 Do
  Dx = Dt / 4                            'проверка високосного года
  Dx = Dx * 4
   If Dx = Dt Then                       'если високосный прибавим 366 дней
    D = D + 366
   Else
    D = D + 365
   End If
  Incr Dt
 Loop Until Dt = Y1                      'не учитываем год проверки, выходим

 If Dy = Y1 Then                         'если год проверки високосный
   Dd = 366 - Dd
  Else
   Dd = 365 - Dd
  End If

  D = D + Ds
  D = D + Dd

End If

 Print D                                 'печатаем количество пройденных дней

End

 

 

 Результат вычислений записывается в переменную D и отправляется в терминал. Думаю по комментариям в коде будет понятно, куда кладутся значения дат. Проверить работу алгоритма можно во встроенном в Bascom-AVR симуляторе. На скриншоте виден результат подсчета и его сравнение с  функцией расчета разницы дат, встроенной в Excel.

Удачи!

 

 

 

UPD:

А вот алгоритм предложенный уважаемым Mrshilov, использующий в качестве опорной даты 2000й год и занимающий всего 1 кб памяти.

 

Dim Dd As Long , Temp_long As Long , Temp_word As Word , Temp1_word As Word
Dim Month As Byte , Dat As Byte , Year As Byte

'-------------------------------------------------------------------------------
Dat = 1
Month = 8
Year = 16
Gosub Days_since                                            'первая дата
Dd = Temp_long

Dat = 2
Month = 10
Year = 51
Gosub Days_since                                            'вторая дата

Dd = Temp_long - Dd
Print Dd                                                    'результат

End
'-------------------------------------------------------------------------------
Days_since:
Temp_word = Year + 2000                                     'дней от 2000 года
Temp_long = Temp_word * 367
Temp1_word = Month + 9
Temp1_word = Temp1_word / 12
Temp1_word = Temp_word + Temp1_word
Temp1_word = Temp1_word * 7
Temp1_word = Temp1_word / 4
Temp_long = Temp_long - Temp1_word
Temp1_word = Month * 275
Temp1_word = Temp1_word / 9
Temp_long = Temp_long + Temp1_word
Temp_long = Temp_long + Dat
Temp_long = Temp_long - 730530
Return

 

 

Категория: Полезная информация | Добавлено: 01.07.2016
Просмотров: 13372 | Комментарии: 7 | Рейтинг: 5.0/7
Всего комментариев: 7
7 Ev3658   (28.02.2018 23:54) [Материал]
Может я чего туплю, но високосный год можно вычислить так:
Год MOD 4 - если равно нулю, то високосный.
Vr = Year Mod 4
If Vr = 0 Then
в феврале будет 29 дней
Else
в феврале будет 28 дней
End If

0  
6 exersizze   (03.07.2016 13:13) [Материал]
Mrshilov  добавил ваш код в статью дабы не потерять.

+1   Спам
5 Mrshilov   (03.07.2016 11:52) [Материал]
Если использовать алгоритм расчета дней, прошедших с 2000 года, то можно совсем упростить:

Dim Dd As Long , Temp_long As Long , Temp_word As Word , Temp1_word As Word
Dim Month As Byte , Dat As Byte , Year As Byte

'-------------------------------------------------------------------------------
Dat = 1
Month = 8
Year = 16
Gosub Days_since 'первая дата
Dd = Temp_long

Dat = 2
Month = 10
Year = 51
Gosub Days_since 'вторая дата

Dd = Temp_long - Dd
Print Dd 'результат

End
'-------------------------------------------------------------------------------
Days_since:
Temp_word = Year + 2000 'дней от 2000 года
Temp_long = Temp_word * 367
Temp1_word = Month + 9
Temp1_word = Temp1_word / 12
Temp1_word = Temp_word + Temp1_word
Temp1_word = Temp1_word * 7
Temp1_word = Temp1_word / 4
Temp_long = Temp_long - Temp1_word
Temp1_word = Month * 275
Temp1_word = Temp1_word / 9
Temp_long = Temp_long + Temp1_word
Temp_long = Temp_long + Dat
Temp_long = Temp_long - 730530
Return

0  
4 exersizze   (03.07.2016 10:30) [Материал]
Не знал о такой функции, Mrshilov спасибо! Изобрел велосипед значит:)

+1   Спам
3 Mrshilov   (02.07.2016 22:42) [Материал]
Проще проверять два младших бита года:
Temp_byte = Year And 3
Если Temp_byte = 0, то год високосный.

Для вычисления дня с начала года в BASCOM существует функция Dayofyear:
Config Clock = User
_day = 1 : _month = 8 : _year = 16
Ds = Dayofyear()

0  
2 exersizze   (02.07.2016 21:17) [Материал]
Нет, здесь високосный рассчитывается следующим образом: сначала номер года делится на 4, а затем результат умножается на 4. Если после этих действий число получается такое же как и до манипуляций, тогда год кратен четырём и значит он високосный (переменные байтовые и при этих операциях в них может сохраняться только целое число). А упрощение здесь в том, что не откидываются года кратные 100. Так, ближайший 2100 год будет по этому алгоритму считаться високосным, хотя на самом деле он должен быть исключен. Но до 2100 нам далеко, поэтому такое упрощение допустимо:)
Ну а баском это всего лишь инструмент, но надо сказать со своей работой он справляется хорошо.

1 Valera18   (02.07.2016 13:25) [Материал]
И вновь проект который доказывает что у "BASCOM" большие возможности.

Принцип подсчёта високосного года Вы использовали из проекта?
"GPS приемник в корпусе Nokia3310" http://avrproject.ru/publ/gps_priemnik_v_korpuse_nokia3310/1-1-0-151

Я тоже сталкивался с подсчётом високосного года когда работал с GPS приёмником, так как время и дата идёт по гринвичу. При прибавления ко времени Своего часового пояса дата с GPS идёт всё-равно по гринвичу, поэтому пришлось придумать принцип вычисления високосного года со всеми последующими корректировками исходных данных с GPS, (то-есть время со Своим часовым поясом, следовательно без глючный месяц "февраль 28 дней или 29 дней", следовательно верный год).

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]






авторизация