From 7ab6dc37f8bcc348ecbdaddce4a32d068e344670 Mon Sep 17 00:00:00 2001
From: James Cook <james@lindenlab.com>
Date: Wed, 18 Nov 2009 19:21:09 -0800
Subject: EXT-1743 People inspectors should show Age, not Date born, fixed date
 math Changed date math to correctly account for month lengths and leap years.
 Extended unit test. Review pending.

---
 indra/newview/lldateutil.cpp            | 71 ++++++++++++++++++++++++++++++---
 indra/newview/llinspectavatar.cpp       |  3 +-
 indra/newview/tests/lldateutil_test.cpp | 26 ++++++++++--
 3 files changed, 90 insertions(+), 10 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/lldateutil.cpp b/indra/newview/lldateutil.cpp
index 040fad3c4a..1fe1715995 100644
--- a/indra/newview/lldateutil.cpp
+++ b/indra/newview/lldateutil.cpp
@@ -66,23 +66,82 @@ static S32 age_days_from_date(const std::string& date_string,
 	return age_days;
 }
 
+static S32 DAYS_PER_MONTH_NOLEAP[] =
+	{ 31, 28, 21, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+static S32 DAYS_PER_MONTH_LEAP[] =
+	{ 31, 29, 21, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+static S32 days_from_month(S32 year, S32 month)
+{
+	if (year % 4 == 0 
+		&& year % 100 != 0)
+	{
+		// leap year
+		return DAYS_PER_MONTH_LEAP[month];
+	}
+	else
+	{
+		return DAYS_PER_MONTH_NOLEAP[month];
+	}
+}
+
 std::string LLDateUtil::ageFromDate(const std::string& date_string,
 									const LLDate& now)
 {
+#define BAD_DATE_MATH 0
+#if BAD_DATE_MATH
 	S32 age_days = age_days_from_date(date_string, now);
 	if (age_days == S32_MIN) return "???";
-
-	// Noun pluralization depends on language
-	std::string lang = LLUI::getLanguage();
-
-	// Try for age in round number of years
-	LLStringUtil::format_map_t args;
 	S32 age_years = age_days / 365;
 	age_days = age_days % 365;
 	// *NOTE: This is wrong.  Not all months have 30 days, but we don't have a library
 	// for relative date arithmetic. :-(  JC
 	S32 age_months = age_days / 30;
 	age_days = age_days % 30;
+#else
+	S32 born_month, born_day, born_year;
+	S32 matched = sscanf(date_string.c_str(), "%d/%d/%d", &born_month, &born_day, &born_year);
+	if (matched != 3) return "???";
+	LLDate born_date;
+	born_date.fromYMDHMS(born_year, born_month, born_day);
+	F64 born_date_secs_since_epoch = born_date.secondsSinceEpoch();
+	// Correct for the fact that account creation dates are in Pacific time,
+	// == UTC - 8
+	born_date_secs_since_epoch += 8.0 * 60.0 * 60.0;
+	born_date.secondsSinceEpoch(born_date_secs_since_epoch);
+	// explode out to month/day/year again
+	born_date.split(&born_year, &born_month, &born_day);
+
+	S32 now_year, now_month, now_day;
+	now.split(&now_year, &now_month, &now_day);
+
+	// Do grade-school subtraction, from right-to-left, borrowing from the left
+	// when things go negative
+	S32 age_days = (now_day - born_day);
+	if (age_days < 0)
+	{
+		now_month -= 1;
+		if (now_month == 0)
+		{
+			now_year -= 1;
+			now_month = 12;
+		}
+		age_days += days_from_month(now_year, now_month);
+	}
+	S32 age_months = (now_month - born_month);
+	if (age_months < 0)
+	{
+		now_year -= 1;
+		age_months += 12;
+	}
+	S32 age_years = (now_year - born_year);
+#endif
+
+	// Noun pluralization depends on language
+	std::string lang = LLUI::getLanguage();
+
+	// Try for age in round number of years
+	LLStringUtil::format_map_t args;
 
 	if (age_months > 0 || age_years > 0)
 	{
diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp
index baddd90d46..866669f326 100644
--- a/indra/newview/llinspectavatar.cpp
+++ b/indra/newview/llinspectavatar.cpp
@@ -39,6 +39,7 @@
 #include "llavataractions.h"
 #include "llavatarpropertiesprocessor.h"
 #include "llcallingcard.h"
+#include "lldateutil.h"
 #include "llfloaterreporter.h"
 #include "llfloaterworldmap.h"
 #include "llinspect.h"
@@ -351,7 +352,7 @@ void LLInspectAvatar::processAvatarData(LLAvatarData* data)
 {
 	LLStringUtil::format_map_t args;
 	args["[BORN_ON]"] = data->born_on;
-	args["[AGE]"] = data->born_on;
+	args["[AGE]"] = LLDateUtil::ageFromDate(data->born_on, LLDate::now());
 	args["[SL_PROFILE]"] = data->about_text;
 	args["[RW_PROFILE"] = data->fl_about_text;
 	args["[ACCTTYPE]"] = LLAvatarPropertiesProcessor::accountType(data);
diff --git a/indra/newview/tests/lldateutil_test.cpp b/indra/newview/tests/lldateutil_test.cpp
index ed753b6ff7..142a5eb5e6 100644
--- a/indra/newview/tests/lldateutil_test.cpp
+++ b/indra/newview/tests/lldateutil_test.cpp
@@ -60,6 +60,11 @@ std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::
 
 std::string LLTrans::getCountString(const std::string& language, const std::string& xml_desc, S32 count)
 {
+	count_string_t key(xml_desc, count);
+	if (gCountString.find(key) == gCountString.end())
+	{
+		return std::string("Couldn't find ") + xml_desc;
+	}
 	return gCountString[ count_string_t(xml_desc, count) ];
 }
 
@@ -91,8 +96,11 @@ namespace tut
 			gCountString[ count_string_t("AgeYears", 2) ]  = "2 years";
 			gCountString[ count_string_t("AgeMonths", 1) ] = "1 month";
 			gCountString[ count_string_t("AgeMonths", 2) ] = "2 months";
+			gCountString[ count_string_t("AgeMonths", 11) ]= "11 months";
 			gCountString[ count_string_t("AgeWeeks", 1) ]  = "1 week";
 			gCountString[ count_string_t("AgeWeeks", 2) ]  = "2 weeks";
+			gCountString[ count_string_t("AgeWeeks", 3) ]  = "3 weeks";
+			gCountString[ count_string_t("AgeWeeks", 4) ]  = "4 weeks";
 			gCountString[ count_string_t("AgeDays", 1) ]   = "1 day";
 			gCountString[ count_string_t("AgeDays", 2) ]   = "2 days";
 		}
@@ -113,12 +121,18 @@ namespace tut
 		ensure_equals("years",
 			LLDateUtil::ageFromDate("12/31/2007", mNow),
 			"2 years old" );
-		ensure_equals("single year",
-			LLDateUtil::ageFromDate("12/31/2008", mNow),
-			"1 year old" );
+		ensure_equals("years",
+			LLDateUtil::ageFromDate("1/1/2008", mNow),
+			"1 year 11 months old" );
+		ensure_equals("single year + one month",
+			LLDateUtil::ageFromDate("11/30/2008", mNow),
+			"1 year 1 month old" );
 		ensure_equals("single year + a bit",
 			LLDateUtil::ageFromDate("12/12/2008", mNow),
 			"1 year old" );
+		ensure_equals("single year",
+			LLDateUtil::ageFromDate("12/31/2008", mNow),
+			"1 year old" );
     }
 
 	template<> template<>
@@ -128,6 +142,9 @@ namespace tut
 		ensure_equals("months",
 			LLDateUtil::ageFromDate("10/30/2009", mNow),
 			"2 months old" );
+		ensure_equals("months 2",
+			LLDateUtil::ageFromDate("10/31/2009", mNow),
+			"2 months old" );
 		ensure_equals("single month",
 			LLDateUtil::ageFromDate("11/30/2009", mNow),
 			"1 month old" );
@@ -137,6 +154,9 @@ namespace tut
 	void dateutil_object_t::test<3>()
 	{
 		set_test_name("Weeks");
+		ensure_equals("4 weeks",
+			LLDateUtil::ageFromDate("12/1/2009", mNow),
+			"4 weeks old" );
 		ensure_equals("weeks",
 			LLDateUtil::ageFromDate("12/17/2009", mNow),
 			"2 weeks old" );
-- 
cgit v1.2.3