PureBasic Forum
http://forums.purebasic.com/english/

Calculate accurate differences between date/times
http://forums.purebasic.com/english/viewtopic.php?f=12&t=25085
Page 1 of 1

Author:  netmaestro [ Thu Dec 21, 2006 8:13 pm ]
Post subject:  Calculate accurate differences between date/times

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:
;===================================================
; 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)

Author:  Tranquil [ Thu Dec 21, 2006 9:44 pm ]
Post subject: 

I thought about this methode too but there must be a way without doing it in loops or?

Author:  netmaestro [ Fri Dec 22, 2006 1:22 am ]
Post subject: 

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/viewtopic.php?t=24957&start=3

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

Author:  PB [ Fri Dec 22, 2006 8:48 am ]
Post subject: 

> 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? :)

Author:  netmaestro [ Fri Dec 22, 2006 12:59 pm ]
Post subject: 

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"

Author:  jear [ Fri Dec 22, 2006 1:12 pm ]
Post subject: 

What's about this code
Code:
; 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())

Author:  rsts [ Fri Dec 22, 2006 3:51 pm ]
Post subject: 

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

Author:  rsts [ Thu Feb 20, 2014 8:30 pm ]
Post subject:  Re: Calculate accurate differences between date/times

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

Author:  Demivec [ Thu Feb 20, 2014 8:44 pm ]
Post subject:  Re: Calculate accurate differences between date/times

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?

Author:  rsts [ Thu Feb 20, 2014 8:51 pm ]
Post subject:  Re: Calculate accurate differences between date/times

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 :)

Author:  Demivec [ Thu Feb 20, 2014 10:41 pm ]
Post subject:  Re: Calculate accurate differences between date/times

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.

Author:  rsts [ Thu Feb 20, 2014 11:25 pm ]
Post subject:  Re: Calculate accurate differences between date/times

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

Author:  Demivec [ Fri Feb 21, 2014 4:44 pm ]
Post subject:  Re: Calculate accurate differences between date/times

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:
If Day(curdate) = startday

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

Author:  rsts [ Fri Feb 21, 2014 6:11 pm ]
Post subject:  Re: Calculate accurate differences between date/times

I did not catch that.

Gratitude for your assistance ;)

Author:  Demivec [ Fri Feb 21, 2014 6:37 pm ]
Post subject:  Re: Calculate accurate differences between date/times

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).

Page 1 of 1 All times are UTC + 1 hour
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/