www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - The difference between the dates in years

reply Alexander Zhirov <azhirov1991 gmail.com> writes:
Is it possible to calculate the difference between dates in years 
using regular means? Something like that


```
writeln(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)));
```

At the same time, keep in mind that the month and day matter, 
because the difference between the year, taking into account the 
month that has not come, will be less.

My abilities are not yet enough to figure it out more elegantly.
Feb 10
next sibling parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Sat, Feb 10, 2024 at 03:53:09PM +0000, Alexander Zhirov via
Digitalmars-d-learn wrote:
 Is it possible to calculate the difference between dates in years
 using regular means? Something like that
 
 
 ```
 writeln(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)));
 ```
 
 At the same time, keep in mind that the month and day matter, because
 the difference between the year, taking into account the month that
 has not come, will be less.
 
 My abilities are not yet enough to figure it out more elegantly.
IIRC you can just subtract two DateTime's to get a Duration that you can then convert into whatever units you want. Only thing is, in this case conversion to months may not work because months don't have a fixed duration (they can vary from 28 days to 31 days) so there is no "correct" way of computing it, you need to program it yourself according to the exact calculation you want. T -- PNP = Plug 'N' Pray
Feb 10
parent reply Alexander Zhirov <azhirov1991 gmail.com> writes:
On Saturday, 10 February 2024 at 16:03:32 UTC, H. S. Teoh wrote:
 On Sat, Feb 10, 2024 at 03:53:09PM +0000, Alexander Zhirov via 
 Digitalmars-d-learn wrote:
 Is it possible to calculate the difference between dates in 
 years using regular means? Something like that
 
 
 ```
 writeln(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)));
 ```
 
 At the same time, keep in mind that the month and day matter, 
 because the difference between the year, taking into account 
 the month that has not come, will be less.
 
 My abilities are not yet enough to figure it out more 
 elegantly.
IIRC you can just subtract two DateTime's to get a Duration that you can then convert into whatever units you want. Only thing is, in this case conversion to months may not work because months don't have a fixed duration (they can vary from 28 days to 31 days) so there is no "correct" way of computing it, you need to program it yourself according to the exact calculation you want. T
I did it this way, but there will be an inaccuracy, since somewhere there may be 366 days, and somewhere 365 ``` auto d = cast(Date)Clock.currTime() - Date.fromISOExtString("2001-02-01"); writeln(d.total!"days"().to!int / 365); ```
Feb 10
parent reply Alexander Zhirov <azhirov1991 gmail.com> writes:
On Saturday, 10 February 2024 at 18:12:07 UTC, Alexander Zhirov 
wrote:
 On Saturday, 10 February 2024 at 16:03:32 UTC, H. S. Teoh wrote:
 On Sat, Feb 10, 2024 at 03:53:09PM +0000, Alexander Zhirov via 
 Digitalmars-d-learn wrote:
 Is it possible to calculate the difference between dates in 
 years using regular means? Something like that
 
 
 ```
 writeln(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)));
 ```
 
 At the same time, keep in mind that the month and day matter, 
 because the difference between the year, taking into account 
 the month that has not come, will be less.
 
 My abilities are not yet enough to figure it out more 
 elegantly.
IIRC you can just subtract two DateTime's to get a Duration that you can then convert into whatever units you want. Only thing is, in this case conversion to months may not work because months don't have a fixed duration (they can vary from 28 days to 31 days) so there is no "correct" way of computing it, you need to program it yourself according to the exact calculation you want. T
I did it this way, but there will be an inaccuracy, since somewhere there may be 366 days, and somewhere 365 ``` auto d = cast(Date)Clock.currTime() - Date.fromISOExtString("2001-02-01"); writeln(d.total!"days"().to!int / 365); ```
``` int getDiffYears(string date) { auto currentDate = cast(Date)Clock.currTime(); auto startDate = Date.fromISOExtString(date); auto currentDay = currentDate.dayOfYear; auto startDay = startDate.dayOfYear; auto currentMonth = currentDate.month; auto startMonth = startDate.month; auto diffDays = currentDate - startDate; auto diffYears = diffDays.total!"days"().to!int / 365; return currentMonth >= startMonth && currentDay >= startDay ? diffYears : diffYears - 1; } ```
Feb 10
parent matheus <matheus gmail.com> writes:
On Saturday, 10 February 2024 at 19:16:35 UTC, Alexander Zhirov 
wrote:
 ...
