К списку форумов К списку вопросов
Как расчитать количество дней в интервале дат?(Access)
afal
08.08.2004 - 14:15
Нужно вычислить количество дней месяцев и лет в интервале двух дат. Работает вот такой алгоритм:
=================================================================
   Dim intYar As Integer
    Dim intDay As Integer
    Dim intMon As Integer
    Dim dblRez As Double
    dblRez = DateDiff("y", Nz(Me![GenExp]), Me![EnGnExp]) / 365.25
     intYar = Fix(dblRez)
     dblRez = dblRez - intYar
     intDay = dblRez * 365.25
     intMon = Fix(intDay / 30.5)
     intDay = (dblRez * 365.25 / 30.5 - intMon) * 30.5
     Me!AllYr = intYar 'количество лет
     Me!AllMon = intMon 'количество месяцев
     Me!AllDay = intDay 'количество дней
=================================================================
До 17 лет включительно считается все правильно. Но при интервале дат более указанного появляется ошибка, а именно считается на один день больше, потом с какого-то предела начинает считать на два дня больше и т.д. Конечно, можно ввести поправочные коэффициенты в этих интервалах дат, но это будет не красиво. Может кто сталкивался с такой задачей, подскажите варианты.
Заранее благодарен.
KAPACb
1 - 08.08.2004 - 15:13
Во всех субд кол-во дней вычисляется вычитанием из одной даты другую.
Типа Days = Date2 - Date1
Если у тебя день, месяц, и год отдельными intами, то переведи их в дату.
Функцию в Акцессе, для перевода дня, месяца, и года в date не помню.
afal
2 - 08.08.2004 - 23:05
(1)То, что ты написал "Типа Days = Date2 - Date1", в VB выполняет функция
DateDiff() и поля в ней GenExp, и EnGnExp имеют тип даты, но вопрос ты прочитал не внимательно.
KAPACb
3 - 09.08.2004 - 03:42
Чувак, не тормози.
Во первых можно делать без функций, а вычитанием.
Во вторых что тебе мешает сделать две переменные date и вычислить кол-во дней?
afal
4 - 09.08.2004 - 12:39
KAPACb - видимо читать ты все-таки не умеешь дальше чем заглавие ветки.
для afal
5 - 09.08.2004 - 14:39
это ты тормозишь, а Карасик абсолютно прав. Зачем так извращаться? Тем паче, что у тебя из-за твоих дробей и возникает ошибка. Считаешь число дней, хранишь в массиве количество дней в каждом месяце(январь, февраль...) и еще високосные года учитываешь. Далее следует элементарная математика для детей дошкольного возраста. Элементарно, Ватсон. :-)
KAPACb
6 - 09.08.2004 - 22:07
Сам алгоритм в корне неверный с его 365.25 дней в году и 30.5 дней в месяце.
Вот так это должно выглядеть:
шаг 1.
DateResult = Me![EnGnExp]) - Me![GenExp])
шаг 2.
перевод DateResult в год, месяц, число
Это делает функция, какая в VB так делает, не помню, искать лень.
IT
7 - 10.08.2004 - 05:56
Data1-data2=Количество дней в VB это всегда так было(если обе переменные являются датами, иначе надо привести к типу DATE DATA2=DataValue(Data2))
afal
8 - 10.08.2004 - 12:30
Вот как раз - то "перевод DateResult в год, месяц, число" и являются сутью вопроса, вернее даже год и месяц определяются безотказно, а именно число, т.е. количество дней оставшихся после определения целого количества лет и месяцев дают ошибку и то только при разнице дат более 17 лет. А что алгоритм не верный, так это и так ясно, иначе бы не было вопроса. Да есть такие функции в 1С, в С++, а вот в VB я такого не нашел, поэтому и спросил, кто знает. Но исключительная уклончивость объяснений позволяет предпологать, что не знает ни кто. Но тем не менее, хоть поговорили ;)
KAPACb
9 - 10.08.2004 - 13:08
проверь, делается ли это функциями Month(Date), Year(Date), Day(Date)
для afal
10 - 10.08.2004 - 14:18
Кстати, еще раз субж перечитал... Один вопрос(на первый взгляд оЧЧень глупый): с точки зрения задачи что такое "месяц" и "год", сколько в них дней(постоянная ли величина или как в действительности, переменная)?
Я вроде в посте №5 предложил алгоритм...
KAPACb
11 - 10.08.2004 - 16:20
Мазафака, я ошибся в 6 и в 9.
Date2 - Date1 - это, естественно, не дата а именно количество дней, разница в днях между двумя датами.
Тебе же надо не просто количество дней, например,648, а именно разницу в годах, месяцах и днях?
Короче, решается проблема видимо так:
YearResult = Year(Date2) - Year(Date1)
MonthResult = Month(Date2) - Month(Date1)
DayResult = Day(Date2) - Day(Date1)
afal
12 - 10.08.2004 - 17:13
(10) "месяц" и "год"- величина переменная (отсюда и взялось количество дней 365.25, т.е. усредненное за четыре года), а реализацию алгоритма в посте 5 ты сам-то представляешь? Можно вообще занести в массив весь Григарианский календарь, но опять все сведется к вычислению разности номеров элементов массива и перевода в количество лет, месяцев и дней. И одна "маленькая неприятность" - заполнению этого массива придется посвятить весь остаток жизни (если еще уложишься). Написать функцию, которая считает приближенно (как, например, считают кадровики стаж) я и сам могу. Вопрос в том что бы воспользоваться уже имеющимися средствами в VB или, если кому не жалко, предложить собственную реализацию решения.
(11) Например, нужно вычислить сколько лет, месяцев и дней между датами 16.03.1994г. и 01.06.1999г. Результат должен быть представлен в такой форме: "5 лет; 2 месяца; 16 дней". Из всего что ты написал правильно сработает только YearResult. Поскольку YearResult =5; MonthResult = 62, а DayResult=1907. Разница в представлении очевидна. Т.е. нужны опять таки преобразования к требуемой форме. Но уже радует, что хоть задача-то стала понятной ;)
KAPACb
13 - 11.08.2004 - 02:42
В акцессе случайно нет функции Month(s)Between(date,date)?
KAPACb
14 - 11.08.2004 - 03:01
Ээээх, чувак, всё ж тебе разжовывать приходицца :)
Вот тебе алгоритм. Даже не поленился акцесс запустить, проверить :)
date1 = "16.03.1994"
date2 = "01.06.1999"
If Year(date2) > Year(date1) Then
  YearResult = Year(date2) - Year(date1)
