-
Jul 26th, 2011, 06:57 AM
#41
Member
Re: Calculating exact time in Years, MOnths, Days
Hey my friend
Ju can adjust the day month and year how you want now the negative you can remove by splitting again because in this case the number is correct exept the negative.
If you have any questions I like to hear.
If you prefer emailing pietercdevries@gmail.com
-
Jul 26th, 2011, 07:11 AM
#42
Re: Calculating exact time in Years, MOnths, Days
Still doesn't work... it appears to work... but consider the following dates:
2011-02-05 & 2011-03-03 .... by your numbers, that's 1 month (03-02) & 2 days (abs(03-05))... but it isn't... it's 28days... less than a month. your calcs work only under the assumption of 30day months.
consider still
2011-01-30 and 2011-02-05 ... your calc would have it as 1 month (02-01) & 25 days(abs(05-30)). Again, this isn't accurate.
Shall I go on? Try two dates that straddle the new year.
2011-12-15 & 2012-01-15... 1 year (2012-2011) 11 months (abs(01-12)) and 0 days (15-15) ... again...
-tg
-
Jul 26th, 2011, 07:24 AM
#43
Re: Calculating exact time in Years, MOnths, Days
This is how I test age (as years / days OR years / months / days) calculations.
I have a series of past dates and future dates that I use.
Code:
Dim dp() As Date = New Date() {#1/31/2004#, #2/29/2004#, #1/31/2005#}
Dim df() As Date = New Date() {#1/31/2009#, #2/28/2009#, #12/31/2009#}
The basic loop looks like this
Code:
For Each pastDate In dp
For Each futureDate In df
Dim stopAt As Date = futureDate
futureDate = pastDate.AddDays(1)
Do While futureDate <= stopAt
'calculate age then check
futureDate = futureDate.AddDays(1)
Loop
Next
Next
Basic age as years and days check
If days are greater than 365 it is an error.
If the years changes, then the days should be 0.
If the years are the same then the days should be one more than the previous day count.
Basic age as years, months, and days check
If days are greater than 31 it is an error.
If the year changes, then the days and months should be 0.
If the months changes, then the days should be 0.
If the months are the same then the days should be one more than the previous day count.
-
Jul 26th, 2011, 07:29 AM
#44
Re: Calculating exact time in Years, MOnths, Days
Originally Posted by techgnome
Still doesn't work... it appears to work... but consider the following dates:
2011-02-05 & 2011-03-03 .... by your numbers, that's 1 month (03-02) & 2 days (abs(03-05))... but it isn't... it's 28days... less than a month. your calcs work only under the assumption of 30day months.
consider still
2011-01-30 and 2011-02-05 ... your calc would have it as 1 month (02-01) & 25 days(abs(05-30)). Again, this isn't accurate.
Shall I go on? Try two dates that straddle the new year.
2011-12-15 & 2012-01-15... 1 year (2012-2011) 11 months (abs(01-12)) and 0 days (15-15) ... again...
-tg
By my calculation 2011-02-05 & 2011-03-03 is 26 days (and 26 days when I use a calendar and count the days manually).
I didn't test pauls last submission because it did not lend itself to being put into my test rig.
-
Jul 26th, 2011, 08:09 AM
#45
Re: Calculating exact time in Years, MOnths, Days
So I figured out how to check pauls code using my test rig. Here is the first error sequence
0 years, 0 months, 28 days between 1/31/2004 2/28/2004
0 years, 0 months, 29 days between 1/31/2004 2/29/2004 <<<<<<<<<<<<<<<<<<<< s/b 1 month
0 years, 0 months, 30 days between 1/31/2004 3/1/2004 <<<<<<<<<<<<<<<<<<<< s/b 1 month 1 day
0 years, 1 months, 2 days between 1/31/2004 3/2/2004
and another
5 years, 0 months, -2 days between 2/29/2004 2/27/2009 A C
5 years, 0 months, -1 days between 2/29/2004 2/28/2009 A C
5 years, 0 months, 0 days between 2/29/2004 3/1/2009 C
5 years, 1 months, -28 days between 2/29/2004 3/2/2009 A C
5 years, 1 months, -27 days between 2/29/2004 3/3/2009 A C
Last edited by dbasnett; Jul 26th, 2011 at 08:55 AM.
-
Jul 26th, 2011, 08:41 AM
#46
Re: Calculating exact time in Years, MOnths, Days
My point was that the calculation that was being proposed of blindly day - day1, month - month1, year - year1 produces wrong results under some cases... enough of a problem to not be a viable solution imho.
-tg
-
Jul 26th, 2011, 08:44 AM
#47
Re: Calculating exact time in Years, MOnths, Days
Also... I'm not sure how you come to the conclusion that the results of Paul's code produces an error sequence... the values seem to reflect what I'd expect... ok... the second sequence isn't right... that's for sure... but the first sequence looks right to me... what's wrong with it?
-tg
-
Jul 26th, 2011, 08:56 AM
#48
Re: Calculating exact time in Years, MOnths, Days
Originally Posted by techgnome
Also... I'm not sure how you come to the conclusion that the results of Paul's code produces an error sequence... the values seem to reflect what I'd expect... ok... the second sequence isn't right... that's for sure... but the first sequence looks right to me... what's wrong with it?
-tg
I should have said
0 years, 0 months, 28 days between 1/31/2004 2/28/2004
0 years, 0 months, 29 days between 1/31/2004 2/29/2004 <<<<<<<<<<<<<<<<<<<< s/b 1 month
0 years, 0 months, 30 days between 1/31/2004 3/1/2004 <<<<<<<<<<<<<<<<<<<< s/b 1 month 1 day
0 years, 1 months, 2 days between 1/31/2004 3/2/2004
my mistake.
-
Jul 26th, 2011, 08:58 AM
#49
Hyperactive Member
Re: Calculating exact time in Years, MOnths, Days
Hi guys,
Here's my attempt at this:
I tried a bunch of the Date Pairs from within this post and it seems to hold it's own.. so far!! Let me know if you can 'break' my function
VB.NET Code:
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Date1 As Date = DateTimePicker1.Value 'Lowest Date
Dim Date2 As Date = DateTimePicker2.Value 'Highest Date
'Use DateDiff for months. (Check the days values to see if an extra month was added when not needed).
Dim Months As Long = DateDiff(DateInterval.Month, Date1, Date2)
If Date2.Day < Date1.Day Then Months -= 1
'Workout years from months
Dim Years As Long = Nothing
If Months >= 12 Then
Do Until Months < 12
Years += 1
Months -= 12
Loop
End If
'Days
Dim Days As Long = Nothing
Dim FirstDay As Long = Date1.Day
Dim SecondDay As Long = Date2.Day
Select Case FirstDay
Case Is < SecondDay
'If the Day from Date1 is less than that of Date2 then we know datediff worked out the months upto the month of Date2.
'Therfore a simple subtraction will get the correct number of days.
Days = SecondDay - FirstDay
Case Is > SecondDay
'If Date1's day is greater than Date2's day then datediff looked at the months upto 1 month prior to Date2's month.
'That being the case, work out how many days are in that month and count the days from Date1's Day to the end of that month.
'Then we can just add the day value of Date2.
Dim MonthBeforeLast As Integer = Date2.Month
Dim Year As Integer = Date2.Year
If MonthBeforeLast = 1 Then
MonthBeforeLast = 12
Year -= 1
Else : MonthBeforeLast -= 1
End If
Dim DaysLeftInMonth As Long = Date.DaysInMonth(Year, MonthBeforeLast) - Date1.Day
Days = Date2.Day + DaysLeftInMonth
Case Is = SecondDay
'If the day values are equal then the number of days will always be 0
Days = 0
End Select
'Display results
Label1.Text = String.Format("Years: {1}{0}Months: {2}{0}Days: {3}{0}", {vbCrLf, Years, Months, Days})
End Sub
End Class
Hope this helps
Jay
Last edited by JayJayson; Jul 26th, 2011 at 09:22 AM.
Reason: Fixed MonthBeforeLast. Didn't deduct 1 year if current month was Jan.
-
Jul 26th, 2011, 09:18 AM
#50
Re: Calculating exact time in Years, MOnths, Days
@jay
0 years, 0 months, 29 days between 1/31/2004 2/29/2004
0 years, 1 months, -1 days between 1/31/2004 3/1/2004 A D
1 years, 0 months, 28 days between 1/31/2004 2/28/2005 C
1 years, 1 months, -2 days between 1/31/2004 3/1/2005 A C
1 years, 1 months, -1 days between 1/31/2004 3/2/2005 A C
1 years, 1 months, 0 days between 1/31/2004 3/3/2005 C
-
Jul 26th, 2011, 09:48 AM
#51
Hyperactive Member
Re: Calculating exact time in Years, MOnths, Days
That was a little quicker than I hoped
I updated the function with something I had overlooked earlier. Can you test it for me now
VB.NET Code:
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Date1 As Date = DateTimePicker1.Value 'Lowest Date
Dim Date2 As Date = DateTimePicker2.Value 'Highest Date
'Use DateDiff for months. (Check the days values to see if an extra month was added when not needed).
Dim Months As Long = DateDiff(DateInterval.Month, Date1, Date2)
If Date2.Day < Date1.Day Then Months -= 1
'Workout years from months
Dim Years As Long = Nothing
If Months >= 12 Then
Do Until Months < 12
Years += 1
Months -= 12
Loop
End If
'Days
Dim Days As Long = Nothing
Dim FirstDay As Long = Date1.Day
Dim SecondDay As Long = Date2.Day
If FirstDay > SecondDay And SecondDay = Date.DaysInMonth(Date2.Year, Date2.Month) And FirstDay = Date.DaysInMonth(Date1.Year, Date1.Month) Then
Months += 1
Days = 0
Else : Select Case FirstDay
Case Is < SecondDay
'If the Day from Date1 is less than that of Date2 then we know datediff worked out the months upto the month of Date2.
'Therfore a simple subtraction will get the correct number of days.
Days = SecondDay - FirstDay
Case Is > SecondDay
'If Date1's day is greater than Date2's day then datediff looked at the months upto 1 month prior to Date2's month.
'That being the case, work out how many days are in that month and count the days from Date1's Day to the end of that month.
'Then we can just add the day value of Date2.
Dim MonthBeforeLast As Integer = Date2.Month
Dim Year As Integer = Date2.Year
If MonthBeforeLast = 1 Then
MonthBeforeLast = 12
Year -= 1
Else : MonthBeforeLast -= 1
End If
'Edited code
Dim DaysLeftInMonth As Long = Date.DaysInMonth(Year, MonthBeforeLast)
If Date1.Day < DaysLeftInMonth Then DaysLeftInMonth -= Date1.Day Else : DaysLeftInMonth = 0
Days = Date2.Day + DaysLeftInMonth
Case Is = SecondDay
'If the day values are equal then the number of days will always be 0
Days = 0
End Select
End If
'Display results
Label1.Text = String.Format("Years: {1}{0}Months: {2}{0}Days: {3}{0}", {vbCrLf, Years, Months, Days})
End Sub
End Class
Last edited by JayJayson; Jul 26th, 2011 at 10:06 AM.
Reason: Edited to make last day in month to last day in month count as a month rather than x days.
-
Jul 26th, 2011, 10:10 AM
#52
Re: Calculating exact time in Years, MOnths, Days
0 years, 0 months, 29 days between 1/31/2004 2/29/2004
0 years, 1 months, 1 days between 1/31/2004 3/1/2004 D
0 years, 2 months, 30 days between 1/31/2005 4/30/2005
0 years, 3 months, 1 days between 1/31/2005 5/1/2005 D
What happened to x months 0 days?
0 years, 5 months, 0 days between 2/29/2004 7/29/2004
0 years, 5 months, 1 days between 2/29/2004 7/30/2004
0 years, 5 months, 2 days between 2/29/2004 7/31/2004
0 years, 5 months, 1 days between 2/29/2004 8/1/2004 D
I posted how I was testing this in Post #42. (Hint)
-
Jul 26th, 2011, 10:17 AM
#53
Hyperactive Member
Re: Calculating exact time in Years, MOnths, Days
I edited the code to fix the x months 0 days after making that last post
Although I can see another problem with this now...
Last edited by JayJayson; Jul 26th, 2011 at 10:26 AM.
-
Jul 26th, 2011, 10:56 AM
#54
Hyperactive Member
Re: Calculating exact time in Years, MOnths, Days
Right ok, I think my latest effort cracked it, fingers crossed
VB.NET Code:
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Date1 As Date = DateTimePicker1.Value 'Lowest Date
Dim Date2 As Date = DateTimePicker2.Value 'Highest Date
'Use DateDiff for months. (Check the days values to see if an extra month was added when not needed).
Dim Months As Long = DateDiff(DateInterval.Month, Date1, Date2)
If Date2.Day < Date1.Day Then Months -= 1
'Workout years from months
Dim Years As Long = Nothing
If Months >= 12 Then
Do Until Months < 12
Years += 1
Months -= 12
Loop
End If
'Days
Dim Days As Long = Nothing
Dim FirstDay As Long = Date1.Day
Dim SecondDay As Long = Date2.Day
'Check for end of the month dates (to be handled differently)
If SecondDay = Date.DaysInMonth(Date2.Year, Date2.Month) And FirstDay = Date.DaysInMonth(Date1.Year, Date1.Month) Then
Days = 0
ElseIf FirstDay = Date.DaysInMonth(Date1.Year, Date1.Month) And SecondDay < Date.DaysInMonth(Date2.Year, Date2.Month) Then
If SecondDay >= FirstDay Then Months -= 1
Days = SecondDay
Else : Select Case FirstDay
'Otherwise just calculate the days ourselves
Case Is < SecondDay
'If the Day from Date1 is less than that of Date2 then we know datediff worked out the months upto the month of Date2.
'Therfore a simple subtraction will get the correct number of days.
Days = SecondDay - FirstDay
Case Is > SecondDay
'If Date1's day is greater than Date2's day then datediff looked at the months upto 1 month prior to Date2's month.
'That being the case, work out how many days are in that month and count the days from Date1's Day to the end of that month.
'Then we can just add the day value of Date2.
Dim MonthBeforeLast As Integer = Date2.Month
Dim Year As Integer = Date2.Year
If MonthBeforeLast = 1 Then
MonthBeforeLast = 12
Year -= 1
Else : MonthBeforeLast -= 1
End If
Dim DaysLeftInMonth As Long = Date.DaysInMonth(Year, MonthBeforeLast) - Date1.Day
Days = Date2.Day + DaysLeftInMonth
Case Is = SecondDay
'If the day values are equal then the number of days will always be 0
Days = 0
End Select
End If
'Display results
Label1.Text = String.Format("Years: {1}{0}Months: {2}{0}Days: {3}{0}", {vbCrLf, Years, Months, Days})
End Sub
End Class
In any case, a better DateDiff function would be nice for the future
-
Jul 26th, 2011, 11:00 AM
#55
Re: Calculating exact time in Years, MOnths, Days
-
Jul 26th, 2011, 11:09 AM
#56
Re: Calculating exact time in Years, MOnths, Days
I don't know what's going on in this thread. If anything it's a painful proof of my assertion that my technique's the best
I count 2 correct results: #2 and #8. After that, 9 attempts to do the same thing other ways were proposed (.paul.'s tried and failed at least 5 times!) I stand my my assertion that my "increment days and update counter" approach is the most accurate way to do this. Everyone who's tried another technique has failed. Do we really need to keep trying?
There's an interesting discussion from #24-#34. Do we call 1/1/2005 to 1/1/2006 1 year exactly? If so, what about 1/1/2004-1/1/2005? 2004 was a leap year so that's technically 366 days. I say that's still 1 year, 0 days. Years are not 365 days every year. Some years they are 366 days. This is why you can only *approximate* days using years \ 365. Perhaps you might encounter some business logic that wants to call that span 1 year and 1 day; you can modify the algorithm accordingly. I don't think "I think you should have one day more for this leap year period" is a bug because in my eyes the behavior is correct.
Originally Posted by .paul.
afraid of a little competition???
Hardly. DateDiff() is objectively awful. Just read the documentation; .NET has entire classes with less documentation than this one little function. The documentation also outlines situations where the return value isn't what you'd expect and in some cases is inaccurate. It takes as long to discover its pitfalls as it does to implement another solution. As further evidence, I present your failed attempts to use it. If an MS Community Contributor with 11k posts and dozens of tutorials under his belt can't use DateDiff() properly, what hope does the average developer have? This is why I retch when I see it.
What I'm afraid of is giving users incorrect advice. Many people copy/paste forum examples and don't bother testing until deployment. That's a dumb approach to coding, but it's out there and odds are I'm going to be affected by it one day. I wanted to dismiss conversation of DateDiff() immediately because any errors involving DateDiff() are usually subtle and complex. Just look at the damage it's done: we've got nearly failed 10 attempts to wrangle DateDiff() into something predictable. You've got 11k posts to my 500 or so; that means you'll usually win the tiebreaker and your posts have more perceived authority. That makes it all the more tragic when your code is incorrect. I was also quite agitated that proposing new solutions implies the previous solutions have a flaw, though none have been raised against my code or dbasnett's. Your code's been attacked because it produces incorrect results. What's wrong with ours? It's odd there's so much discussion about a solved problem.
DateDiff() was designed for a structured programming world. Even in structured programming, it's generally agreed functions with multiple behaviors are confusing. In the OOP world, it would be best represented as many methods of a class: MonthsBetween(), YearsBetween(), etc. I think the noda-time project has some functionality to do this that might present an API example. It's a shame they haven't found the time to write documentation so I could prove it.
Last edited by Sitten Spynne; Jul 26th, 2011 at 11:48 AM.
-
Jul 26th, 2011, 11:59 AM
#57
Re: Calculating exact time in Years, MOnths, Days
As of now there aren't any correct answers, IMHO. Here are some of the results, using your code, from my test (post #42).
0 years, 0 months, 29 days between 1/31/2004 2/29/2004
0 years, 0 months, 30 days between 1/31/2004 3/1/2004
0 years, 0 months, 31 days between 1/31/2004 3/2/2004
0 years, 0 months, 32 days between 1/31/2004 3/3/2004 A
0 years, 0 months, 33 days between 1/31/2004 3/4/2004 A
The problems are boundaries (for everyone as far as I can tell).
If you are in the Doctors office on 1/31/2004 and they say they will see you, in a month or in a year I take it to mean 2/29/2004 and 1/31/2005 respectively.
If you are in the Doctors office on 1/31/2005 and they say they will see you, in a month or in a year I take it to mean 2/28/2005 and 1/31/2006 respectively.
If anyone has issue with how I test, or how I define the boundaries, please let me know. This thread is painful proof that this is hard.
-
Jul 26th, 2011, 12:13 PM
#58
Hyperactive Member
Re: Calculating exact time in Years, MOnths, Days
Using post #42 to test the output, I have tweaked my code to the following:
VB.NET Code:
Public Class Form1
'Date1 = LowestDate, Date2 = Highest Date
Private Sub DateCompare(ByVal Date1 As Date, ByVal date2 As Date)
'Get the Months first
Dim Months As Long = ((date2.Year * 12) + date2.Month) - ((Date1.Year * 12) + Date1.Month) - 1
'Days
Dim Days As Long = Nothing
Dim FirstDay As Long = Date1.Day
Dim SecondDay As Long = date2.Day
'Check for end of the month dates (to be handled differently)
If SecondDay = Date.DaysInMonth(date2.Year, date2.Month) And FirstDay = Date.DaysInMonth(Date1.Year, Date1.Month) Then
Months += 1
Days = 0
ElseIf FirstDay = Date.DaysInMonth(Date1.Year, Date1.Month) And SecondDay < Date.DaysInMonth(date2.Year, date2.Month) Then
If SecondDay >= FirstDay Then Months += 1
Days = SecondDay
Else : Select Case FirstDay
'Otherwise just calculate the days ourselves
Case Is < SecondDay
'If the Day from Date1 is less than that of Date2 then we know datediff worked out the months upto the month of Date2.
'Therfore a simple subtraction will get the correct number of days.
Days = SecondDay - FirstDay
Case Is > SecondDay
'If Date1's day is greater than Date2's day then datediff looked at the months upto 1 month prior to Date2's month.
'That being the case, work out how many days are in that month and count the days from Date1's Day to the end of that month.
'Then we can just add the day value of Date2.
Dim MonthBeforeLast As Integer = date2.Month
Dim Year As Integer = date2.Year
If MonthBeforeLast = 1 Then
MonthBeforeLast = 12
Year -= 1
Else : MonthBeforeLast -= 1
End If
Dim DaysLeftInMonth As Long = Date.DaysInMonth(Year, MonthBeforeLast) - Date1.Day
Days = date2.Day + DaysLeftInMonth
Case Is = SecondDay
'If the day values are equal then the number of days will always be 0
Days = 0
End Select
End If
'Workout years from months
Dim Years As Long = Nothing
If Months >= 12 Then
Do Until Months < 12
Years += 1
Months -= 12
Loop
End If
'Display results
MsgBox(String.Format("Years: {1}{0}Months: {2}{0}Days: {3}{0}", {vbCrLf, Years, Months, Days}))
End Sub
End Class
As far as I can see it gets the desired result every time now
-
Jul 26th, 2011, 12:31 PM
#59
Re: Calculating exact time in Years, MOnths, Days
Here is the code to test with(crude I know). The last attempt by jay did not work.
Code:
Dim dp() As Date = New Date() {#1/31/2004#, #2/29/2004#, #1/31/2005#}
Dim df() As Date = New Date() {#1/31/2009#, #2/28/2009#, #12/31/2009#}
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
Debug.WriteLine("")
Debug.WriteLine("")
Dim pastDate As DateTime
Dim futureDate As DateTime
Dim foo As New Age
Dim sb As New System.Text.StringBuilder
For Each pastDate In dp
Debug.WriteLine("")
For Each futureDate In df
Dim stopAt As Date = futureDate
futureDate = pastDate.AddDays(1)
Dim ly As Integer = 0
Dim ld As Integer = 0
Dim lm As Integer = 0
Dim isErr As String = ""
Do While futureDate <= stopAt
'checks
foo.DateFuture = futureDate
foo.DatePast = pastDate
'PUT YOUR CALL TO CALC HERE
foo = myAgeCalc.AgeInYearsMonthsDays()
'<<<<<<<<<
If foo.Days < 0 OrElse foo.Days > 31 Then
isErr &= "A "
End If
If foo.Months < 0 OrElse foo.Months > 11 Then
isErr &= "B "
End If
If foo.Years <> ly Then
If foo.Years = ly + 1 AndAlso foo.Months = 0 AndAlso foo.Days = 0 Then
ly = foo.Years
ld = 0
lm = 0
Debug.WriteLine("New Year")
Else
isErr &= "C "
End If
ElseIf foo.Months <> lm Then
If foo.Months = lm + 1 AndAlso foo.Days = 0 Then
lm = foo.Months
ld = 0
Else
isErr &= "D "
lm = foo.Months
ld = 0
End If
ElseIf foo.Days <> ld + 1 Then
isErr &= "D "
ld = foo.Days
Else
ld = foo.Days
End If
Debug.Write(String.Format("{0} years, {1} months, {2} days between {3} {4} ", foo.Years, foo.Months, foo.Days, pastDate.ToShortDateString, futureDate.ToShortDateString))
If isErr <> "" Then
Debug.WriteLine(isErr)
sb.AppendLine(isErr & pastDate.ToShortDateString & " " & futureDate.ToShortDateString)
isErr = "" 'for error stop
Else
Debug.WriteLine("")
End If
futureDate = futureDate.AddDays(1)
Loop
Next
Next
If sb.Length <> 0 Then
Stop
'My.Computer.Clipboard.SetText(sb.ToString)
End If
End Sub
Code:
Class Age
Private _Years As Integer
Public Property Years() As Integer
Get
Return Me._Years
End Get
Set(ByVal value As Integer)
Me._Years = value
End Set
End Property
Private _Months As Integer
Public Property Months() As Integer
Get
Return Me._Months
End Get
Set(ByVal value As Integer)
Me._Months = value
End Set
End Property
Private _Days As Integer
Public Property Days() As Integer
Get
Return Me._Days
End Get
Set(ByVal value As Integer)
Me._Days = value
End Set
End Property
Private _DatePast As DateTime
Public Property DatePast() As DateTime
Get
Return Me._DatePast
End Get
Set(ByVal value As DateTime)
Me._DatePast = value
Me.setInterval()
End Set
End Property
Private _DateFuture As DateTime
Public Property DateFuture() As DateTime
Get
Return Me._DateFuture
End Get
Set(ByVal value As DateTime)
Me._DateFuture = value
Me.setInterval()
End Set
End Property
Private Sub setInterval()
Me._Interval = (Me.DateFuture - Me.DatePast).Duration
End Sub
Private _Interval As TimeSpan
Public ReadOnly Property Interval() As TimeSpan
Get
Return Me._Interval
End Get
End Property
End Class
Last edited by dbasnett; Jul 26th, 2011 at 12:37 PM.
-
Jul 26th, 2011, 01:03 PM
#60
Hyperactive Member
Re: Calculating exact time in Years, MOnths, Days
Just curious which dates it failed on?
I will implement your test later as I am on my way out
-
Jul 26th, 2011, 01:53 PM
#61
Re: Calculating exact time in Years, MOnths, Days
I feel like some of the "failed" cases are correct, but it depends on when you want to say "a month has elapsed."
If it's 31 Jan 2004 and my doctor says I need an appointment scheduled in "a month", I wouldn't be surprised by an appointment anywhere between 28 Feb 2004 and anything in March 2004. The semantics are "roughly 30 days should elapse before you see me again." So 29 days is as accurate a description as 1 month, seeing as a month means anything from 28-31 days.
That's not to say it's an unimportant distinction. My banking contracts speak either in terms of days or include an end date for vague concepts like "30-year mortgage". 90 days is unambiguous. 21 October 2042 is unambiguous. "2 months" can mean anything from 59 to 62 days; resolution of the exact period requires the start date. My credit card interest is determined by average daily balance, and every statement explicitly states the number of days in the billing period.
This issue doesn't matter so much in less important scenarios. If my buddy asks for $10 and promises to pay me back "next month", I tend to assume that means some time in the next 30-60 days. Even something precise like "meet me in 5 minutes" has some assumed leeway. For these scenarios, simple estimations will work. "1 month from now" on January 31 can be Feb 28, Feb 29, Mar 1, or any of a handful of other days and most people won't bat an eye. Even if you're calculating a loan term, "1 year 6 months" is close enough for most people to properly visualize 3,842 days.
So I'm not sure if it's ever worth the effort to work out all the kinks. If the software needs to be accurate, then all time spans should be reported in days; anything with years and months should be qualified as an estimate only (and optionally allow display of the exact number of days.) I think we've stumbled upon an explanation for why TimeSpan only goes up to days: days are the largest constant unit of time. (Technically, leap seconds take even that away!)
It's definitely wrong that I calculate 32 days between 31 Jan 2004 and 3 Mar 2004. If the start date is on a 31 day, this error happens in many other places. I might be able to correct it, but I'd rather attack it from a different angle.
Here's a second attempt that gets many boundary cases right; I'll include the beginnings of my test harness so you can see what I tested.
Code:
Function CalculateRange2(ByVal startDate As DateTime, ByVal endDate As DateTime) As TimePeriod
Dim period As New TimePeriod()
Dim currentDate As DateTime = startDate
' Add years until we're at the same year
Do While currentDate.Year <> endDate.Year
currentDate = currentDate.AddYears(1)
If currentDate > endDate Then
' We overshot by some amount; take the year back and exit the loop. This happens when going from say
' 24 Feb 2003 to 1 Jan 2004.
currentDate = currentDate.AddYears(-1)
Exit Do
Else
' We haven't overshot yet; add a year to the reckoning
period.Years += 1
End If
Loop
' Add months until we're at the same month; it's possible we're the same month in a previous year
Do While currentDate.Month <> endDate.Month OrElse currentDate.Year <> endDate.Year
currentDate = currentDate.AddMonths(1)
If currentDate > endDate Then
' Overshot; take the month back and exit.
currentDate = currentDate.AddMonths(-1)
Exit Do
Else
period.Months += 1
End If
Loop
' Add days until we're at the same date
Do Until currentDate.Day = endDate.Day
currentDate = currentDate.AddDays(1)
period.Days += 1
Loop
Return period
End Function
Test cases (I'm sure you can figure out the TestCase class):
Code:
Dim testCases() As TestCase = {New TestCase(#2/28/2004#, #3/1/2004#, New TimePeriod() With {.Days = 2}),
New TestCase(#2/28/2004#, #3/2/2004#, New TimePeriod() With {.Days = 3}),
New TestCase(#2/28/2005#, #3/1/2005#, New TimePeriod() With {.Days = 1}),
New TestCase(#2/28/2005#, #3/2/2005#, New TimePeriod() With {.Days = 2}),
New TestCase(#1/28/2004#, #3/1/2004#, New TimePeriod() With {.Months = 1, .Days = 2}),
New TestCase(#1/28/2005#, #3/1/2005#, New TimePeriod() With {.Months = 1, .Days = 1}),
New TestCase(#2/28/2004#, #2/28/2005#, New TimePeriod() With {.Years = 1}),
New TestCase(#2/28/2005#, #2/28/2006#, New TimePeriod() With {.Years = 1}),
New TestCase(#1/31/2004#, #2/28/2004#, New TimePeriod() With {.Days = 30}),
New TestCase(#1/31/2004#, #2/29/2004#, New TimePeriod() With {.Months = 1}),
New TestCase(#1/31/2005#, #2/28/2005#, New TimePeriod() With {.Months = 1}),
New TestCase(#12/15/2003#, #12/14/2004#, New TimePeriod() With {.Months = 11, .Days = 29}),
New TestCase(#12/15/2003#, #12/16/2004#, New TimePeriod() With {.Years = 1, .Days = 1}),
New TestCase(#12/15/2004#, #12/15/2005#, New TimePeriod() With {.Years = 1}),
New TestCase(#12/15/2004#, #12/14/2005#, New TimePeriod() With {.Months = 11, .Days = 29}),
New TestCase(#12/15/2004#, #12/16/2005#, New TimePeriod() With {.Years = 1, .Days = 1})
}
The most interesting test case in my opinion is 31 Jan 2004 to 28 Feb 2004. Technically, 1 month from the start date is 29 Feb 2004, so rather than the intuitive 1 month span it's actually a 30 day span. This is not true in 2005, where the distance between the two dates is 1 month again. Many people would argue this is *still* a distance of one month. I think both sides are right and business logic should be the deciding factor; if your boss wants it to be a month special case it to a month.
I'm less confident this one is correct in all cases than my original because it's far more complicated. It took me about 45 minutes to get it right, whereas #2 took me 10 minutes. It looks like a reimagining of AgeInYearsMonthsDays() from your post #2, so it's hardly a new idea. And whether it is "correct" still depends on when 28 days counts as a month. I think the hardest part of verifying correctness is the sheer number of edge cases; I'm not sure if I've covered all of them yet.
On #57: it's complicated and less intuitive than an incremental approach; that makes me superstitious it's got hidden errors. My guess is it started simple, then lots of special cases got added. That's why I made a different attempt rather than tweaking #2. Incremental approaches are easier to understand and thus easier to maintain. Others might not share my opinion, but I don't like math-based approach to date differencing. Been burned too many times. I'm too lazy to throw test cases at it; I'm out of break time today.
-
Jul 26th, 2011, 02:47 PM
#62
Re: Calculating exact time in Years, MOnths, Days
@dBasnett
what about this:
Start: 23/08/2008
End: 22/07/2010
Years: 1
Months: 10
Days: 28
should be 1 yr, 10 months, 29 days
but allowing for the 366 day year in 2008 it's wrong.???
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
Jul 26th, 2011, 06:11 PM
#63
Re: Calculating exact time in Years, MOnths, Days
Originally Posted by Sitten Spynne
I don't know what's going on in this thread. If anything it's a painful proof of my assertion that my technique's the best
Originally Posted by dbasnett
As of now there aren't any correct answers, IMHO.
it'd help if Microsoft had a consensus of opinion on this. their methods are all over the place...
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
Jul 26th, 2011, 06:41 PM
#64
Re: Calculating exact time in Years, MOnths, Days
Originally Posted by Sitten Spynne
I think the noda-time project has some functionality to do this that might present an API example. It's a shame they haven't found the time to write documentation so I could prove it.
It's also a shame they haven't reached a stable release either, as that precludes it from most production code. My first thought yesterday when reading this thread was "Ah! Use Noda Time!", only to find they're still a little way off a 1.0 release.
Originally Posted by Sitten Spynne
I think both sides are right and business logic should be the deciding factor; if your boss wants it to be a month special case it to a month.
I'm slightly disappointed it took 60 posts before someone threw in that phrase. The requirements for this seem very simple but they hide a huge number of edge and corner cases where we need clarification on the desired behaviour. Yes, I have an idea of what makes sense to me, but that doesn't mean my idea of the obvious behaviour in those cases is correct for this application. (And as to why I haven't posted the code for my answer? I got bored after the fifth test case )
The real answer here, IMO, is "That's hard to get correct in all cases, are you sure you can't just have the number of days, or just display the start and end date?"
-
Jul 26th, 2011, 06:41 PM
#65
Hyperactive Member
Re: Calculating exact time in Years, MOnths, Days
I have taken a leaf out of SittenSpynne's book and rewritten my code from the beggining. Although it is mostly the same, it helped me find a bug and lay the code out in an easier to follow way.
I've tested it with a bunch of dates and so far it seems to be working as it should be (or at least according to the 'rules' laid out by dbasnett, which I am onboard with).
Let's hope none of you manage to find a date pair to ruin my moment of self gratifaction
Here's the code:
VB.NET Code:
Public Class Form1
'Date1 is the lowest date, Date2 is the highest date
Private Sub DateCompare(ByVal Date1 As Date, ByVal Date2 As Date)
Dim Days As Integer = Nothing
Dim Months As Integer = Nothing
Dim Years As Integer = Nothing
'Convert Date1/Date2 months and years into months.
Dim Date1Months As Integer = Date1.Year * 12 + Date1.Month
Dim Date2Months As Integer = Date2.Year * 12 + Date2.Month
'Get the number of Months between the two dates. Subtract 1 (we will add this back later where necessary)
Months = Date2Months - Date1Months - 1
'See how many days are within the Month Date1/Date2 reside.
Dim DaysInDate1Month As Integer = Date.DaysInMonth(Date1.Year, Date1.Month)
Dim DaysInDate2Month As Integer = Date.DaysInMonth(Date2.Year, Date2.Month)
If Date1.Day = DaysInDate1Month And Date2.Day = DaysInDate2Month Then
'If Both Date1 and Date2 occur on the LastDayOfTheMonth.
'We count this as a month rather than x amount of days.
'Therefore add 1 to Months and set Days to zero.
'nb: We subtracted a month when we first grabbed the number of Months. Add it back here.
Months += 1
Days = 0
ElseIf Date1.Day = DaysInDate1Month And Date2.Day < DaysInDate2Month Then
'If Date1 occur's on the LastDayOfTheMonth but Date2 resides on a day prior to the end of it's month.
'We know the number of Days between the two dates will be the number of Days in Date2.
Days = Date2.Day
Else
'If Neither Date1 or Date2 occur on the LastDayOfTheMonth then we can do a day count ourselves.
Select Case Date1.Day
Case Is < Date2.Day
'If Date2's Day is higher then Date1's Day then the 'Months' value will represent the number of months upto the Month that Date2 resides.
'The number of days will therefore be the difference between Date1/Date2's Day values.
Days = Date2.Day - Date1.Day
'nb: We subtracted a month when we first grabbed the number of Months. Add it back here.
Months += 1
Case Is > Date2.Day
'If Date2's Day is lower then Date1's Day then the 'Months' value will represent the number of months upto the Month prior to that which Date2 resides.
'First we need to find out the month prior to Date2's month value.
Dim MonthBeforeDate2 As Integer = Date2.Month
'If Date2 occurs in January, then we will also need to use the previous year in our calculation.
Dim Date2Year As Integer = Date2.Year
If MonthBeforeDate2 = 1 Then
MonthBeforeDate2 = 12
Date2Year -= 1
Else : MonthBeforeDate2 -= 1
End If
'Next we will see how many days occur between the value of Date1's Day and the end of the month prior to Date2.
Dim DaysLeftInMonthBeforeDate2 As Long = Date.DaysInMonth(Date2Year, MonthBeforeDate2) - Date1.Day
'Finally we can add the number of Days in Date2.
Days = DaysLeftInMonthBeforeDate2 + Date2.Day
Case Is = Date2.Day
'If the Day value in Date1/Date2 matches.
'nb: We subtracted a month when we first grabbed the number of Months. Add it back here.
Months += 1
End Select
End If
'The last step is to work out the number of Years. We can work this out from the 'Months'.
'nb: We left this until last because there may have been a few neccessary changes to the Months value along the way.
If Months >= 12 Then
Do Until Months < 12
Years += 1
Months -= 12
Loop
End If
'Now just format the result:
MsgBox(String.Format("Years: {1}{0}Months: {2}{0}Days: {3}", {vbCrLf, Years, Months, Days}))
End Sub
End Class
-
Jul 26th, 2011, 06:46 PM
#66
Re: Calculating exact time in Years, MOnths, Days
Originally Posted by jay20aiii
I have taken a leaf out of SittenSpynne's book and rewritten my code from the beggining.
i haven't tested your code, but if you had taken a leaf out of SittenSpynne's book you'd have just written an essay + no code
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
Jul 26th, 2011, 06:59 PM
#67
Hyperactive Member
Re: Calculating exact time in Years, MOnths, Days
I could paste some Wikipedia excerpts if that would help
-
Jul 26th, 2011, 07:30 PM
#68
Re: Calculating exact time in Years, MOnths, Days
Originally Posted by .paul.
i haven't tested your code, but if you had taken a leaf out of SittenSpynne's book you'd have just written an essay + no code
I think a leaf from SittenSpynne's book would be more like "post some pretty robust code, then discuss why it's robust and the thought processes behind making it robust so that next time, the people asking for help might be able to get a bit further by themselves."
Compare that with a leaf from another person's book:
"Post some code that seems to work when given a cursory test with some really easy values. When it is pointed out the code has flaws, tweak the code to handle each bug. Hope and pray you don't introduce new bugs with the changes, or regress previously working cases. Give up and then try and suggest SittenSpynne is the one posturing."
I know whose book I'd rather be reading.
-
Jul 26th, 2011, 07:36 PM
#69
Re: Calculating exact time in Years, MOnths, Days
Originally Posted by Evil_Giraffe
I think a leaf from SittenSpynne's book would be more like "post some pretty robust code, then discuss why it's robust and the thought processes behind making it robust so that next time, the people asking for help might be able to get a bit further by themselves."
Compare that with a leaf from another person's book:
"Post some code that seems to work when given a cursory test with some really easy values. When it is pointed out the code has flaws, tweak the code to handle each bug. Hope and pray you don't introduce new bugs with the changes, or regress previously working cases. Give up and then try and suggest SittenSpynne is the one posturing."
I know whose book I'd rather be reading.
hmmm...
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
Jul 26th, 2011, 07:39 PM
#70
Re: Calculating exact time in Years, MOnths, Days
Originally Posted by Evil_Giraffe
I think a leaf from SittenSpynne's book would be more like "post some pretty robust code, then discuss why it's robust and the thought processes behind making it robust so that next time, the people asking for help might be able to get a bit further by themselves."
Compare that with a leaf from another person's book:
"Post some code that seems to work when given a cursory test with some really easy values. When it is pointed out the code has flaws, tweak the code to handle each bug. Hope and pray you don't introduce new bugs with the changes, or regress previously working cases. Give up and then try and suggest SittenSpynne is the one posturing."
I know whose book I'd rather be reading.
you should obviously be made aware that SittenSpynne's code doesn't work reliably either...
check the history. my reference to SittenSpynne's essays was based on his past performance.
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
Jul 26th, 2011, 07:41 PM
#71
Re: Calculating exact time in Years, MOnths, Days
@Evil Giraffe. see post#62
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
Jul 26th, 2011, 08:01 PM
#72
Re: Calculating exact time in Years, MOnths, Days
@.paul.
Apologies, my last post was incredibly snarky towards you, and it looks like it's derailed the thread into mud-slinging - for which I apologise to everyone involved. If you want to continue that discussion (I'm not particularly keen to, but since I started it...) PM me and we'll take it off-thread.
Sorry again, everyone.
-
Jul 26th, 2011, 08:13 PM
#73
Hyperactive Member
Re: Calculating exact time in Years, MOnths, Days
I managed to find a date that makes my code in post #64 redundant. Better to find the bug myself I guess... but way to soil in my own sand.
Back to the drawing board I guess
-
Jul 27th, 2011, 06:59 AM
#74
Re: Calculating exact time in Years, Months, Days
What sitten had to say was needed for perspective. This thread was started with this, "I am trying to calculate the exact amount of time in Years, Months and Days between 2 date values." We have all found out how incredibly hard to know what that means, and how hard it is to do with any consistency.
Personal attacks don't have a place here, no matter how frustrated you are.
-
Jul 27th, 2011, 07:39 AM
#75
Re: Calculating exact time in Years, MOnths, Days
Success!
0 years, 0 months, 28 days between 2/29/2004 3/28/2004
0 years, 1 months, 0 days between 2/29/2004 3/29/2004
0 years, 1 months, 30 days between 2/29/2004 4/28/2004
0 years, 2 months, 0 days between 2/29/2004 4/29/2004
0 years, 11 months, 30 days between 2/29/2004 2/28/2005
1 years, 0 months, 0 days between 2/29/2004 3/1/2005
0 years, 0 months, 28 days between 1/31/2004 2/28/2004
0 years, 1 months, 0 days between 1/31/2004 2/29/2004
0 years, 2 months, 29 days between 1/31/2004 4/29/2004
0 years, 3 months, 0 days between 1/31/2004 4/30/2004
0 years, 1 months, 0 days between 1/31/2005 2/28/2005
0 years, 1 months, 1 days between 1/31/2005 3/1/2005
2 years, 0 months, 29 days between 1/29/2005 2/27/2007
2 years, 1 months, 0 days between 1/29/2005 2/28/2007
2 years, 1 months, 1 days between 1/29/2005 3/1/2007
3 years, 0 months, 30 days between 1/29/2005 2/28/2008
3 years, 1 months, 0 days between 1/29/2005 2/29/2008
Last edited by dbasnett; Jul 27th, 2011 at 07:47 AM.
-
Jul 27th, 2011, 07:49 AM
#76
Hyperactive Member
Re: Calculating exact time in Years, MOnths, Days
congrats dbasnett, can we see the code your referring to?
The main reason I have been following this thread is because I made a datediff function years ago to work out my son's age in Years, Months, Weeks, Days and would like to update it with a more robust version
-
Jul 27th, 2011, 08:01 AM
#77
Re: Calculating exact time in Years, MOnths, Days
Originally Posted by jay20aiii
congrats dbasnett, can we see the code your referring to?
The main reason I have been following this thread is because I made a datediff function years ago to work out my son's age in Years, Months, Weeks, Days and would like to update it with a more robust version
As you might imagine the code is a mess at this point. Let me clean it up and I'll post it.
Weeks!
-
Jul 27th, 2011, 08:26 AM
#78
Re: Calculating exact time in Years, MOnths, Days
Still needs work but the code is here
http://www.vbforums.com/showpost.php...40&postcount=2
Sitten was right about keeping the date manipulation as simple as possible.
-
Jul 27th, 2011, 08:52 AM
#79
Hyperactive Member
Re: Calculating exact time in Years, MOnths, Days
Cool, I will take a look over it
I think I have just managed to irradicate the last of the issues with my code. So here it is as well. A bit of competition never hurts and hopefully that will make 2 working DateDiff functions!
VB.NET Code:
Public Class Form1
'Date1 is the lowest date, Date2 is the highest date
Private Sub DateCompare(ByVal Date1 As Date, ByVal Date2 As Date)
Dim Days As Integer = Nothing
Dim Months As Integer = Nothing
Dim Years As Integer = Nothing
'Convert Date1/Date2 months and years into months.
Dim Date1Months As Integer = Date1.Year * 12 + Date1.Month
Dim Date2Months As Integer = Date2.Year * 12 + Date2.Month
'Get the number of Months between the two dates. Subtract 1 (we will add this back later where necessary)
Months = Date2Months - Date1Months - 1
'See how many days are within the Month Date1/Date2 reside.
Dim DaysInDate1Month As Integer = Date.DaysInMonth(Date1.Year, Date1.Month)
Dim DaysInDate2Month As Integer = Date.DaysInMonth(Date2.Year, Date2.Month)
If Date1.Day = DaysInDate1Month And Date2.Day = DaysInDate2Month Then
'If Both Date1 and Date2 occur on the LastDayOfTheMonth.
'We count this as a month rather than x amount of days.
'Therefore add 1 to Months and set Days to zero.
'nb: We subtracted a month when we first grabbed the number of Months. Add it back here.
Months += 1
Days = 0
ElseIf Date1.Day = DaysInDate1Month And Date2.Day < DaysInDate2Month Then
'If Date1 occur's on the LastDayOfTheMonth but Date2 resides on a day prior to the end of it's month.
'We know the number of Days between the two dates will be the number of Days in Date2.
Days = Date2.Day
Else
'If Neither Date1 or Date2 occur on the LastDayOfTheMonth then we can do a day count ourselves.
Select Case Date1.Day
Case Is < Date2.Day
'If Date2's Day is higher then Date1's Day then the 'Months' value will represent the number of months upto the Month that Date2 resides.
'The number of days will therefore be the difference between Date1/Date2's Day values.
Days = Date2.Day - Date1.Day
'nb: We subtracted a month when we first grabbed the number of Months. Add it back here.
Months += 1
Case Is > Date2.Day
'If Date2's Day is lower then Date1's Day then the 'Months' value will represent the number of months upto the Month prior to that which Date2 resides.
'First we need to find out the month prior to Date2's month value.
Dim MonthBeforeDate2 As Integer = Date2.Month
'If Date2 occurs in January, then we will also need to use the previous year in our calculation.
Dim Date2Year As Integer = Date2.Year
If MonthBeforeDate2 = 1 Then
MonthBeforeDate2 = 12
Date2Year -= 1
Else : MonthBeforeDate2 -= 1
End If
'If there are more days in Date1 than occur in the month prior to Date2 then we want to avoid calculating the days that remain in that month.
'As that would lead to a neagtive value.
If Date1.Day > Date.DaysInMonth(Date2Year, MonthBeforeDate2) Then
'In this case we only need the number of days in Date2.
Days = Date2.Day
Else
'Otherwise next we will see how many days occur between the value of Date1's Day and the end of the month prior to Date2.
Dim DaysLeftInMonthBeforeDate2 As Long = Date.DaysInMonth(Date2Year, MonthBeforeDate2) - Date1.Day
'Finally we can add the number of Days in Date2.
Days = DaysLeftInMonthBeforeDate2 + Date2.Day
End If
Case Is = Date2.Day
'If the Day value in Date1/Date2 matches.
'nb: We subtracted a month when we first grabbed the number of Months. Add it back here.
Months += 1
End Select
End If
'The last step is to work out the number of Years. We can work this out from the 'Months'.
'nb: We left this until last because there may have been a few neccessary changes to the Months value along the way.
If Months >= 12 Then
Do Until Months < 12
Years += 1
Months -= 12
Loop
End If
'Now just format the result:
MsgBox(String.Format("Years: {1}{0}Months: {2}{0}Days: {3}", {vbCrLf, Years, Months, Days}))
End Sub
End Class
Now, about those weeks...
-
Jul 27th, 2011, 09:19 AM
#80
Re: Calculating exact time in Years, MOnths, Days
Originally Posted by jay20aiii
Cool, I will take a look over it ....Now, about those weeks...
When you look it over you might find weeks.
BTW - IMHO this is an error
0 years, 0 months, 30 days between 1/29/2005 2/28/2005
0 years, 1 months, 1 days between 1/29/2005 3/1/2005
Last edited by dbasnett; Jul 27th, 2011 at 09:30 AM.
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|