Maybe this will help: I think if you will divide it can't be 365, but 365.242199. About your code: I having tested fully, but I found a few problems and I wrote (Again without further tests) as below: import std; int getDiffYears(string date) { auto currentDate = cast(Date)Clock.currTime(); auto startDate = Date.fromISOExtString(date); auto currentDay = currentDate.dayOfYear; auto startDay = startDate.dayOfYear; auto currentMonth = currentDate.month; auto startMonth = startDate.month; auto diffDays = currentDate - startDate; auto diffYears = diffDays.total!"days"().to!int / 365; return currentMonth >= startMonth && currentDay >= startDay ? diffYears : diffYears - 1; } int getDiffYears2(string date) { auto now = cast(Date)Clock.currTime(); auto from = Date.fromISOExtString(date); auto dy = now.year-from.year; auto dm = (from.month-now.month); auto dd = (dm == 0 && from.day>now.day); return dy - 1 * (dm > 0 || dd); } void main(){ writeln("2001-02-09 is ", getDiffYears("2001-02-09")); writeln("2001-02-10 is ", getDiffYears("2001-02-10")); writeln("2001-02-11 is ", getDiffYears("2001-02-11")); writeln("2001-03-01 is ", getDiffYears("2001-03-01")); writeln(""); writeln("2001-02-09 is ", getDiffYears2("2001-02-09")); writeln("2001-02-10 is ", getDiffYears2("2001-02-10")); writeln("2001-02-11 is ", getDiffYears2("2001-02-11")); writeln("2001-03-01 is ", getDiffYears2("2001-03-01")); } Output: 2001-02-09 is 23 2001-02-10 is 23 2001-02-11 is 22 2001-03-01 is 21 2001-02-09 is 23 2001-02-10 is 23 2001-02-11 is 22 2001-03-01 is 22 Matheus.
Feb 10
prev sibling next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Saturday, February 10, 2024 8:53:09 AM MST Alexander Zhirov via 
Digitalmars-d-learn wrote:
 Is it possible to calculate the difference between dates in years
 using regular means? Something like that


 ```
 writeln(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)));
 ```

 At the same time, keep in mind that the month and day matter,
 because the difference between the year, taking into account the
 month that has not come, will be less.

 My abilities are not yet enough to figure it out more elegantly.
Well, diffMonths could be used to build what you want, but std.datetime doesn't really provide a direct solution for it. Subtracting one Date from another gives you a Duration, but that doesn't take the month or year into account, because the lengths of the months are variable. diffMonths is the solution to get around that, since it does take the exact years and months involved to give you the number of months. However, it doesn't take the days of the month into account. It's just diffing the months themselves, and any day of the month counts as being part of that month. However, after some mucking around, I think that I have a solution built on top of diffMonths, though of course, it's possible that I screwed up somewhere with my test dates. I named it yearsApart rather than diffYears, since unlike diffMonths, it does take the smaller units into account. But of course, you can name it whatever you want. import std.datetime.date : Date; int yearsApart(Date lhs, Date rhs) { auto months = lhs.diffMonths(rhs); auto years = months / 12; if(years == 0) return 0; auto remainder = months % 12; if(remainder != 0) return years; if(months >= 0) return lhs.day >= rhs.day ? years : years - 1; return lhs.day <= rhs.day ? years : years + 1; } unittest { assert(yearsApart(Date(1999, 3, 1), Date(1999, 1, 1)) == 0); assert(yearsApart(Date(1999, 1, 1), Date(1999, 3, 1)) == 0); assert(yearsApart(Date(1999, 3, 1), Date(1998, 1, 1)) == 1); assert(yearsApart(Date(1998, 3, 1), Date(1999, 1, 1)) == 0); assert(yearsApart(Date(2000, 12, 1), Date(1995, 12, 1)) == 5); assert(yearsApart(Date(1995, 12, 1), Date(2000, 12, 1)) == -5); assert(yearsApart(Date(2000, 12, 2), Date(1995, 12, 1)) == 5); assert(yearsApart(Date(1995, 12, 1), Date(2000, 12, 2)) == -5); assert(yearsApart(Date(2000, 12, 1), Date(1995, 12, 2)) == 4); assert(yearsApart(Date(1995, 12, 2), Date(2000, 12, 1)) == -4); assert(yearsApart(Date(2000, 2, 29), Date(1999, 2, 28)) == 1); assert(yearsApart(Date(1999, 2, 28), Date(2000, 2, 29)) == -1); assert(yearsApart(Date(2000, 2, 29), Date(1999, 3, 1)) == 0); assert(yearsApart(Date(1999, 3, 1), Date(2000, 2, 29)) == 0); assert(yearsApart(Date(2000, 2, 29), Date(1998, 3, 1)) == 1); assert(yearsApart(Date(1998, 3, 1), Date(2000, 2, 29)) == -1); assert(yearsApart(Date(2001, 3, 1), Date(2000, 2, 29)) == 1); assert(yearsApart(Date(2000, 2, 29), Date(2001, 3, 1)) == -1); assert(yearsApart(Date(2005, 3, 1), Date(2000, 2, 29)) == 5); assert(yearsApart(Date(2004, 3, 1), Date(2000, 2, 29)) == 4); assert(yearsApart(Date(2003, 3, 1), Date(2000, 2, 29)) == 3); assert(yearsApart(Date(2002, 3, 1), Date(2000, 2, 29)) == 2); assert(yearsApart(Date(2001, 3, 1), Date(2000, 2, 29)) == 1); assert(yearsApart(Date(2000, 3, 1), Date(2000, 2, 29)) == 0); assert(yearsApart(Date(2000, 2, 29), Date(2001, 3, 1)) == -1); assert(yearsApart(Date(2000, 2, 29), Date(2002, 3, 1)) == -2); assert(yearsApart(Date(2000, 2, 29), Date(2003, 3, 1)) == -3); assert(yearsApart(Date(2000, 2, 29), Date(2004, 3, 1)) == -4); assert(yearsApart(Date(2000, 2, 29), Date(2005, 3, 1)) == -5); assert(yearsApart(Date(2005, 2, 28), Date(2000, 2, 29)) == 4); assert(yearsApart(Date(2004, 2, 29), Date(2000, 2, 29)) == 4); assert(yearsApart(Date(2004, 2, 28), Date(2000, 2, 29)) == 3); assert(yearsApart(Date(2003, 2, 28), Date(2000, 2, 29)) == 2); assert(yearsApart(Date(2002, 2, 28), Date(2000, 2, 29)) == 1); assert(yearsApart(Date(2001, 2, 28), Date(2000, 2, 29)) == 0); assert(yearsApart(Date(2000, 2, 29), Date(2000, 2, 29)) == 0); assert(yearsApart(Date(2000, 2, 28), Date(2000, 2, 29)) == 0); assert(yearsApart(Date(2000, 2, 29), Date(2000, 2, 28)) == 0); assert(yearsApart(Date(2000, 2, 29), Date(2000, 2, 29)) == 0); assert(yearsApart(Date(2000, 2, 29), Date(2001, 2, 28)) == 0); assert(yearsApart(Date(2000, 2, 29), Date(2002, 2, 28)) == -1); assert(yearsApart(Date(2000, 2, 29), Date(2003, 2, 28)) == -2); assert(yearsApart(Date(2000, 2, 29), Date(2004, 2, 28)) == -3); assert(yearsApart(Date(2000, 2, 29), Date(2004, 2, 29)) == -4); assert(yearsApart(Date(2000, 2, 29), Date(2005, 2, 28)) == -4); } - Jonathan M Davis
Feb 10
prev sibling next sibling parent reply Lance Bachmeier <no spam.net> writes:
On Saturday, 10 February 2024 at 15:53:09 UTC, Alexander Zhirov 
wrote:
 Is it possible to calculate the difference between dates in 
 years using regular means? Something like that


 ```
 writeln(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)));
 ```

 At the same time, keep in mind that the month and day matter, 
 because the difference between the year, taking into account 
 the month that has not come, will be less.

 My abilities are not yet enough to figure it out more elegantly.
I'm assuming you mean you want the number of full years between the dates. If so, I use something like this: ``` import std; void main() { writeln(fullYears(Date(1999, 3, 1), Date(1999, 2, 1))); writeln(fullYears(Date(2000, 3, 1), Date(1999, 2, 1))); writeln(fullYears(Date(2000, 3, 1), Date(1999, 4, 1))); writeln(fullYears(Date(2006, 4, 1), Date(1999, 4, 1))); } bool earlierInYear(Date date1, Date date2) { return date1 < Date(date1.year, date2.month, date2.day); } long fullYears(Date date1, Date date2) { assert(date1 >= date2, "The first date has to be later"); if (date1.earlierInYear(date2)) { return max(date1.year - date2.year, 0); } else { return date1.year - date2.year; } } ```
Feb 10
parent Lance Bachmeier <no spam.net> writes:
On Saturday, 10 February 2024 at 21:56:30 UTC, Lance Bachmeier 
wrote:
 On Saturday, 10 February 2024 at 15:53:09 UTC, Alexander Zhirov 
 wrote:
 Is it possible to calculate the difference between dates in 
 years using regular means? Something like that


 ```
 writeln(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)));
 ```

 At the same time, keep in mind that the month and day matter, 
 because the difference between the year, taking into account 
 the month that has not come, will be less.

 My abilities are not yet enough to figure it out more 
 elegantly.
I'm assuming you mean you want the number of full years between the dates. If so, I use something like this: ``` import std; void main() { writeln(fullYears(Date(1999, 3, 1), Date(1999, 2, 1))); writeln(fullYears(Date(2000, 3, 1), Date(1999, 2, 1))); writeln(fullYears(Date(2000, 3, 1), Date(1999, 4, 1))); writeln(fullYears(Date(2006, 4, 1), Date(1999, 4, 1))); } bool earlierInYear(Date date1, Date date2) { return date1 < Date(date1.year, date2.month, date2.day); } long fullYears(Date date1, Date date2) { assert(date1 >= date2, "The first date has to be later"); if (date1.earlierInYear(date2)) { return max(date1.year - date2.year, 0); } else { return date1.year - date2.year; } } ```
should be ``` long fullYears(Date date1, Date date2) { assert(date1 >= date2, "The first date has to be later"); if (date1.earlierInYear(date2)) { return max(date1.year - date2.year - 1, 0); } else { return date1.year - date2.year; } } ```
Feb 10
prev sibling next sibling parent reply Brad Roberts <braddr puremagic.com> writes:
Back when I was doing lots of software developer interviews, one of my 
frequent questions involved date math.  This wasn't because it's 
difficult from a coding standpoint, but that it's NOT a coding problem. 
The key part of the question is realization that it's a requirements 
question.  The thing that makes dates complicated is defining what the 
question actually is.

The topic _seems_ like it should be simple, but the deeper you dig the 
more you realize it's anything but simple.

On 2/10/2024 7:53 AM, Alexander Zhirov via Digitalmars-d-learn wrote:
 Is it possible to calculate the difference between dates in years using 
 regular means? Something like that
 
 
 ```
 writeln(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)));
 ```
 
 At the same time, keep in mind that the month and day matter, because 
 the difference between the year, taking into account the month that has 
 not come, will be less.
 
 My abilities are not yet enough to figure it out more elegantly.
Feb 10
parent reply matheus <matheus gmail.com> writes:
On Saturday, 10 February 2024 at 22:11:48 UTC, Brad Roberts wrote:
 Back when I was doing lots of software developer interviews, 
 one of my frequent questions involved date math.  This wasn't 
 because it's difficult from a coding standpoint, but that it's 
 NOT a coding problem. The key part of the question is 
 realization that it's a requirements question.  The thing that 
 makes dates complicated is defining what the question actually 
 is.

 The topic _seems_ like it should be simple, but the deeper you 
 dig the more you realize it's anything but simple.
Interesting but I have a doubt, they (Interviewees) should do the test with already existing Date Functions of given language or write new ones? Matheus.
Feb 10
parent Brad Roberts <braddr puremagic.com> writes:
On 2/10/2024 6:01 PM, matheus via Digitalmars-d-learn wrote:
 On Saturday, 10 February 2024 at 22:11:48 UTC, Brad Roberts wrote:
 Back when I was doing lots of software developer interviews, one of my 
 frequent questions involved date math.  This wasn't because it's 
 difficult from a coding standpoint, but that it's NOT a coding 
 problem. The key part of the question is realization that it's a 
 requirements question.  The thing that makes dates complicated is 
 defining what the question actually is.

 The topic _seems_ like it should be simple, but the deeper you dig the 
 more you realize it's anything but simple.
Interesting but I have a doubt, they (Interviewees) should do the test with already existing Date Functions of given language or write new ones? Matheus.
My interviews were never tests, they were discussions that might also involve coding tasks. In this case, the discussion was about requirements gathering disguised initially as a coding task. By and large, I never put a lot of weight on the coding parts themselves and mostly focused on problem solving and thinking skills. If the candidate couldn't get past writing code and wouldn't ask questions, that was a very bad sign.
Feb 10
prev sibling next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Saturday, February 10, 2024 3:11:48 PM MST Brad Roberts via Digitalmars-d-
learn wrote:
 Back when I was doing lots of software developer interviews, one of my
 frequent questions involved date math.  This wasn't because it's
 difficult from a coding standpoint, but that it's NOT a coding problem.
 The key part of the question is realization that it's a requirements
 question.  The thing that makes dates complicated is defining what the
 question actually is.

 The topic _seems_ like it should be simple, but the deeper you dig the
 more you realize it's anything but simple.
Indeed. And because it seems simple at first, it's very common for code to be written which does the wrong thing with regards to dates and times - often which seems like it does the right thing, because it works a lot of the time, when in fact, there are edge cases where it definitely does the wrong thing (e.g. with regards to leap years or DST). And math around months is a prime area where it's difficult to get right in part because different people have different requirements depending on the actual problem that they're trying to solve. - Jonathan M Davis
Feb 10
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On Saturday, 10 February 2024 at 15:53:09 UTC, Alexander Zhirov 
wrote:
 Is it possible to calculate the difference between dates in 
 years using regular means? Something like that


 ```
 writeln(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)));
 ```

 At the same time, keep in mind that the month and day matter, 
 because the difference between the year, taking into account 
 the month that has not come, will be less.

 My abilities are not yet enough to figure it out more elegantly.
Maybe I'm not understanding the question, but why not that result / 12? -Steve
Feb 10
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Saturday, February 10, 2024 4:20:33 PM MST Steven Schveighoffer via 
Digitalmars-d-learn wrote:
 On Saturday, 10 February 2024 at 15:53:09 UTC, Alexander Zhirov

 wrote:
 Is it possible to calculate the difference between dates in
 years using regular means? Something like that


 ```
 writeln(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)));
 ```

 At the same time, keep in mind that the month and day matter,
 because the difference between the year, taking into account
 the month that has not come, will be less.

 My abilities are not yet enough to figure it out more elegantly.
Maybe I'm not understanding the question, but why not that result / 12?
If I understand correctly, he cares about how far into the month the dates are, whereas diffMonths ignores the smaller units, meaning that you get the same result no matter when in the month the dates are. So, 2000-05-10 - 1990-05-09 would give 10, whereas 2000-05-10 - 1990-05-30 would give 9. diffMonths / 12 would give 10 in both cases. - Jonathan M Davis
Feb 10
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On Saturday, 10 February 2024 at 23:48:56 UTC, Jonathan M Davis 
wrote:

 If I understand correctly, he cares about how far into the 
 month the dates
 are, whereas diffMonths ignores the smaller units, meaning that 
 you get the
 same result no matter when in the month the dates are. So,
 2000-05-10 - 1990-05-09 would give 10, whereas 2000-05-10 - 
 1990-05-30
 would give 9. diffMonths / 12 would give 10 in both cases.
I thought `diffMonths` was actually already taking this into account... Looking at the impl, it's pretty simple. Would it make sense to have an overload that takes into account the day as well as the month/year? This kind of stuff is sometimes tricky to get right. -Steve
Feb 10
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Saturday, February 10, 2024 7:31:47 PM MST Steven Schveighoffer via 
Digitalmars-d-learn wrote:
 On Saturday, 10 February 2024 at 23:48:56 UTC, Jonathan M Davis

 wrote:
 If I understand correctly, he cares about how far into the
 month the dates
 are, whereas diffMonths ignores the smaller units, meaning that
 you get the
 same result no matter when in the month the dates are. So,
 2000-05-10 - 1990-05-09 would give 10, whereas 2000-05-10 -
 1990-05-30
 would give 9. diffMonths / 12 would give 10 in both cases.
I thought `diffMonths` was actually already taking this into account... Looking at the impl, it's pretty simple. Would it make sense to have an overload that takes into account the day as well as the month/year? This kind of stuff is sometimes tricky to get right.
Possibly. Given the trickiness involved, there probably should be a function in std.datetime to handle it, but I'll have to think about what the best way to do it would be in terms of the API. It is kind of similar to how some functions behave differently depending on how you want to treat the end of the month (e.g. whether adding a month to May 31st gives you June 30th or July 1st), but it's also not quite the same. So, the enum related to that wouldn't be appropriate, but it's yet another edge case where sometimes you want one behavior, and at other times, you want the other behavior. - Jonathan M Davis
Feb 10
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On Saturday, 10 February 2024 at 15:53:09 UTC, Alexander Zhirov 
wrote:
 Is it possible to calculate the difference between dates in 
 years using regular means? Something like that


 ```
 writeln(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)));
 ```

 At the same time, keep in mind that the month and day matter, 
 because the difference between the year, taking into account 
 the month that has not come, will be less.

 My abilities are not yet enough to figure it out more elegantly.
OK, so I thought this was already taking into account the day of the month. This is what I came up with: ```d int diffMonthNew(Date d1, Date d2) { auto diff = d1.diffMonths(d2); if(diff > 0) return diff + (d1.day < d2.day ? -1 : 0); else if(diff < 0) return diff + (d1.day > d2.day ? 1 : 0); return 0; } ``` Then if you want the years, it would be `diffMonthNew(d1, d2) / 12` -Steve
Feb 10