Else
  YearResult = Year(date1) - Year(date2)
End If
If Month(date2) > Month(date1) Then
  MonthResult = Month(date2) - Month(date1)
Else
  MonthResult = Month(date1) - Month(date2)
End If
If Day(date2) > Day(date1) Then
  DayResult = Day(date2) - Day(date1)
Else
  DayResult = Day(date1) - Day(date2)
End If
MsgBox (YearResult)
MsgBox (MonthResult)
MsgBox (DayResult)
IT
15 - 11.08.2004 - 05:49
DAY между прочим возвращает не количество дней от рождества христова, я день даты Т.е. Day(16.03.1994)=16
Day(01.06.1999)=1 1-16=-15
Тоже самое и с остальным
в 12 YearResult =5; MonthResult = 3; DayResult=-15
Кто хочет поспорить ?
KAPACb
16 - 11.08.2004 - 09:46
(15) Дурень, прочитай внимательно 14. И причем тут количества дней от рождиства христова?
А насчет кто хочет поспорить" если читать код не умеешь, засунь (14) в акцесс и запусти. И посмотри сколько будет DayResult 1 или -15.
для afal
17 - 11.08.2004 - 09:48
12. А зачем весь Григорианский календарь вводить? Двумерный массив из 12 строк(по количеству месяцев) и двух столбцов. Строка массива: месяц, кол-во дней в месяце.
Далее, чтоб не запутаться, отсчет можно вести от ближайшего(с округлением в меньшую сторону) високосного года(который /4).
Вопрос только один, даже в пределах одного месяца... Положим, есть период с XX.YY.ZZZZ до XX.YY+1.ZZZZ. В зависимости от значения YY количество дней может разниться. Что в этом случае делать? Т.е., если промежуток более 4-х лет, то остаток может быть однозначно не определяем(если не сделать допущения).
14. Алгоритм некорректный.
15. Абсолютно верно.
для afal
18 - 11.08.2004 - 09:49
Тьфу, блин, 15 только наполовину верно. Как DAY -15 может возвращать???
для KAPACb
19 - 11.08.2004 - 09:51
14. А что, в ВБ нет функции а-ля ABS()?
KAPACb
20 - 11.08.2004 - 09:52
Так, действительно, алгоритм (14) не верный.
(0) Когда даты 31.12.2003 и 01.01.2004 - ты какой рещультат хочешь получить?
KAPACb
21 - 11.08.2004 - 09:54
(19) Да, это я с утра не догнал, проверки ни к чему. конечно нужен абс().
Но в свете последних мыслей abs не поможет.
Пример - даты 31.12.2003 и 01.01.2004.
должен быть результат 0 лет, 0 месяцев, 1 день как я понимаю.
afal
22 - 11.08.2004 - 10:03
(15) прошу прощения, в (12) я подставил результаты функции DateDiff().
(14) По представленному тобой коду получается, что YearResult =5; MonthResult = 3; DayResult=15. Что не соответствует действительности, поскольку на самом деле MonthResult = 2, трем он будет равен, только когда исполнится 16.06.1999. Поэтому нужно писать еще одно условие, учитывающее это обстаятельство. Функции подобной "Month(s)Between(date,date)", я не знаю и как раз хотел узнать, т.е. как я писал в (12), "воспользоваться уже имеющимися средствами в VB".
для KAPACb
23 - 11.08.2004 - 10:14
Проблема, насколько я понимаю, в том, как считать остаток от не полных четырех лет.
afal
24 - 11.08.2004 - 10:23
(20) В (0) как раз и получится 1 день. Поскольку разница между двумя датами там считается функцией DateDiff() в количестве дней между этими датами. И потом уже оттуда "вояются" месяцы и годы.
afal
25 - 11.08.2004 - 10:30
(23) Проблема, как считать остаток ДНЕЙ от полных лет и месяцев, которые в пределах разумного считаются безукоризненно. Я незнаю, конечно, возможно если считать в разных тысячелетиях и там будет ошибка, но практического применения это вряд ли найдет в большинстве случаев.
Sergini
26 - 11.08.2004 - 11:38
Дроби, средние значения однозначно не пойдут, так как будут давать погрешности.
Попробуй так:
Function sss(A, B As Date) As String
Dim intYar As Integer
    Dim intDay As Integer
    Dim intMon As Integer
    Dim dblRez As Double
    Dim DD As Date
    ' Твой вариант
    'dblRez = DateDiff("y", A, B) / 365.25
     'intYar = Fix(dblRez)
     'dblRez = dblRez - intYar
     'intDay = dblRez * 365.25
     'intMon = Fix(intDay / 30.5)
     'intDay = (dblRez * 365.25 / 30.5 - intMon) * 30.5
     ' Мой Вариант
     intYar = DateDiff("yyyy", A, B)
     intMon = (DateDiff("m", A, B) - intYar * 12)
     DD = DateAdd("yyyy", intYar, A)
     DD = DateAdd("m", intMon, DD)
     intDay = DateDiff("y", DD, B)
     sss = intDay & " " & intMon & " " & intYar
