Skip to content
Snippets Groups Projects
Commit 3eed56c4 authored by Rob Swindell's avatar Rob Swindell :speech_balloon:
Browse files

Allow sysop-configurable date separator (e.g. YYYY-MM-DD)

To complete the request from: Max (WESTLINE)

  Is it possible to do a new dateformat in scfg
  In sweden we using YYYY-MM-DD format as standard.

The default will be '/'.  Technically, any separator is possible by editing
the "date_sep" value in the global section of main.ini.  SCFG allows the
most popular separators: /.- and space.
parent 83607285
No related branches found
No related tags found
No related merge requests found
Pipeline #5185 passed
......@@ -712,11 +712,11 @@ const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode,
}
if(strcmp(sp, "DATEFMT") == 0) {
return date_format(&cfg);
return date_format(&cfg, str, maxlen);
}
if(strcmp(sp, "BDATEFMT") == 0 || strcmp(sp, "BIRTHFMT") == 0) {
return birthdate_format(&cfg);
return birthdate_format(&cfg, str, maxlen);
}
if(strcmp(sp, "GENDERS") == 0)
......
......@@ -28,16 +28,25 @@ const char *mon[]={"Jan","Feb","Mar","Apr","May","Jun"
/****************************************************************************/
/****************************************************************************/
const char* date_format(scfg_t* cfg)
char* date_format(scfg_t* cfg, char* buf, size_t size)
{
switch (cfg->sys_date_fmt) {
case DDMMYY: return "DD/MM/YY";
case MMDDYY: return "MM/DD/YY";
case YYMMDD: return "YY/MM/DD";
case DDMMYY: snprintf(buf, size, "DD%cMM%cYY", cfg->sys_date_sep, cfg->sys_date_sep); return buf;
case MMDDYY: snprintf(buf, size, "MM%cDD%cYY", cfg->sys_date_sep, cfg->sys_date_sep); return buf;
case YYMMDD: snprintf(buf, size, "YY%cMM%cDD", cfg->sys_date_sep, cfg->sys_date_sep); return buf;
}
return "????????";
}
/****************************************************************************/
/* Assumes buf is at least 9 bytes in size */
/****************************************************************************/
char* date_template(scfg_t* cfg, char* buf, size_t size)
{
snprintf(buf, size, "nn%cnn%cnn", cfg->sys_date_sep, cfg->sys_date_sep);
return buf;
}
#define DECVAL(ch, mul) (DEC_CHAR_TO_INT(ch) * (mul))
/****************************************************************************/
......@@ -102,33 +111,41 @@ time32_t dstrtounix(scfg_t* cfg, const char *instr)
/****************************************************************************/
char* unixtodstr(scfg_t* cfg, time32_t t, char *str)
{
struct tm tm;
struct tm tm = {0};
time_t unix_time=t;
if(!unix_time)
strcpy(str,"00/00/00");
else {
if(localtime_r(&unix_time,&tm)==NULL) {
strcpy(str,"00/00/00");
return(str);
}
if (unix_time != 0) {
if (localtime_r(&unix_time, &tm) != NULL) {
if(tm.tm_mon>11) { /* DOS leap year bug */
tm.tm_mon=0;
tm.tm_year++;
}
if(tm.tm_mday>31)
tm.tm_mday=1;
}
}
if (cfg->sys_date_fmt == YYMMDD)
sprintf(str,"%02u/%02u/%02u"
,TM_YEAR(tm.tm_year), tm.tm_mon+1, tm.tm_mday);
sprintf(str,"%02u%c%02u%c%02u"
,TM_YEAR(tm.tm_year)
,cfg->sys_date_sep
,tm.tm_mon+1
,cfg->sys_date_sep
,tm.tm_mday);
else if(cfg->sys_date_fmt == DDMMYY)
sprintf(str,"%02u/%02u/%02u",tm.tm_mday,tm.tm_mon+1
sprintf(str,"%02u%c%02u%c%02u"
,tm.tm_mday
,cfg->sys_date_sep
,tm.tm_mon+1
,cfg->sys_date_sep
,TM_YEAR(tm.tm_year));
else
sprintf(str,"%02u/%02u/%02u",tm.tm_mon+1,tm.tm_mday
sprintf(str,"%02u%c%02u%c%02u"
,tm.tm_mon+1
,cfg->sys_date_sep
,tm.tm_mday
,cfg->sys_date_sep
,TM_YEAR(tm.tm_year));
}
return(str);
return str;
}
/****************************************************************************/
......
......@@ -32,7 +32,8 @@ extern "C" {
extern const char* wday[]; /* abbreviated weekday names */
extern const char* mon[]; /* abbreviated month names */
DLLEXPORT const char* date_format(scfg_t*);
DLLEXPORT char * date_format(scfg_t*, char* buf, size_t);
DLLEXPORT char * date_template(scfg_t*, char* buf, size_t);
DLLEXPORT char * zonestr(short zone);
DLLEXPORT time32_t dstrtounix(scfg_t*, const char *str);
DLLEXPORT char * unixtodstr(scfg_t*, time32_t, char *str);
......
......@@ -281,9 +281,9 @@ BOOL sbbs_t::newuser()
}
}
while((cfg.uq&UQ_BIRTH) && online && text[EnterYourBirthday][0]) {
bprintf(text[EnterYourBirthday], birthdate_format(&cfg));
bprintf(text[EnterYourBirthday], birthdate_format(&cfg, tmp, sizeof tmp));
format_birthdate(&cfg, useron.birth, str, sizeof(str));
if(gettmplt(str, cfg.sys_date_fmt == YYMMDD ? "nnnn/nn/nn" : "nn/nn/nnnn", K_EDIT) < 10)
if(gettmplt(str, birthdate_template(&cfg, tmp, sizeof tmp), K_EDIT) < 10)
continue;
int age = getage(&cfg, parse_birthdate(&cfg, str, tmp, sizeof(tmp)));
if(age >= 0 && age <= 200) { // TODO: Configurable min/max user age
......
......@@ -1316,7 +1316,18 @@ int edit_sys_datefmt(int page, int total)
{
int mode = WIN_SAV | WIN_MID;
int i = cfg.sys_date_fmt;
char* opts[] = { "MM/DD/YY", "DD/MM/YY", "YY/MM/DD", NULL };
if(cfg.sys_date_sep == '.')
i += 3;
else if(cfg.sys_date_sep == '_')
i += 6;
else if(cfg.sys_date_sep == ' ')
i += 9;
char* opts[] = {
"MM/DD/YY", "DD/MM/YY", "YY/MM/DD",
"MM.DD.YY", "DD.MM.YY", "YY.MM.DD",
"MM-DD-YY", "DD-MM-YY", "YY-MM-DD",
"MM DD YY", "DD MM YY", "YY MM DD",
NULL };
uifc.helpbuf=
"`Date Display Format:`\n"
"\n"
......@@ -1324,13 +1335,24 @@ int edit_sys_datefmt(int page, int total)
"U.S. date format of month first, choose `MM/DD/YY`. If you prefer the\n"
"European traditional date format of day first, choose `DD/MM/YY`.\n"
"If you and your users would prefer year first, choose `YY/MM/DD`.\n"
"\n"
"Different date value separators are also supported.\n";
;
if(page)
mode = wiz_help(page, total, uifc.helpbuf);
i=uifc.list(mode,0,10,0,&i,0
i=uifc.list(mode,0,00,0,&i,0
,"Date Display Format", opts);
if(i >= 0)
cfg.sys_date_fmt = i;
if (i < 0)
return i;
cfg.sys_date_fmt = i % 3;
if (i < 3)
cfg.sys_date_sep = '/';
else if(i < 6)
cfg.sys_date_sep = '.';
else if(i < 9)
cfg.sys_date_sep = '-';
else
cfg.sys_date_sep = ' ';
return i;
}
......@@ -1655,7 +1677,7 @@ void sys_cfg(void)
snprintf(opt[i++],MAX_OPLN,"%-20s%s %s","Local Time Zone"
,smb_zonestr(cfg.sys_timezone,NULL)
,SMB_TZ_HAS_DST(cfg.sys_timezone) && cfg.sys_misc&SM_AUTO_DST ? "(Auto-DST)" : "");
snprintf(opt[i++],MAX_OPLN,"%-20s%s","Local Date Format", date_format(&cfg));
snprintf(opt[i++],MAX_OPLN,"%-20s%s","Local Date Format", date_format(&cfg, str, sizeof str));
snprintf(opt[i++],MAX_OPLN,"%-20s%s","Operator",cfg.sys_op);
strcpy(opt[i++],"Notifications...");
......
......@@ -463,6 +463,7 @@ typedef struct
char sys_location[41]; /* System Location */
int16_t sys_timezone; /* Time Zone of BBS */
enum date_fmt sys_date_fmt;
char sys_date_sep;
char sys_daily[LEN_CMD+1]; /* Daily event */
char sys_logon[LEN_CMD+1]; /* Logon event */
char sys_logout[LEN_CMD+1]; /* Logoff event */
......
......@@ -103,6 +103,7 @@ BOOL read_main_cfg(scfg_t* cfg, char* error, size_t maxerrlen)
cfg->sys_timezone = iniGetInt16(ini, ROOT_SECTION, "timezone", 0);
cfg->sys_misc = iniGetUInteger(ini, ROOT_SECTION, "settings", 0);
cfg->sys_date_fmt = iniGetInteger(ini, ROOT_SECTION, "date_fmt", cfg->sys_misc & SM_EURODATE ? DDMMYY : MMDDYY);
cfg->sys_date_sep = *iniGetString(ini, NULL, "date_sep", "/", value);
cfg->sys_login = iniGetUInteger(ini, ROOT_SECTION, "login", 0);
cfg->sys_pwdays = iniGetInteger(ini, ROOT_SECTION, "pwdays", 0);
cfg->sys_deldays = iniGetInteger(ini, ROOT_SECTION, "deldays", 0);
......
......@@ -123,6 +123,8 @@ BOOL write_main_cfg(scfg_t* cfg)
iniSetInt16(&ini, ROOT_SECTION, "timezone", cfg->sys_timezone, NULL);
iniSetHexInt(&ini, ROOT_SECTION, "settings", cfg->sys_misc, NULL);
iniSetUInteger(&ini, ROOT_SECTION, "date_fmt", cfg->sys_date_fmt, NULL);
SAFEPRINTF(tmp, "%c", cfg->sys_date_sep);
iniSetString(&ini, ROOT_SECTION, "date_sep", tmp, NULL);
iniSetHexInt(&ini, ROOT_SECTION, "login", cfg->sys_login, NULL);
iniSetUInteger(&ini, ROOT_SECTION, "lastnode", cfg->sys_lastnode, NULL);
iniSetUInteger(&ini, ROOT_SECTION, "pwdays", cfg->sys_pwdays, NULL);
......
......@@ -934,31 +934,37 @@ int getbirthday(scfg_t* cfg, const char* birth)
}
// Always returns string in MM/DD/YY format
char* getbirthmmddyy(scfg_t* cfg, const char* birth, char* buf, size_t max)
char* getbirthmmddyy(scfg_t* cfg, char sep, const char* birth, char* buf, size_t max)
{
safe_snprintf(buf, max, "%02u/%02u/%02u"
safe_snprintf(buf, max, "%02u%c%02u%c%02u"
, getbirthmonth(cfg, birth)
, sep
, getbirthday(cfg, birth)
, sep
, getbirthyear(birth) % 100);
return buf;
}
// Always returns string in DD/MM/YY format
char* getbirthddmmyy(scfg_t* cfg, const char* birth, char* buf, size_t max)
char* getbirthddmmyy(scfg_t* cfg, char sep, const char* birth, char* buf, size_t max)
{
safe_snprintf(buf, max, "%02u/%02u/%02u"
safe_snprintf(buf, max, "%02u%c%02u%c%02u"
, getbirthday(cfg, birth)
, sep
, getbirthmonth(cfg, birth)
, sep
, getbirthyear(birth) % 100);
return buf;
}
// Always returns string in YY/MM/DD format
char* getbirthyymmdd(scfg_t* cfg, const char* birth, char* buf, size_t max)
char* getbirthyymmdd(scfg_t* cfg, char sep, const char* birth, char* buf, size_t max)
{
safe_snprintf(buf, max, "%02u/%02u/%02u"
safe_snprintf(buf, max, "%02u%c%02u%c%02u"
, getbirthyear(birth) % 100
, sep
, getbirthmonth(cfg, birth)
, sep
, getbirthday(cfg, birth));
return buf;
}
......@@ -966,11 +972,11 @@ char* getbirthyymmdd(scfg_t* cfg, const char* birth, char* buf, size_t max)
char* getbirthdstr(scfg_t* cfg, const char* birth, char* buf, size_t max)
{
if(cfg->sys_date_fmt == YYMMDD)
getbirthyymmdd(cfg, birth, buf, max);
getbirthyymmdd(cfg, cfg->sys_date_sep, birth, buf, max);
else if(cfg->sys_date_fmt == DDMMYY)
getbirthddmmyy(cfg, birth, buf, max);
getbirthddmmyy(cfg, cfg->sys_date_sep, birth, buf, max);
else
getbirthmmddyy(cfg, birth, buf, max);
getbirthmmddyy(cfg, cfg->sys_date_sep, birth, buf, max);
return buf;
}
......@@ -1026,28 +1032,53 @@ char* format_birthdate(scfg_t* cfg, const char* birthdate, char* out, size_t max
*out = '\0';
if(*birthdate) {
if (cfg->sys_date_fmt == YYMMDD)
safe_snprintf(out, maxlen, "%04u/%02u/%02u"
, getbirthyear(birthdate), getbirthmonth(cfg, birthdate), getbirthday(cfg, birthdate));
safe_snprintf(out, maxlen, "%04u%c%02u%c%02u"
, getbirthyear(birthdate)
, cfg->sys_date_sep
, getbirthmonth(cfg, birthdate)
, cfg->sys_date_sep
, getbirthday(cfg, birthdate));
else if (cfg->sys_date_fmt == DDMMYY)
safe_snprintf(out, maxlen, "%02u/%02u/%04u"
,getbirthday(cfg, birthdate), getbirthmonth(cfg, birthdate), getbirthyear(birthdate));
safe_snprintf(out, maxlen, "%02u%c%02u%c%04u"
, getbirthday(cfg, birthdate)
, cfg->sys_date_sep
, getbirthmonth(cfg, birthdate)
, cfg->sys_date_sep
, getbirthyear(birthdate));
else
safe_snprintf(out, maxlen, "%02u/%02u/%04u"
,getbirthmonth(cfg, birthdate), getbirthday(cfg, birthdate), getbirthyear(birthdate));
safe_snprintf(out, maxlen, "%02u%c%02u%c%04u"
, getbirthmonth(cfg, birthdate)
, cfg->sys_date_sep
, getbirthday(cfg, birthdate)
, cfg->sys_date_sep
, getbirthyear(birthdate));
}
return out;
}
const char* birthdate_format(scfg_t* cfg)
/****************************************************************************/
/****************************************************************************/
char* birthdate_format(scfg_t* cfg, char* buf, size_t size)
{
switch (cfg->sys_date_fmt) {
case MMDDYY: return "MM/DD/YYYY";
case DDMMYY: return "DD/MM/YYYY";
case YYMMDD: return "YYYY/MM/DD";
case MMDDYY: snprintf(buf, size, "MM%cDD%cYYYY", cfg->sys_date_sep, cfg->sys_date_sep); return buf;
case DDMMYY: snprintf(buf, size, "DD%cMM%cYYYY", cfg->sys_date_sep, cfg->sys_date_sep); return buf;
case YYMMDD: snprintf(buf, size, "YYYY%cMM%cDD", cfg->sys_date_sep, cfg->sys_date_sep); return buf;
}
return "??????????";
}
/****************************************************************************/
/****************************************************************************/
char* birthdate_template(scfg_t* cfg, char* buf, size_t size)
{
if (cfg->sys_date_fmt == YYMMDD)
snprintf(buf, size, "nnnn%cnn%cnn", cfg->sys_date_sep, cfg->sys_date_sep);
else
snprintf(buf, size, "nn%cnn%cnnnn", cfg->sys_date_sep, cfg->sys_date_sep);
return buf;
}
/****************************************************************************/
/****************************************************************************/
int opennodedat(scfg_t* cfg)
......
......@@ -63,12 +63,13 @@ DLLEXPORT int getbirthmonth(scfg_t*, const char* birthdate);
DLLEXPORT int getbirthday(scfg_t*, const char* birthdate);
DLLEXPORT int getbirthyear(const char* birthdate);
DLLEXPORT char* getbirthdstr(scfg_t*, const char* birthdate, char* buf, size_t);
DLLEXPORT char* getbirthmmddyy(scfg_t*, const char* birthdate, char* buf, size_t);
DLLEXPORT char* getbirthddmmyy(scfg_t*, const char* birthdate, char* buf, size_t);
DLLEXPORT char* getbirthyymmdd(scfg_t*, const char* birthdate, char* buf, size_t);
DLLEXPORT char* getbirthmmddyy(scfg_t*, char sep, const char* birthdate, char* buf, size_t);
DLLEXPORT char* getbirthddmmyy(scfg_t*, char sep, const char* birthdate, char* buf, size_t);
DLLEXPORT char* getbirthyymmdd(scfg_t*, char sep, const char* birthdate, char* buf, size_t);
DLLEXPORT char* parse_birthdate(scfg_t*, const char* birthdate, char* out, size_t);
DLLEXPORT char* format_birthdate(scfg_t*, const char* birthdate, char* out, size_t);
DLLEXPORT const char* birthdate_format(scfg_t*);
DLLEXPORT char* birthdate_format(scfg_t*, char* buf, size_t);
DLLEXPORT char* birthdate_template(scfg_t*, char* buf, size_t);
DLLEXPORT char* username(scfg_t*, int usernumber, char * str);
DLLEXPORT char* usermailaddr(scfg_t*, char* addr, const char* name);
DLLEXPORT void smtp_netmailaddr(scfg_t*, smbmsg_t*, char* name, size_t namelen, char* addr, size_t addrlen);
......
......@@ -183,9 +183,9 @@ void sbbs_t::useredit(int usernumber)
putuserstr(user.number, USER_HANDLE, user.handle);
break;
case 'B':
bprintf(text[EnterYourBirthday], birthdate_format(&cfg));
bprintf(text[EnterYourBirthday], birthdate_format(&cfg, tmp, sizeof tmp));
format_birthdate(&cfg, user.birth, str, sizeof(str));
if(gettmplt(str, "nn/nn/nnnn", kmode) == 10) {
if(gettmplt(str, birthdate_template(&cfg, tmp, sizeof tmp), kmode) == 10) {
parse_birthdate(&cfg, str, user.birth, sizeof(user.birth));
putuserstr(user.number, USER_BIRTH, user.birth);
}
......@@ -342,28 +342,28 @@ void sbbs_t::useredit(int usernumber)
case 'K': /* date changes */
bputs(text[UeditLastOn]);
unixtodstr(&cfg,user.laston,str);
gettmplt(str,"nn/nn/nn",K_LINE|K_EDIT);
gettmplt(str, date_template(&cfg, tmp, sizeof tmp),K_LINE|K_EDIT);
if(sys_status&SS_ABORT)
break;
user.laston=dstrtounix(&cfg,str);
putuserdatetime(user.number, USER_LASTON, user.laston);
bputs(text[UeditFirstOn]);
unixtodstr(&cfg,user.firston,str);
gettmplt(str,"nn/nn/nn",K_LINE|K_EDIT);
gettmplt(str, date_template(&cfg, tmp, sizeof tmp),K_LINE|K_EDIT);
if(sys_status&SS_ABORT)
break;
user.firston=dstrtounix(&cfg,str);
putuserdatetime(user.number, USER_FIRSTON, user.firston);
bputs(text[UeditExpire]);
unixtodstr(&cfg,user.expire,str);
gettmplt(str,"nn/nn/nn",K_LINE|K_EDIT);
gettmplt(str, date_template(&cfg, tmp, sizeof tmp),K_LINE|K_EDIT);
if(sys_status&SS_ABORT)
break;
user.expire=dstrtounix(&cfg,str);
putuserdatetime(user.number, USER_EXPIRE, user.expire);
bputs(text[UeditPwModDate]);
unixtodstr(&cfg,user.pwmod,str);
gettmplt(str,"nn/nn/nn",K_LINE|K_EDIT);
gettmplt(str, date_template(&cfg, tmp, sizeof tmp),K_LINE|K_EDIT);
if(sys_status&SS_ABORT)
break;
user.pwmod=dstrtounix(&cfg,str);
......
......@@ -213,7 +213,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle
safe_snprintf(str, sizeof(str), "%u\n%u\n%s\n%c\n%u\n%s\n"
,useron.level /* User main level */
,useron.level /* User transfer level */
,getbirthmmddyy(&cfg, useron.birth, tmp, sizeof(tmp)) /* User birthday (MM/DD/YY) */
,getbirthmmddyy(&cfg, '/', useron.birth, tmp, sizeof(tmp)) /* User birthday (MM/DD/YY) */
,useron.sex ? useron.sex : '?' /* User sex (M/F) */
,useron.number /* User number */
,useron.phone); /* User phone number */
......@@ -433,7 +433,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle
"\n%s\n%02d:%02d\n%c\n"
,0 /* 30: Kbytes downloaded today */
,user_available_credits(&useron)/1024UL /* 31: Max Kbytes to download today */
,getbirthmmddyy(&cfg, useron.birth, tmp, sizeof(tmp)) /* 32: User birthday (MM/DD/YY) */
,getbirthmmddyy(&cfg, '/', useron.birth, tmp, sizeof(tmp)) /* 32: User birthday (MM/DD/YY) */
,node_dir /* 33: Path to MAIN directory */
,data_dir /* 34: Path to GEN directory */
,cfg.sys_op /* 35: Sysop name */
......@@ -681,7 +681,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle
,8 /* Data bits */
,online==ON_LOCAL?"LOCAL":"REMOTE" /* Online local or remote */
,cfg.com_port /* COMx port */
,getbirthmmddyy(&cfg, useron.birth, tmp, sizeof(tmp)) /* User birthday (MM/DD/YY) */
,getbirthmmddyy(&cfg, '/', useron.birth, tmp, sizeof(tmp)) /* User birthday (MM/DD/YY) */
,dte_rate /* DTE rate */
,"FALSE" /* Already connected? */
,"Normal Connection"); /* Normal or ARQ connect */
......@@ -939,7 +939,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, uint tle
,MIN((tleft/60), INT16_MAX) /* Minutes left */
,useron.phone /* User's phone number */
,useron.location /* User's city and state */
,getbirthmmddyy(&cfg, useron.birth, tmp, sizeof(tmp)) /* User's birth date (MM/DD/YY) */
,getbirthmmddyy(&cfg, '/', useron.birth, tmp, sizeof(tmp)) /* User's birth date (MM/DD/YY) */
);
lfexpand(str,misc);
fwrite(str, strlen(str), 1, fp);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment