Page 1 of 1

Calculate accurate differences between date/times

Posted: Thu Dec 21, 2006 8:13 pm
by netmaestro
Code updated for 5.20+

I originally posted a version of this yesterday that seemed to work fine, until I noticed some problems. I fixed them, or so I thought, only to discover that more problems existed. It was so unreliable and my attempts at fixing it were so stupid I thought it best to take it down for the time being and see if I could get if working. I damn near despaired, I don't mind admitting. However, a minor brainwave washed over me this morning and I came up with a better approach. After a fair bit of testing I think I have something that can be relied upon to be accurate. If someone can break this, please do and post the results:

Code: Select all

;=================================================== 
; Program:          DateDiff library function 
; Author:           netmaestro 
; Date:             December 20, 2006 
; Target OS:        Windows All 
; Target Compiler:  PureBasic 4.0 
; License:          Free, unrestricted, credit 
;                   appreciated but not required 
;=================================================== 

Structure TimeDiff 
  totaldays.l
  years.l 
  months.l 
  daysremaining.l 
  hours.l 
  minutes.l 
  seconds.l 
EndStructure 

Procedure DateDiff(dateearly, datelate, *diff.TimeDiff) 
  
  Protected totaldays,years,months,daysremaining,hours,minutes,seconds 
  
  curdate = dateearly 
  testdate = dateearly 
  startday = Day(dateearly) 
  totaldays = 0
  daysremaining = 0 
  
  While testdate <= datelate 
    testdate = AddDate(curdate, #PB_Date_Day, 1) 
    If testdate <= datelate 
      curdate = testdate 
      totaldays+1 
      daysremaining+1
      If Day(curdate) = startday 
        months+1 
        daysremaining=0 
      EndIf 
    EndIf 
  Wend 
  
  testdate = curdate 
  hours = 0 
  While testdate<datelate 
    testdate = AddDate(curdate, #PB_Date_Hour, 1) 
    If testdate <= datelate 
      curdate = testdate 
      hours+1 
    EndIf 
  Wend 
  
  testdate = curdate 
  minutes = 0 
  While testdate<datelate 
    testdate = AddDate(curdate, #PB_Date_Minute, 1) 
    If testdate <= datelate 
      curdate = testdate 
      minutes+1 
    EndIf 
  Wend 
  
  testdate = curdate 
  seconds = 0 
  While testdate<datelate 
    testdate = AddDate(curdate, #PB_Date_Second, 1) 
    If testdate <= datelate 
      curdate = testdate 
      seconds+1 
    EndIf 
  Wend 
  
  years = months/12 
  If years 
    months % 12 
  EndIf 
  
  *diff\totaldays = totaldays
  *diff\years = years 
  *diff\months = months 
  *diff\daysremaining = daysremaining 
  *diff\hours = hours 
  *diff\minutes = minutes 
  *diff\seconds = seconds 
  
EndProcedure 

dateearly = ParseDate("%yyyy/%mm/%dd/%hh:%ii:%ss", "2005/9/9/12:30:00") 
datelate = Date()

MyDiff.TimeDiff 
DateDiff(dateearly,datelate,@MyDiff) 

Debug "Total Days: "+Str(MyDiff\totaldays)
Debug "Years: "+Str(MyDiff\years) 
Debug "Months: "+Str(MyDiff\months) 
Debug "Days: "+Str(MyDiff\daysremaining) 
Debug "Hours: "+Str(MyDiff\hours) 
Debug "Minutes: "+Str(MyDiff\minutes) 
Debug "Seconds: "+Str(MyDiff\seconds) 

Posted: Thu Dec 21, 2006 9:44 pm
by Tranquil
I thought about this methode too but there must be a way without doing it in loops or?

Posted: Fri Dec 22, 2006 1:22 am
by netmaestro
Well, you can always fall back on the good old windows scripting host. VBScript has a native DateDiff function and you can build a command string, save it to a file and execute it with RunProgram. There's an example of using it to solve an expression here:

http://www.purebasic.fr/english/viewtop ... 57&start=3

It would just be a matter of modifying it to do DateDiff, shouldn't be hard.

Posted: Fri Dec 22, 2006 8:48 am
by PB
> there must be a way without doing it in loops

DoubleDutch posted a similar tip in 2005 that doesn't use loops:
http://www.purebasic.fr/english/viewtopic.php?t=18366

@netmaestro: Why doesn't your tip show weeks? :)

Posted: Fri Dec 22, 2006 12:59 pm
by netmaestro
I'm only looping for one reason, and that's to count accurately the number of calendar months that passed between the two dates. Everything up to weeks can be got easily with arithmetic, as their lengths never change. I'm working on a better version of this using a SYSTEMTIME structure which will handle dates before 1970 and can do stuff like tell you "You've been a human being for 62 years, 3 months, 22 days, 4 hours, 27 minutes and 13 seconds"

Posted: Fri Dec 22, 2006 1:12 pm
by jear
What's about this code

Code: Select all

; TimeDiffString : jear Dez 2005

;#TimeUnits = "Woche|n,Tag|e,Stunde|n,Minute|n,Sekunde|n"
#TimeUnits = "week|s,day|s,hour|s,minute|s,second|s"
  
Procedure.s AddTimeUnit(number.l, unit.l) 
  Protected Result.s, sUnit.s
  If number = 0 : ProcedureReturn "" : EndIf
  If number < 0 : number * -1 : EndIf
  sUnit = StringField(#TimeUnits, unit, ",")
  If number > 1
    sUnit = RemoveString(sUnit, "|")
  Else
    sUnit = StringField(sUnit, 1, "|")
  EndIf
  Result + Space(1) + Str(number) + Space(1) + sUnit  
  ProcedureReturn Result 
EndProcedure 

Procedure.s sTimeDiff(Seconds.l) 
  Protected Result.s
  Protected Weeks.l, Days.l, Hours.l, Minutes.l
  Weeks   = Seconds / 604800 : Seconds = Seconds % 604800
  Days    = Seconds / 86400  : Seconds = Seconds % 86400
  Hours   = Seconds / 3600   : Seconds = Seconds % 3600 
  Minutes = Seconds / 60     : Seconds = Seconds % 60 
  Result = AddTimeUnit(Weeks,1) 
  Result + AddTimeUnit(Days,2) : Result + AddTimeUnit(Hours,3) 
  Result + AddTimeUnit(Minutes,4) : Result + AddTimeUnit(Seconds,5) 
  ProcedureReturn Result
EndProcedure

PastDate.l = ParseDate("%dd.%mm.%yyyy", "20.12.2005 12:00")
Debug Date() - PastDate
Debug sTimeDiff(Date() - PastDate)
Delay(2000)
Debug PastDate - Date()
Debug sTimeDiff(PastDate - Date())

Posted: Fri Dec 22, 2006 3:51 pm
by rsts
netmaestro wrote:"You've been a human being for 62 years, 3 months, 22 days, 4 hours, 27 minutes and 13 seconds"
Once again, you amaze me. How the heck did you know that?

cheers

Re: Calculate accurate differences between date/times

Posted: Thu Feb 20, 2014 8:30 pm
by rsts
If I run the datediff routine above as is, seems OK.

If I change the "test" from
dateearly = ParseDate("%yyyy/%mm/%dd/%hh:%ii:%ss", "2005/9/9/12:30:00")
to
dateearly = ParseDate("%yyyy/%mm/%dd/%hh:%ii:%ss", "2010/12/31/12:30:00") or even
dateearly = ParseDate("%yyyy/%mm/%dd/%hh:%ii:%ss", "2005/10/30/12:30:00")

it appears to fail.

Did anything weird happen on the update to 5.20+ or was it always like this?

cheers

Re: Calculate accurate differences between date/times

Posted: Thu Feb 20, 2014 8:44 pm
by Demivec
rsts wrote:If I run the datediff routine above as is, seems OK.

If I change the "test" from
dateearly = ParseDate("%yyyy/%mm/%dd/%hh:%ii:%ss", "2005/9/9/12:30:00")
to
dateearly = ParseDate("%yyyy/%mm/%dd/%hh:%ii:%ss", "2010/12/31/12:30:00") or even
dateearly = ParseDate("%yyyy/%mm/%dd/%hh:%ii:%ss", "2005/10/30/12:30:00")

it appears to fail.
When I tested, it appears to work correctly for the dates you gave. Why are you saying it fails?

Re: Calculate accurate differences between date/times

Posted: Thu Feb 20, 2014 8:51 pm
by rsts
For the original test date I get 8 years 5 months
for the test date of dateearly = ParseDate("%yyyy/%mm/%dd/%hh:%ii:%ss", "2005/10/30/12:30:00")
slightly over 1 month different, I get 7 years 7 months. Or have I gone mad (very possible at this stage :)

Re: Calculate accurate differences between date/times

Posted: Thu Feb 20, 2014 10:41 pm
by Demivec
rsts wrote:For the original test date I get 8 years 5 months
for the test date of dateearly = ParseDate("%yyyy/%mm/%dd/%hh:%ii:%ss", "2005/10/30/12:30:00")
slightly over 1 month different, I get 7 years 7 months. Or have I gone mad (very possible at this stage :)
I see the problem now. When I did my tests I looked solely at the day count, which is correct.

The month count is incorrectly calculated. And the year count is based on the possibly faulty month count.


When I have a moment I will post a solution.

Re: Calculate accurate differences between date/times

Posted: Thu Feb 20, 2014 11:25 pm
by rsts
My apologies, Demivec. I should have been clearer as to what the fault was. I was just wondering if the error was introduced in the update to 5.2 or if it had been there all along. I imagine only netmaestro can answer that since I do not have the original source.

Thanks for your assistance in looking at it.

cheers

Re: Calculate accurate differences between date/times

Posted: Fri Feb 21, 2014 4:44 pm
by Demivec
rsts wrote:My apologies, Demivec. I should have been clearer as to what the fault was. I was just wondering if the error was introduced in the update to 5.2 or if it had been there all along. I imagine only netmaestro can answer that since I do not have the original source.

Thanks for your assistance in looking at it.
The error wasn't due to anything that changed in v5.21 LTS. The error was in the original code.

As I said earlier, the error is in how the calendar months are counted. Netmaestro compared the starting calendar day to each sequential day in turn, if the day in the month matched the number of months that occurred thus far was incremented.

Unfortunately not all months are the same length. This means if you started on a day (i.e. 1/31 of any year) and counted 40 more days you still wouldn't come to the day '31' of the next month because February would have only 28 or 29 days. This happens at various places around the calendar year and can make the month count inaccurate if the starting day is the 29th, 30th, or 31st of the month.

Because the year count is based off of the month count it will also be affected.

The end result is that the TotalDays count was correct, as were the hours, minutes, seconds. I believe the day count was also correct.


To correct the error you can change line 37 from:

Code: Select all

If Day(curdate) = startday
to

Code: Select all

If Day(curdate) = startday Or (startday > 28 And Day(curdate) = 1 And daysremaining > 3)

Re: Calculate accurate differences between date/times

Posted: Fri Feb 21, 2014 6:11 pm
by rsts
I did not catch that.

Gratitude for your assistance ;)

Re: Calculate accurate differences between date/times

Posted: Fri Feb 21, 2014 6:37 pm
by Demivec
rsts wrote:I did not catch that.

Gratitude for your assistance ;)
Your welcome. :)


I forgot to mention what was involved in the change. To properly count a full month of days the line checks for three additional conditions if the simple case fail. The simple case is if the day in the month is the same as the starting day.

Having failed the simple case, the three additional cases are checked for and all need to be met to mark off another month. These conditions are seeing if the starting day is one of the problematic ones (29th, 30th, or 31st), and if the current day is now day 1 (this signals we went from the end of a possibly shorther month to the start of the next month) and finally, if we have counted more than 3 days since the last time the month count was incremented (this last test makes sure the count won't increment unnecessarily for the first few days after a start day of 29, or 30 if this is a long month with 30 or 31 days).