End Function
Sergini
27 - 11.08.2004 - 12:00
Правда это тоже работать будет условно, так как нужно уточнить что понимать под месяцем. Сколько нужно получить между датой 12/07/2004 и 11/08/2004? С одной стороны это месяц, так как прошло 30 дней, но не факт так как месяц может быть и 31 день.
для afal
28 - 11.08.2004 - 13:22
Еще раз говорю. Есть какой-то промежуток, например: [13.03.2003,17.07.2014].
Разбиваешь на промежутки:
[13.03.2003, 12.03.2011] - целые периоды по 4 года(по календарю!), то бишь, 8 лет;
[01.01.2012, 31.12.2013] - число целых лет(по календарю!) внутри остатка, т.е., 2 года;
[13.03.2011, 31.12.2011], [01.01.2014,17.07.2014] - два периода остатков, нужно считать с помощью массива, о котором я говорил выше. Данные два промежутка еще можно разделить на несколько с целью вычленения целого числа месяцев. + нужно анализировать, попадают ли остатки на високосный год(/4 или не /4). Мысль ясна? Ну повозиться чуть-чуть с кодированием придется, зато работать будет более корректно.
для afal
29 - 11.08.2004 - 13:24
В любом случае, погрешности в один день не избежать, но множиться при увеличении периода она не должна.
afal
30 - 11.08.2004 - 15:49
Sergini ты сам, наверное не проверял работу этого кода. Дело в том, что у тебя intMon всегда будет величина отрицательная, кроме случая, когда число и месяц начальной и конечной дат будут совпадать, поскольку (intYar * 12) > DateDiff("m", A, B) (число лет считается включительно, а число месяцев по фактическому количеству в интервале дат). Т.о. если считать разницу в датах с 01.06.89 по 01.01.99, то intYar=10, а на самом деле шести месяцев хватать не будет до десяти лет. Но само по себе применение функции DateAdd() интересная идея. Спасибо за нее.(28) И я еще раз говорю слишком сложную реализацию ты предлагаешь и то только теоретически. На практике все может оказаться значительно сложнее и многообразнее той картины, которая видится при теоретическом подходе. Тем более, что ты пророчишь: «В любом случае, погрешности в один день не избежать…», так это все уже работает в том виде, который был представлен изначально. Тогда лучше и проще считать по принципу расчета стажа кадровиками, принимая каждый месяц, не зависимо от года, в котором он находится, равным 30 дней. Почитай постинги выше, какие были предложения «в лёт» и во что они вылились потом. Задача не простая, а решение, предлагаемое тобой, слишком сложное, есть проще.
Sergini
31 - 11.08.2004 - 16:13
2 afal. Я знаю, что функция которую я написал работает в данном варианте неверно и она и не должна работать верно. Для этого и написал, чтобы ты пояснил: что в твоём понимании месяц? Ведь месяц это интервал, причём в данном случае разный(29-31 день). Поэтому задача в этом варианте не имеет решений. Вот если бы ты принял за месяц интервалы типа 21.12.2002-21.01.2003.
Т. е. принять за месяц промежуток от даты n этого месяца до даты n следующего, то такая задача уже бы имела решение.
afal
32 - 11.08.2004 - 17:56
Sergini я не совсем понял вопрос. Месяц в данной задаче это интервал времени, соответствующий одноименным значениям календаря. Т.е. если это январь, то это 31 день, если февраль, то в зависимости от года, но столько же сколько в календаре. Повторюсь, например, нужно вычислить сколько лет, месяцев и дней между датами 16.03.1994г. и 01.06.1999г. Результат должен быть представлен в такой форме: "5 лет; 2 месяца; 15 дней". Как здесь определить, что понимается под месяцем? Я понимаю месяц. Только считается он в данном случае не с 01.03.1994г., а с 16.03.1994г. Но мне представлялось, что эти вопросы как раз и должны учитывать встроенные функции языка. Видимо я ошибался.
Sergini
33 - 12.08.2004 - 08:44
Afal ты привёл не совсем удачный пример. А что будет в случае. с 05.02.1994 по 25.05.1994. Годов понятно 0. Месяцев целых 2(март, апрель). Ещё остаётся примерно 23 дня от февраля + 25дней от мая. Если их сложить, то они явно за месяц переваливают. Что же мы теперь подразумеваем под месяцем: можем принять 31, 28, 30... То есть если тебе нужно считать только целые месяца, то всё понятно(только количество дней будет больше 31 в некоторых случаях). В противном случае требуются уточнения.
afal
34 - 12.08.2004 - 10:10
Да Sergini ты совершенно прав. Но твой пример, можно ведь рассмотреть и в другом ракурсе. С 5 февраля по 5 мая будет три месяца со всем тем их количеством дней которое предусмотрено календарем и еще остаеся 20 дней мая, которые и пойдут в счет количества дней этого временного интервала. Я именно в таком плане хотел построить расчет. Вот и получится 0 лет 3 месяца 20 дней. Ведь, наверное, трудно оспаривать, что с 5 февраля по 5 мая будет три месяца? Другой вопрос по сколько дней будет в этих месяцах? По столько же как и в месяцах которые попадают в этот временной интервал минус единица.
Sergini
35 - 12.08.2004 - 10:52
А я о чём говорил? Цитирую:"...Вот если бы ты принял за месяц интервалы типа 21.12.2002-21.01.2003.Т. е. принять за месяц промежуток от даты n этого месяца до даты n следующего, то такая задача уже бы имела решение."
Sergini
36 - 12.08.2004 - 11:16
А вот и решение:
Function sss(A, B As Date) As String
    Dim intYar As Integer
    Dim intDay As Integer
    Dim intMon As Integer
    Dim DD As Date
     DD = DateSerial(Year(B), Month(B), Day(A))
     intYar = DateDiff("yyyy", A, DD)
     intMon = (DateDiff("m", A, DD) - intYar * 12)
    
     intDay = DateDiff("y", DD, B)
     If intDay < 0 Then
        DD = DateSerial(Year(B - Day(B)), Month(B - Day(B)), Day(A))
        intYar = DateDiff("yyyy", A, DD)
        intMon = (DateDiff("m", A, DD) - intYar * 12)
        intDay = DateDiff("y", DD, B)
      End If
     sss = intDay & " " & intMon & " " & intYar
