From 3e17c8d8f897ee459c8f1776d8d0cd3c327e2481 Mon Sep 17 00:00:00 2001
From: Rob <rob@synchro.net>
Date: Sat, 3 Oct 2020 02:50:34 -0700
Subject: [PATCH] Changed user birthdate format to YYYYMMDD, input to
 YYYY/MM/DD

Support the old MM/DD/YY or DD/MM/YY format for reading. New users get the new format.
---
 src/sbbs3/newuser.cpp  | 17 +++++++---
 src/sbbs3/userdat.c    | 75 +++++++++++++++++++++++++++++-------------
 src/sbbs3/userdat.h    |  5 ++-
 src/sbbs3/useredit.cpp | 15 +++++----
 4 files changed, 78 insertions(+), 34 deletions(-)

diff --git a/src/sbbs3/newuser.cpp b/src/sbbs3/newuser.cpp
index 4ba1928680..95d6595c70 100644
--- a/src/sbbs3/newuser.cpp
+++ b/src/sbbs3/newuser.cpp
@@ -315,11 +315,20 @@ BOOL sbbs_t::newuser()
 			bputs(text[EnterYourSex]);
 			useron.sex=(char)getkeys("MF",0); 
 		}
+		str[0] = 0;
 		while((cfg.uq&UQ_BIRTH) && online && text[EnterYourBirthday][0]) {
-			bprintf(text[EnterYourBirthday]
-				,cfg.sys_misc&SM_EURODATE ? "DD/MM/YY" : "MM/DD/YY");
-			if(gettmplt(useron.birth,"nn/nn/nn",K_EDIT)==8 && getage(&cfg,useron.birth))
-				break; 
+			bprintf(text[EnterYourBirthday], "YYYY/MM/DD");
+			if(gettmplt(str, "nnnn/nn/nn", K_EDIT) < 10)
+				continue;
+			int age = getage(&cfg, str);
+			if(age >= 1 && age <= 150) {
+				SAFEPRINTF3(useron.birth, "%.4s%.2s%.2s", str, str + 5, str + 8);
+				break;
+			}
+			SAFEPRINTF3(str, "%04u/%02u/%02u"
+				,getbirthyear(useron.birth)
+				,getbirthmonth(&cfg, useron.birth)
+				,getbirthday(&cfg, useron.birth));
 		}
 		if(!online) return(FALSE);
 		while(!(cfg.uq&UQ_NONETMAIL) && online && text[EnterNetMailAddress][0]) {
diff --git a/src/sbbs3/userdat.c b/src/sbbs3/userdat.c
index c1c7837d6b..35437cedb7 100644
--- a/src/sbbs3/userdat.c
+++ b/src/sbbs3/userdat.c
@@ -734,12 +734,55 @@ int putusername(scfg_t* cfg, int number, char *name)
 	return(0);
 }
 
+#define DECVAL(ch, mul)	(DEC_CHAR_TO_INT(ch) * (mul))
+
+int getbirthyear(const char *birth)
+{
+	if(isdigit(birth[2]))				// CCYYMMYY format
+		return DECVAL(birth[0], 1000)
+				+ DECVAL(birth[1], 100)
+				+ DECVAL(birth[2], 10)
+				+ DECVAL(birth[3], 1);
+	// DD/MM/YY or MM/DD/YY format
+	time_t now = time(NULL);
+	struct	tm tm;
+	if(localtime_r(&now, &tm) == NULL)
+		return 0;
+	tm.tm_year += 1900;
+	int year = 1900 + DECVAL(birth[6], 10) + DECVAL(birth[7], 1);
+	if(tm.tm_year - year > 105)
+		year += 100;
+	return year;
+}
+
+int getbirthmonth(scfg_t* cfg, const char *birth)
+{
+	if(isdigit(birth[5]))				// CCYYMMYY format
+		return DECVAL(birth[4], 10)	+ DECVAL(birth[5], 1);
+	if(cfg->sys_misc & SM_EURODATE) {	// DD/MM/YY format
+		return DECVAL(birth[3], 10) + DECVAL(birth[4], 1);
+	} else {							// MM/DD/YY format
+		return DECVAL(birth[0], 10) + DECVAL(birth[1], 1);
+	}
+}
+
+int getbirthday(scfg_t* cfg, const char *birth)
+{
+	if(isdigit(birth[5]))				// CCYYMMYY format
+		return DECVAL(birth[6], 10)	+ DECVAL(birth[7], 1);
+	if(cfg->sys_misc & SM_EURODATE) {	// DD/MM/YY format
+		return DECVAL(birth[0], 10) + DECVAL(birth[1], 1);
+	} else {							// MM/DD/YY format
+		return DECVAL(birth[3], 10) + DECVAL(birth[4], 1);
+	}
+}
+
 /****************************************************************************/
-/* Returns the age derived from the string 'birth' in the format MM/DD/YY	*/
+/* Returns the age derived from the string 'birth' in the format CCYYMMDD	*/
+/* or legacy: MM/DD/YY or DD/MM/YY											*/
 /****************************************************************************/