End Function
afal
37 - 13.08.2004 - 11:14
Я видимо не врубился в твое предложение принять за месяц интервалы дат. Скорее всего, это связано с тем, что в примере интервал не внутри года, а на смене лет. Ну да бог с ним. Все совершенно правильно, но, к сожалению код, который ты представил, в той форме как он есть, не работает как нужно. Например в интервале дат 23.06.96 – 12.02.03 получается 7 лет, ( минус)5 месяцев и 20 дней. Но это просто информация для тебя, что бы ты ни думал, что его где-то можно применять без изменений. Я еще не пытался исправить эту ситуацию, но думаю, что большого труда это не составит. Спасибо тебе большое за советы и помощь.
Sergini
38 - 13.08.2004 - 15:38
Пожалуйста :)
"Например в интервале дат 23.06.96 – 12.02.03 получается 7 лет, ( минус)5 месяцев и 20 дней"
Попробуй тогда так:
Function sss(A, B As Date) As String
    Dim intYar As Integer
    Dim intDay As Integer
    Dim intMon As Integer
    Dim DD As Date
     DD = DateSerial(Year(B), Month(B), Day(A))
     intYar = DateDiff("m", A, DD) \ 12
     intMon = (DateDiff("m", A, DD) - intYar * 12)
    
     intDay = DateDiff("y", DD, B)
     If intDay < 0 Then
        intMon = intMon - 1
        DD = DateSerial(Year(B - Day(B)), Month(B - Day(B)), Day(A))
        intYar = DateDiff("yyyy", A, DD)
        
        intDay = DateDiff("y", DD, B)
      End If
     sss = intDay & " " & intMon & " " & intYar
End Function
KAPACb
39 - 15.08.2004 - 04:04
Похоже Sergini сделал верный алгоритм, снимаю шляпу
afal
40 - 15.08.2004 - 10:13
Да Sergini, все правильно, но небольшая ошибка. Например в том же интервале дат 23.06.96 – 12.02.03 получается 7 лет, 7 месяцев и 20 дней, а должно 7 лет, 6 месяцев и 20 дней. Не следует вычислять
        intYar = DateDiff("yyyy", A, DD)
через формат года, года в этом случае округляются до большего значения. Вот такой код, вроде работает во всех диапазонах верно:
Function sss(A, B As Date) As String
    Dim intYar As Integer
    Dim intDay As Integer
    Dim intMon As Integer
    Dim DD As Date
     DD = DateSerial(Year(B), Month(B), Day(A))
     intYar = DateDiff("m", A, DD) \ 12
     intMon = (DateDiff("m", A, DD) - intYar * 12)
     intDay = DateDiff("y", DD, B)
     If intDay < 0 Then
        intMon = intMon - 1
        DD = DateSerial(Year(B - Day(B)), Month(B - Day(B)), Day(A))
        intDay = DateDiff("y", DD, B)
      End If
     sss = intDay & " " & intMon & " " & intYar
End Function
Спасибо тебе еще раз!

К списку вопросов на форуме Базы данных

>>