-uint getage(scfg_t* cfg, char *birth)
+int getage(scfg_t* cfg, const char *birth)
 {
-	uint	age;
 	struct	tm tm;
 	time_t	now;
 
@@ -752,26 +795,14 @@ uint getage(scfg_t* cfg, char *birth)
 	now=time(NULL);
 	if(localtime_r(&now,&tm)==NULL)
 		return(0);
-	age=(tm.tm_year)-(((birth[6]&0xf)*10)+(birth[7]&0xf));
-	if(age>105)
-		age-=100;
+
 	tm.tm_mon++;	/* convert to 1 based */
-	if(cfg->sys_misc&SM_EURODATE) {		/* DD/MM/YY format */
-		if(atoi(birth)>31 || atoi(birth+3)>12)
-			return(0);
-		if(((birth[3]&0xf)*10)+(birth[4]&0xf)>tm.tm_mon ||
-			(((birth[3]&0xf)*10)+(birth[4]&0xf)==tm.tm_mon &&
-			((birth[0]&0xf)*10)+(birth[1]&0xf)>tm.tm_mday))
-			age--;
-	} else {							/* MM/DD/YY format */
-		if(atoi(birth)>12 || atoi(birth+3)>31)
-			return(0);
-		if(((birth[0]&0xf)*10)+(birth[1]&0xf)>tm.tm_mon ||
-			(((birth[0]&0xf)*10)+(birth[1]&0xf)==tm.tm_mon &&
-			((birth[3]&0xf)*10)+(birth[4]&0xf)>tm.tm_mday))
-			age--;
-	}
-	return(age);
+	int year = getbirthyear(birth);
+	int age = (1900 + tm.tm_year) - year;
+	int mon = getbirthmonth(cfg, birth);
+	if(mon > tm.tm_mon || (mon == tm.tm_mon && getbirthday(cfg, birth) > tm.tm_mday))
+		age--;
+	return age;
 }
 
 /****************************************************************************/
diff --git a/src/sbbs3/userdat.h b/src/sbbs3/userdat.h
index 37ec8c4139..e0af4b2afe 100644
--- a/src/sbbs3/userdat.h
+++ b/src/sbbs3/userdat.h
@@ -80,7 +80,10 @@ DLLEXPORT int	putusername(scfg_t*, int number, char * name);
 DLLEXPORT uint	total_users(scfg_t*);
 DLLEXPORT uint	lastuser(scfg_t*);
 DLLEXPORT BOOL	del_lastuser(scfg_t*);
-DLLEXPORT uint	getage(scfg_t*, char *birthdate);
+DLLEXPORT int	getage(scfg_t*, const char* birthdate);
+DLLEXPORT int	getbirthmonth(scfg_t*, const char* birthdate);
+DLLEXPORT int	getbirthday(scfg_t*, const char* birthdate);
+DLLEXPORT int	getbirthyear(const char* birthdate);
 DLLEXPORT char*	username(scfg_t*, int usernumber, char * str);
 DLLEXPORT char* usermailaddr(scfg_t*, char* addr, const char* name);
 DLLEXPORT int	opennodedat(scfg_t*);
diff --git a/src/sbbs3/useredit.cpp b/src/sbbs3/useredit.cpp
index a137eb3056..231ee4cd0e 100644
--- a/src/sbbs3/useredit.cpp
+++ b/src/sbbs3/useredit.cpp
@@ -101,7 +101,8 @@ void sbbs_t::useredit(int usernumber)
 			,user.level>useron.level && console&CON_R_ECHO
 			? "XXX-XXX-XXXX" : user.phone);
 		bprintf(text[UeditAddressBirthday]
-			,user.address,getage(&cfg,user.birth),user.sex,user.birth);
+			,user.address,getage(&cfg,user.birth),user.sex,user.birth
+			,getbirthyear(user.birth), getbirthmonth(&cfg, user.birth), getbirthday(&cfg, user.birth));
 		bprintf(text[UeditLocationZipcode],user.location,user.zipcode);
 		bprintf(text[UeditNoteHandle],user.note,user.handle);
 		bprintf(text[UeditComputerModem],user.comp,user.modem);
@@ -186,12 +187,12 @@ void sbbs_t::useredit(int usernumber)
 				putuserrec(&cfg,user.number,U_HANDLE,LEN_HANDLE,user.handle);
 				break;
 			case 'B':
-				bprintf(text[EnterYourBirthday]
-					,cfg.sys_misc&SM_EURODATE ? "DD/MM/YY" : "MM/DD/YY");
-				gettmplt(user.birth,"nn/nn/nn",kmode);
-				if(sys_status&SS_ABORT)
-					break;
-				putuserrec(&cfg,user.number,U_BIRTH,LEN_BIRTH,user.birth);
+				bprintf(text[EnterYourBirthday], "YYYY/MM/DD");
+				SAFEPRINTF3(str, "%04u/%02u/%02u", getbirthyear(user.birth), getbirthmonth(&cfg, user.birth), getbirthday(&cfg, user.birth));
+				if(gettmplt(str, "nnnn/nn/nn", kmode) == 10) {
+					SAFEPRINTF3(user.birth, "%.4s%.2s%.2s", str, str + 5, str + 8);
+					putuserrec(&cfg,user.number,U_BIRTH,LEN_BIRTH,user.birth);
+				}
 				break;
 			case 'C':
 				bputs(text[EnterYourComputer]);
-- 
GitLab