From 445394f9fc31d3d40261737c101ff39fe6ba01d0 Mon Sep 17 00:00:00 2001
From: "Rob Swindell (on Windows 11)" <rob@synchro.net>
Date: Fri, 20 Dec 2024 19:44:47 -0800
Subject: [PATCH] Encode local wallclock (not time_t) in SMB's when_t.time

Increment SMBLIB version to 3.10

Fix issue #845: Changing system/OS time zone, changes dates/times of posted
messages

Sysops and users shouldn't notice any change unless they change the time zone
of their system/OS (not accounting changes for daylight/standard time) and
the result will be that message dates appear the same after such a change.

For backward compatibily, any stored time_t's in msghdr_t.when_written.time
(i.e. all existing SMB messages) will still be decoded and displayed properly.
We detect a time_t value by the upper 6 bits being non-zero. When the upper
6 bits of a when_written.time value are zero, then we know the 'year' is
stored in the 16-bits before the when_written field (never used bits of the
netattr field, now part of the when_t structure definition) and the Month,
Day, Hour, Minute, and Second of the wallclock at the poster's site are
encoded in the low 26 bits of the time field.

This also eliminates more uses of 32-bit time_t that'll likely start being
a problem 2038 and really fall over and die in 2106. At least messages'
posting dates won't have any issue now. The "when_imported" values could use
a similar treatment someday I suppose - and we could get rid of the
when_imported.zone value as its not really needed we could use those 16-bits
for the when_imported.year.

Didn't change anything with filebases (still using time_t's though the
when_written hdr field isn't used for much with regards to files).

Yes, we could have converted all imported "broken down" message dates to
UTC and continued to store them as a time_t using timegm() instead of mktime()
for conversion to time_t, and I considered that. But we would have needed to
create/use a flag in the message header to indicate such stored date/times
(since they'd have to go through different adjustment for original time zone
before display, basically reversing the logic of all the places we display the
message dates/times using localtime verus gmtime/UTC C RTL functions),
couldn't just initialize the time with a call to time() upon import of local
messages (unless the local timezone happened to be UTC). And in the end, we'd
still have a 32-bit time_t value. So this seemed the better path.

I would have liked to have stored the date fields in a more human readable
encoding (BCD or decimal, ala isoDate and isoTime_t), but I just didn't have
the spare bits in the fixed portion of message headers to be wasteful like
that.

Here's an example from smbutil v of a message header posted after this change:
  when_written     03292595 41E0 Fri Dec 20 18:22:21 2024 PST
  when_imported    6766265D 41E0 Fri Dec 20 18:22:21 2024 PST

Notice the difference in the hex encoding of the date/time between the 2
header fields: when_imported still uses time_t. The when_written.year value
isn't output here.
---
 src/sbbs3/atcodes.cpp  |  6 +++---
 src/sbbs3/bulkmail.cpp |  3 +--
 src/sbbs3/email.cpp    |  5 +++--
 src/sbbs3/getmsg.cpp   |  7 ++++---
 src/sbbs3/js_bbs.cpp   |  2 +-
 src/sbbs3/js_msgbase.c |  4 ++--
 src/sbbs3/mailsrvr.c   |  3 ++-
 src/sbbs3/msgdate.c    |  2 +-
 src/sbbs3/msgtoqwk.cpp |  6 +++---
 src/sbbs3/netmail.cpp  | 12 ++++++-----
 src/sbbs3/postmsg.cpp  | 21 ++++++++++----------
 src/sbbs3/qwk.cpp      | 11 +++++------
 src/sbbs3/qwknodes.c   | 12 +++++------
 src/sbbs3/qwktomsg.cpp |  7 +++----
 src/sbbs3/readmail.cpp |  2 +-
 src/sbbs3/readmsgs.cpp |  4 ++--
 src/sbbs3/sbbsecho.c   | 31 +++++++++++++++--------------
 src/sbbs3/smbutil.c    | 12 +++++------
 src/sbbs3/writemsg.cpp |  2 +-
 src/smblib/smbadd.c    | 21 ++++++++++----------
 src/smblib/smbdefs.h   | 26 ++++++++++++++++++++----
 src/smblib/smbdump.c   |  2 +-
 src/smblib/smblib.c    | 45 ++++++++++++++++++++++++++++++++++++++++--
 src/smblib/smblib.h    |  2 ++
 24 files changed, 156 insertions(+), 92 deletions(-)

diff --git a/src/sbbs3/atcodes.cpp b/src/sbbs3/atcodes.cpp
index 44b1927daa..cdf20b1471 100644
--- a/src/sbbs3/atcodes.cpp
+++ b/src/sbbs3/atcodes.cpp
@@ -2036,14 +2036,14 @@ const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode,
 	if(!strcmp(sp,"MSG_TAGS") && current_msg!=NULL)
 		return(current_msg->tags==NULL ? nulstr : current_msg->tags);
 	if(!strcmp(sp,"MSG_DATE") && current_msg!=NULL)
-		return(timestr(current_msg->hdr.when_written.time));
+		return(timestr(smb_time(current_msg->hdr.when_written)));
 	if(!strcmp(sp,"MSG_DATE_UTC") && current_msg!=NULL)
-		return(timestr(current_msg->hdr.when_written.time - (smb_tzutc(current_msg->hdr.when_written.zone) * 60)));
+		return(timestr(smb_time(current_msg->hdr.when_written) - (smb_tzutc(current_msg->hdr.when_written.zone) * 60)));
 	if(!strcmp(sp,"MSG_IMP_DATE") && current_msg!=NULL)
 		return(timestr(current_msg->hdr.when_imported.time));
 	if(!strcmp(sp,"MSG_AGE") && current_msg!=NULL)
 		return age_of_posted_item(str, maxlen
-			, current_msg->hdr.when_written.time - (smb_tzutc(current_msg->hdr.when_written.zone) * 60));
+			,smb_time(current_msg->hdr.when_written) - (smb_tzutc(current_msg->hdr.when_written.zone) * 60));
 	if(!strcmp(sp,"MSG_TIMEZONE") && current_msg!=NULL)
 		return(smb_zonestr(current_msg->hdr.when_written.zone,NULL));
 	if(!strcmp(sp,"MSG_IMP_TIMEZONE") && current_msg!=NULL)
diff --git a/src/sbbs3/bulkmail.cpp b/src/sbbs3/bulkmail.cpp
index a5ed6aa794..2376c27bf2 100644
--- a/src/sbbs3/bulkmail.cpp
+++ b/src/sbbs3/bulkmail.cpp
@@ -92,8 +92,7 @@ bool sbbs_t::bulkmail(uchar *ar)
 
 	smb_hfield_str(&msg,SUBJECT,title);
 
-	msg.hdr.when_written.time=time32(NULL);
-	msg.hdr.when_written.zone=sys_timezone(&cfg);
+	msg.hdr.when_written = smb_when(time(NULL), sys_timezone(&cfg));
 
 	editor_info_to_msg(&msg, editor, charset);
 
diff --git a/src/sbbs3/email.cpp b/src/sbbs3/email.cpp
index 2fe85ab491..fd39db24f3 100644
--- a/src/sbbs3/email.cpp
+++ b/src/sbbs3/email.cpp
@@ -241,8 +241,9 @@ bool sbbs_t::email(int usernumber, const char *top, const char *subj, int mode,
 	msg.hdr.attr=msgattr;
 	if(mode&WM_FILE)
 		msg.hdr.auxattr |= (MSG_FILEATTACH | MSG_KILLFILE);
-	msg.hdr.when_written.time=msg.hdr.when_imported.time=time32(NULL);
-	msg.hdr.when_written.zone=msg.hdr.when_imported.zone=sys_timezone(&cfg);
+	msg.hdr.when_imported.time = time32(NULL);
+	msg.hdr.when_imported.zone = sys_timezone(&cfg);
+	msg.hdr.when_written = smb_when(msg.hdr.when_imported.time, msg.hdr.when_imported.zone);
 
 	if(cfg.mail_maxcrcs) {
 		i=smb_addcrc(&smb,crc);
diff --git a/src/sbbs3/getmsg.cpp b/src/sbbs3/getmsg.cpp
index 73d732cf1f..ae740b27e3 100644
--- a/src/sbbs3/getmsg.cpp
+++ b/src/sbbs3/getmsg.cpp
@@ -223,10 +223,11 @@ void sbbs_t::show_msghdr(smb_t* smb, const smbmsg_t* msg, const char* subject, c
 				,msg->upvotes, msg->user_voted==1 ? text[PollAnswerChecked] : nulstr
 				,msg->downvotes, msg->user_voted==2 ? text[PollAnswerChecked] : nulstr
 				,msg->upvotes - msg->downvotes);
+		time_t t = smb_time(msg->hdr.when_written);
 		bprintf(text[MsgDate]
-			,timestr(msg->hdr.when_written.time)
+			,timestr(t)
 			,smb_zonestr(msg->hdr.when_written.zone,NULL)
-			,age_of_posted_item(age, sizeof(age), msg->hdr.when_written.time - (smb_tzutc(msg->hdr.when_written.zone) * 60)));
+			,age_of_posted_item(age, sizeof(age), t - (smb_tzutc(msg->hdr.when_written.zone) * 60)));
 		bputs(text[MsgHdrBodySeparator]);
 	}
 	for(i=0;i<msg->total_hfields;i++) {
@@ -495,7 +496,7 @@ bool sbbs_t::msgtotxt(smb_t* smb, smbmsg_t* msg, const char *fname, bool header,
 		if(msg->from_net.addr)
 			fprintf(out," (%s)",smb_netaddrstr(&msg->from_net,tmp));
 		fprintf(out,"\r\nDate : %.24s %s"
-			,timestr(msg->hdr.when_written.time)
+			,timestr(smb_time(msg->hdr.when_written))
 			,smb_zonestr(msg->hdr.when_written.zone,NULL));
 		fprintf(out,"\r\n\r\n");
 	}
diff --git a/src/sbbs3/js_bbs.cpp b/src/sbbs3/js_bbs.cpp
index 0a3d67fc7f..2cb0ae208e 100644
--- a/src/sbbs3/js_bbs.cpp
+++ b/src/sbbs3/js_bbs.cpp
@@ -622,7 +622,7 @@ static JSBool js_bbs_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 			break;
 		case BBS_PROP_MSG_DATE:
 			if(sbbs->current_msg!=NULL)
-				val=sbbs->current_msg->hdr.when_written.time;
+				val=(uint32)smb_time(sbbs->current_msg->hdr.when_written);
 			break;
 		case BBS_PROP_MSG_TIMEZONE:
 			if(sbbs->current_msg!=NULL)
diff --git a/src/sbbs3/js_msgbase.c b/src/sbbs3/js_msgbase.c
index e444fad32d..60084c1805 100644
--- a/src/sbbs3/js_msgbase.c
+++ b/src/sbbs3/js_msgbase.c
@@ -893,7 +893,7 @@ static bool parse_header_object(JSContext* cx, private_t* p, JSObject* hdr, smbm
 	if(JS_GetProperty(cx, hdr, "when_written_time", &val) && !JSVAL_NULL_OR_VOID(val))  {
 		if(!JS_ValueToECMAUint32(cx,val,&u32))
 			goto err;
-		msg->hdr.when_written.time=u32;
+		msg->hdr.when_written = smb_when(u32, msg->hdr.when_written.zone);
 	}
 	if(JS_GetProperty(cx, hdr, "when_written_zone", &val) && !JSVAL_NULL_OR_VOID(val)) {
 		if(!JS_ValueToInt32(cx,val,&i32))
@@ -1438,7 +1438,7 @@ static JSBool js_get_msg_header_resolve(JSContext *cx, JSObject *obj, jsid id)
 	LAZY_UINTEGER("attr", p->msg.hdr.attr, JSPROP_ENUMERATE);
 	LAZY_UINTEGER("auxattr", p->msg.hdr.auxattr, JSPROP_ENUMERATE);
 	LAZY_UINTEGER("netattr", p->msg.hdr.netattr, JSPROP_ENUMERATE);
-	LAZY_UINTEGER("when_written_time", p->msg.hdr.when_written.time, JSPROP_ENUMERATE);
+	LAZY_UINTEGER("when_written_time", smb_time(p->msg.hdr.when_written), JSPROP_ENUMERATE);
 	LAZY_INTEGER("when_written_zone", p->msg.hdr.when_written.zone, JSPROP_ENUMERATE);
 	LAZY_INTEGER("when_written_zone_offset", smb_tzutc(p->msg.hdr.when_written.zone), JSPROP_ENUMERATE|JSPROP_READONLY);
 	LAZY_UINTEGER("when_imported_time", p->msg.hdr.when_imported.time, JSPROP_ENUMERATE);
diff --git a/src/sbbs3/mailsrvr.c b/src/sbbs3/mailsrvr.c
index 8dc9792b88..3fe49fb60d 100644
--- a/src/sbbs3/mailsrvr.c
+++ b/src/sbbs3/mailsrvr.c
@@ -3922,6 +3922,7 @@ static bool smtp_client_thread(smtp_t* smtp)
 					if (session != -1)
 						with_val |= WITH_TLS;
 
+					when_t when = { .time = msg.hdr.when_imported.time, .zone = msg.hdr.when_imported.zone };
 					snprintf(hdrfield,sizeof(hdrfield),
 						"from %s (%s [%s%s])\r\n"
 						"          by %s [%s%s] (%s %s%c-%s) with %s\r\n"
@@ -3936,7 +3937,7 @@ static bool smtp_client_thread(smtp_t* smtp)
 						,server_name
 						,VERSION, REVISION, PLATFORM_DESC
 						,with_clauses[with_val]
-						,forward_path,msgdate(msg.hdr.when_imported,date)
+						,forward_path,msgdate(when, date)
 						,reverse_path);
 					smb_hfield_add_str(&newmsg, SMTPRECEIVED, hdrfield, /* insert: */TRUE);
 
diff --git a/src/sbbs3/msgdate.c b/src/sbbs3/msgdate.c
index 02313a7215..9b956ba08e 100644
--- a/src/sbbs3/msgdate.c
+++ b/src/sbbs3/msgdate.c
@@ -40,7 +40,7 @@ char* msgdate(when_t when, char* buf)
 		tz=-tz;
 	}
 
-	tt=when.time;
+	tt=smb_time(when);
 	if(localtime_r(&tt,&tm)==NULL)
 		memset(&tm,0,sizeof(tm));
 	sprintf(buf,"%s, %d %s %d %02d:%02d:%02d %c%02u%02u"
diff --git a/src/sbbs3/msgtoqwk.cpp b/src/sbbs3/msgtoqwk.cpp
index f838d730cb..2cd6c94a05 100644
--- a/src/sbbs3/msgtoqwk.cpp
+++ b/src/sbbs3/msgtoqwk.cpp
@@ -103,7 +103,7 @@ int sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, int mode, smb_t* smb
 		/* Time/Date/Zone info */
 		fprintf(voting,"WhenWritten:  %-20s %04hx\n"
 			,xpDateTime_to_isoDateTimeStr(
-				time_to_xpDateTime(msg->hdr.when_written.time,smb_tzutc(msg->hdr.when_written.zone))
+				time_to_xpDateTime(smb_time(msg->hdr.when_written), smb_tzutc(msg->hdr.when_written.zone))
 				,/* separators: */"","","", /* precision: */0
 				,str,sizeof(str))
 			,msg->hdr.when_written.zone
@@ -131,7 +131,7 @@ int sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, int mode, smb_t* smb
 		/* Time/Date/Zone info */
 		fprintf(hdrs,"WhenWritten:  %-20s %04hx\n"
 			,xpDateTime_to_isoDateTimeStr(
-				time_to_xpDateTime(msg->hdr.when_written.time,smb_tzutc(msg->hdr.when_written.zone))
+				time_to_xpDateTime(smb_time(msg->hdr.when_written), smb_tzutc(msg->hdr.when_written.zone))
 				,/* separators: */"","","", /* precision: */0
 				,str,sizeof(str))
 			,msg->hdr.when_written.zone
@@ -527,7 +527,7 @@ int sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, int mode, smb_t* smb
 	}
 	free(buf);
 
-	tt=msg->hdr.when_written.time;
+	tt=smb_time(msg->hdr.when_written);
 	if(localtime_r(&tt,&tm)==NULL)
 		memset(&tm,0,sizeof(tm));
 
diff --git a/src/sbbs3/netmail.cpp b/src/sbbs3/netmail.cpp
index 5476c839d7..1048374f70 100644
--- a/src/sbbs3/netmail.cpp
+++ b/src/sbbs3/netmail.cpp
@@ -544,7 +544,7 @@ void sbbs_t::qwktonetmail(FILE *rep, char *block, char *into, uchar fromhub)
 		tm.tm_sec=0;
 
 		tm.tm_isdst=-1;	/* Do not adjust for DST */
-		msg.hdr.when_written.time=mktime32(&tm);
+		msg.hdr.when_written = smb_when(mktime(&tm), msg.hdr.when_written.zone);
 
 		if(subject == NULL) {
 			snprintf(str, sizeof str, "%.25s", block+71);
@@ -1121,8 +1121,9 @@ bool sbbs_t::inetmail(const char *into, const char *subj, int mode, smb_t* resmb
 	msg.hdr.version=smb_ver();
 	if(mode&WM_FILE)
 		msg.hdr.auxattr |= (MSG_FILEATTACH | MSG_KILLFILE);
-	msg.hdr.when_written.time=msg.hdr.when_imported.time=time32(NULL);
-	msg.hdr.when_written.zone=msg.hdr.when_imported.zone=sys_timezone(&cfg);
+	msg.hdr.when_written = smb_when(time(NULL), sys_timezone(&cfg));
+	msg.hdr.when_imported.time = time32(NULL);
+	msg.hdr.when_imported.zone = sys_timezone(&cfg);
 
 	msg.hdr.netattr |= NETMSG_LOCAL;
 	if(cfg.inetmail_misc&NMAIL_KILL)
@@ -1405,8 +1406,9 @@ bool sbbs_t::qnetmail(const char *into, const char *subj, int mode, smb_t* resmb
 	msg.hdr.version=smb_ver();
 	if(mode&WM_FILE)
 		msg.hdr.auxattr |= (MSG_FILEATTACH | MSG_KILLFILE);
-	msg.hdr.when_written.time=msg.hdr.when_imported.time=time32(NULL);
-	msg.hdr.when_written.zone=msg.hdr.when_imported.zone=sys_timezone(&cfg);
+	msg.hdr.when_written = smb_when(time(NULL), sys_timezone(&cfg));
+	msg.hdr.when_imported.time = time32(NULL);
+	msg.hdr.when_imported.zone = sys_timezone(&cfg);
 
 	msg.hdr.offset=(uint32_t)offset;
 
diff --git a/src/sbbs3/postmsg.cpp b/src/sbbs3/postmsg.cpp
index f98e00b518..7feab19d35 100644
--- a/src/sbbs3/postmsg.cpp
+++ b/src/sbbs3/postmsg.cpp
@@ -113,7 +113,7 @@ bool sbbs_t::postmsg(int subnum, int wm_mode, smb_t* resmb, smbmsg_t* remsg)
 			,title
 			,from
 			,msghdr_field(remsg, remsg->to, NULL, term_supports(UTF8))
-			,timestr(remsg->hdr.when_written.time)
+			,timestr(smb_time(remsg->hdr.when_written))
 			,smb_zonestr(remsg->hdr.when_written.zone,NULL)));
 		if(remsg->tags != NULL)
 			SAFECOPY(tags, remsg->tags);
@@ -285,8 +285,9 @@ bool sbbs_t::postmsg(int subnum, int wm_mode, smb_t* resmb, smbmsg_t* remsg)
 
 	memset(&msg,0,sizeof(msg));
 	msg.hdr.attr=msgattr;
-	msg.hdr.when_written.time=msg.hdr.when_imported.time=time32(NULL);
-	msg.hdr.when_written.zone=msg.hdr.when_imported.zone=sys_timezone(&cfg);
+	msg.hdr.when_written = smb_when(time(NULL), sys_timezone(&cfg));
+	msg.hdr.when_imported.time = time32(NULL);
+	msg.hdr.when_imported.zone = msg.hdr.when_written.zone;
 
 	msg.hdr.number=smb.status.last_msg+1; /* this *should* be the new message number */
 
@@ -491,8 +492,8 @@ extern "C" int savemsg(scfg_t* cfg, smb_t* smb, smbmsg_t* msg, client_t* client,
 		msg->hdr.when_imported.time=time32(NULL);
 		msg->hdr.when_imported.zone=sys_timezone(cfg);
 	}
-	if(msg->hdr.when_written.time==0)	/* Uninitialized */
-		msg->hdr.when_written = msg->hdr.when_imported;
+	if(msg->hdr.when_written.time==0) 	/* Uninitialized */
+		msg->hdr.when_written = smb_when(msg->hdr.when_imported.time, msg->hdr.when_imported.zone);
 
 	msg->hdr.number=smb->status.last_msg+1;		/* needed for MSG-ID generation */
 
@@ -569,8 +570,6 @@ extern "C" int votemsg(scfg_t* cfg, smb_t* smb, smbmsg_t* msg, const char* smsgf
 		msg->hdr.when_imported.time = time32(NULL);
 		msg->hdr.when_imported.zone = sys_timezone(cfg);
 	}
-	if(msg->hdr.when_written.time == 0)	/* Uninitialized */
-		msg->hdr.when_written = msg->hdr.when_imported;
 
 	add_msg_ids(cfg, smb, msg, /* remsg: */NULL);
 
@@ -623,7 +622,7 @@ extern "C" int votemsg(scfg_t* cfg, smb_t* smb, smbmsg_t* msg, const char* smsgf
 				}
 			}
 			safe_snprintf(smsg, sizeof(smsg), smsgfmt
-				,timestr(cfg, msg->hdr.when_written.time, tstr)
+				,timestr(cfg, (time32_t)smb_time(msg->hdr.when_written), tstr)
 				,cfg->grp[cfg->sub[smb->subnum]->grp]->sname
 				,cfg->sub[smb->subnum]->sname
 				,from
@@ -645,7 +644,7 @@ extern "C" int closepoll(scfg_t* cfg, smb_t* smb, uint32_t msgnum, const char* u
 
 	msg.hdr.when_imported.time = time32(NULL);
 	msg.hdr.when_imported.zone = sys_timezone(cfg);
-	msg.hdr.when_written = msg.hdr.when_imported;
+	msg.hdr.when_written = smb_when(time(NULL), msg.hdr.when_imported.zone);
 	msg.hdr.thread_back = msgnum;
 	smb_hfield_str(&msg, SENDER, username);
 
@@ -664,7 +663,7 @@ extern "C" int postpoll(scfg_t* cfg, smb_t* smb, smbmsg_t* msg)
 		msg->hdr.when_imported.zone = sys_timezone(cfg);
 	}
 	if(msg->hdr.when_written.time == 0)
-		msg->hdr.when_written = msg->hdr.when_imported;
+		msg->hdr.when_written = smb_when(msg->hdr.when_imported.time, msg->hdr.when_imported.zone);
 
 	add_msg_ids(cfg, smb, msg, /* remsg: */NULL);
 
@@ -691,7 +690,7 @@ extern "C" int notify(scfg_t* cfg, uint usernumber, const char* subject, const c
 
 	msg.hdr.when_imported.time = time32(NULL);
 	msg.hdr.when_imported.zone = sys_timezone(cfg);
-	msg.hdr.when_written = msg.hdr.when_imported;
+	msg.hdr.when_written = smb_when(time(NULL), msg.hdr.when_imported.zone);
 	smb_hfield(&msg, SENDERAGENT, sizeof(agent), &agent);
 	smb_hfield_str(&msg, SENDER, cfg->sys_name);
 	smb_hfield_str(&msg, RECIPIENT, user.alias);
diff --git a/src/sbbs3/qwk.cpp b/src/sbbs3/qwk.cpp
index b384dea4b2..992a400640 100644
--- a/src/sbbs3/qwk.cpp
+++ b/src/sbbs3/qwk.cpp
@@ -1027,8 +1027,7 @@ bool sbbs_t::qwk_vote(str_list_t ini, const char* section, smb_net_type_t net_ty
 	if((p=iniGetString(ini, section, "WhenWritten", NULL, NULL)) != NULL) {
 		char	zone[32];
 		xpDateTime_t dt=isoDateTimeStr_parse(p);
-		msg.hdr.when_written.time=(uint32_t)xpDateTime_to_localtime(dt);
-		msg.hdr.when_written.zone=dt.zone;
+		msg.hdr.when_written = smb_when(xpDateTime_to_localtime(dt), dt.zone);
 		sscanf(p,"%*s %s",zone);
 		if(zone[0])
 			msg.hdr.when_written.zone=(ushort)strtoul(zone,NULL,16);
@@ -1163,12 +1162,12 @@ bool sbbs_t::qwk_vote(str_list_t ini, const char* section, smb_net_type_t net_ty
 bool sbbs_t::qwk_msg_filtered(smbmsg_t* msg, msg_filters filters)
 {
 	uint32_t now = time32(NULL);
-
-	if(cfg.max_qwkmsgage && msg->hdr.when_written.time < now
-		&& (now-msg->hdr.when_written.time)/(24*60*60) > cfg.max_qwkmsgage) {
+	time_t when_written = smb_time(msg->hdr.when_written);
+	if(cfg.max_qwkmsgage && when_written < now
+		&& (now-when_written)/(24*60*60) > cfg.max_qwkmsgage) {
 		lprintf(LOG_NOTICE,"!Filtering QWK message from %s due to age: %u days"
 			,msg->from
-			,(unsigned int)(now-msg->hdr.when_written.time)/(24*60*60)); 
+			,(unsigned int)(now-when_written)/(24*60*60));
 		return true;
 	}
 
diff --git a/src/sbbs3/qwknodes.c b/src/sbbs3/qwknodes.c
index 9be245ae82..93e500c3f6 100644
--- a/src/sbbs3/qwknodes.c
+++ b/src/sbbs3/qwknodes.c
@@ -330,7 +330,7 @@ int main(int argc, char **argv)
 							else
 								*(p++)=0;
 							safe_snprintf(str, sizeof(str), "%s %s:%s%c%s"
-								,unixtodstr(&cfg,msg.hdr.when_written.time,tmp)
+								,unixtodstr(&cfg,smb_time(msg.hdr.when_written),tmp)
 								,p,cfg.sys_id,p==addr ? 0 : '/'
 								,addr);
 							fprintf(route,"%s\r\n",str); 
@@ -340,7 +340,7 @@ int main(int argc, char **argv)
 							if(p) {
 								*(p++)=0;
 							fprintf(route,"%s %s:%.*s\r\n"
-								,unixtodstr(&cfg,msg.hdr.when_written.time,str)
+								,unixtodstr(&cfg,smb_time(msg.hdr.when_written),str)
 								,p
 								,(uint)(p-addr)
 								,addr); 
@@ -358,12 +358,12 @@ int main(int argc, char **argv)
 						if(p)
 							fprintf(users,"%-25.25s  %-8.8s  %s  (%s)\r\n"
 								,msg.from,p+1
-								,unixtodstr(&cfg,msg.hdr.when_written.time,tmp)
+								,unixtodstr(&cfg,smb_time(msg.hdr.when_written),tmp)
 								,str);
 						else
 							fprintf(users,"%-25.25s  %-8.8s  %s\r\n"
 								,msg.from,str
-								,unixtodstr(&cfg,msg.hdr.when_written.time,tmp)); 
+								,unixtodstr(&cfg,smb_time(msg.hdr.when_written),tmp));
 					}
 					if(cmd&NODES && msg.from_net.type==NET_QWK) {
 						if(mode&TAGS)
@@ -381,7 +381,7 @@ int main(int argc, char **argv)
 							else
 								fprintf(nodes,"%-8.8s  %s  (%s)\r\n"
 									,p+1
-									,unixtodstr(&cfg,msg.hdr.when_written.time,tmp)
+									,unixtodstr(&cfg,smb_time(msg.hdr.when_written),tmp)
 									,str); 
 						}
 						else
@@ -389,7 +389,7 @@ int main(int argc, char **argv)
 								,str
 								,mode&TAGS
 								? tag
-								: unixtodstr(&cfg,msg.hdr.when_written.time,tmp)); 
+								: unixtodstr(&cfg,smb_time(msg.hdr.when_written),tmp));
 					}
 				} 
 			}
diff --git a/src/sbbs3/qwktomsg.cpp b/src/sbbs3/qwktomsg.cpp
index 54b792bdb0..91ae002384 100644
--- a/src/sbbs3/qwktomsg.cpp
+++ b/src/sbbs3/qwktomsg.cpp
@@ -46,8 +46,7 @@ static bool qwk_parse_header_list(sbbs_t* sbbs, uint confnum, smbmsg_t* msg, str
 	if((p=iniPopKey(headers,ROOT_SECTION,"WhenWritten",value))!=NULL) {
 		xpDateTime_t dt=isoDateTimeStr_parse(p);
 
-		msg->hdr.when_written.time=(uint32_t)xpDateTime_to_localtime(dt);
-		msg->hdr.when_written.zone=dt.zone;
+		msg->hdr.when_written = smb_when(xpDateTime_to_localtime(dt), dt.zone);
 		sscanf(p,"%*s %s",zone);
 		if(zone[0])
 			msg->hdr.when_written.zone=(ushort)strtoul(zone,NULL,16);
@@ -195,7 +194,7 @@ bool sbbs_t::qwk_new_msg(uint confnum, smbmsg_t* msg, char* hdrblk, int offset,
 		tm.tm_hour=((hdrblk[16]&0xf)*10)+(hdrblk[17]&0xf);
 		tm.tm_min=((hdrblk[19]&0xf)*10)+(hdrblk[20]&0xf);
 
-		msg->hdr.when_written.time=(uint32_t)sane_mktime(&tm);
+		msg->hdr.when_written = smb_when(sane_mktime(&tm), msg->hdr.when_written.zone);
 	}
 
 	if(msg->to==NULL)
@@ -263,7 +262,7 @@ bool sbbs_t::qwk_import_msg(FILE *qwk_fp, char *hdrblk, uint blocks
 	msg->hdr.when_imported.zone=sys_timezone(&cfg);
 
 	if(!(useron.rest&FLAG('Q')) && !fromhub && msg->hdr.when_written.zone==0)
-		msg->hdr.when_written = msg->hdr.when_imported;
+		msg->hdr.when_written = smb_when(msg->hdr.when_imported.time, msg->hdr.when_imported.zone);
 
 	hdrblk[116]=0;	// don't include number of blocks in "re: msg number"
 	if(!(useron.rest&FLAG('Q')) && !fromhub)
diff --git a/src/sbbs3/readmail.cpp b/src/sbbs3/readmail.cpp
index 51fe567f10..48c2d7cb67 100644
--- a/src/sbbs3/readmail.cpp
+++ b/src/sbbs3/readmail.cpp
@@ -348,7 +348,7 @@ int sbbs_t::readmail(uint usernumber, int which, int lm_mode)
 				else						/* Reply to other */
 					text_num = RegardingByOn;
 				SAFECOPY(str2, format_text(text_num, msghdr_field(&msg, msg.subj), msghdr_field(&msg, msg.from, tmp)
-						,timestr(msg.hdr.when_written.time)));
+						,timestr(smb_time(msg.hdr.when_written))));
 
 				p=strrchr(str,'@');
 				if(p) { 							/* name @addr */
diff --git a/src/sbbs3/readmsgs.cpp b/src/sbbs3/readmsgs.cpp
index 6e4047e4a0..68b8bd95e1 100644
--- a/src/sbbs3/readmsgs.cpp
+++ b/src/sbbs3/readmsgs.cpp
@@ -372,7 +372,7 @@ void sbbs_t::show_thread(uint32_t msgnum, post_t* post, unsigned curmsg, int thr
 			? text[Anonymous] : msghdr_field(&msg, msg.from)
 		,(unsigned)i == curmsg ? '<' : ' '
 		,msg_listing_flag(smb.subnum, &msg, &post[i])
-		,datestr(msg.hdr.when_written.time));
+		,datestr(smb_time(msg.hdr.when_written)));
 
 	if(thread_depth) {
 		if(msg.hdr.thread_first)
@@ -1010,7 +1010,7 @@ int sbbs_t::scanposts(int subnum, int mode, const char *find)
 					break;
 				SAFECOPY(str2, format_text(Regarding
 						,msghdr_field(&msg, msg.subj)
-						,timestr(msg.hdr.when_written.time)));
+						,timestr(smb_time(msg.hdr.when_written))));
 				if(msg.from_net.addr==NULL)
 					SAFECOPY_UTF8(str,msg.from);
 				else if(msg.from_net.type==NET_FIDO)
diff --git a/src/sbbs3/sbbsecho.c b/src/sbbs3/sbbsecho.c
index 29c1764cf4..4b4e779343 100644
--- a/src/sbbs3/sbbsecho.c
+++ b/src/sbbs3/sbbsecho.c
@@ -114,7 +114,7 @@ int lprintf(int level, char *fmt, ...)
 #endif
 ;
 int mv(const char *insrc, const char *indest, bool copy);
-time32_t fmsgtime(const char *str);
+time_t fmsgtime(const char *str);
 ulong export_echomail(const char *sub_code, const nodecfg_t*, bool rescan);
 const char* area_desc(const char* areatag);
 
@@ -380,7 +380,7 @@ echostat_msg_t* smsg_to_echostat_msg(const smbmsg_t* smsg, size_t msglen, fidoad
 	SAFECOPY(emsg.to	, smsg->to);
 	SAFECOPY(emsg.from	, smsg->from);
 	SAFECOPY(emsg.subj	, smsg->subj);
-	emsg.msg_time		= smsg->hdr.when_written.time;
+	emsg.msg_time		= smb_time(smsg->hdr.when_written);
 	emsg.localtime		= time(NULL);
 	SAFECOPY(emsg.msg_tz, smb_zonestr(smsg->hdr.when_written.zone, NULL));
 	if(smsg->from_net.type == NET_FIDO && smsg->from_net.addr != NULL)
@@ -1050,8 +1050,7 @@ int create_netmail(const char *to, const smbmsg_t* msg, const char *subject, con
 	bool	direct=false;
 
 	if(msg==NULL) {
-		when_written.time = time32(NULL);
-		when_written.zone = sys_timezone(&scfg);
+		when_written = smb_when(time(NULL), sys_timezone(&scfg));
 	} else {
 		from = msg->from;
 		when_written = msg->hdr.when_written;
@@ -1130,7 +1129,7 @@ int create_netmail(const char *to, const smbmsg_t* msg, const char *subject, con
 		direct = nodecfg->direct;
 	}
 
-	t = when_written.time;
+	t = smb_time(when_written);
 	tm = localtime(&t);
 	snprintf(hdr.time, sizeof hdr.time, "%02u %3.3s %02u  %02u:%02u:%02u"
 		,tm->tm_mday,mon[tm->tm_mon],TM_YEAR(tm->tm_year)
@@ -3207,7 +3206,7 @@ link_list_t user_list;
 /****************************************************************************/
 /* Converts goofy FidoNet time format into Unix format						*/
 /****************************************************************************/
-time32_t fmsgtime(const char *str)
+time_t fmsgtime(const char *str)
 {
 	char month[4];
 	struct tm tm;
@@ -3284,7 +3283,7 @@ time32_t fmsgtime(const char *str)
 		tm.tm_min=atoi(str+17);
 		tm.tm_sec=0;
 	}
-	return(mktime32(&tm));
+	return mktime(&tm);
 }
 
 static short fmsgzone(const char* p)
@@ -3410,16 +3409,17 @@ int fmsgtosmsg(char* fbuf, fmsghdr_t* hdr, uint usernumber, uint subnum)
 	msg.hdr.when_imported.time=now;
 	msg.hdr.when_imported.zone=sys_timezone(&scfg);
 	if(hdr->time[0]) {
-		msg.hdr.when_written.time=fmsgtime(hdr->time);
-		if(max_msg_age && (time32_t)msg.hdr.when_written.time < now
-			&& (now - msg.hdr.when_written.time) > max_msg_age) {
+		msg.hdr.when_written = smb_when(fmsgtime(hdr->time), msg.hdr.when_written.zone);
+		time_t when_written = smb_time(msg.hdr.when_written);
+		if(max_msg_age && when_written < now
+			&& (now - when_written) > max_msg_age) {
 			lprintf(LOG_INFO, "Filtering message from %s due to age: %1.1f days"
 				,hdr->from
-				,(now - msg.hdr.when_written.time) / (24.0*60.0*60.0));
+				,(now - when_written) / (24.0*60.0*60.0));
 			return IMPORT_FILTERED_AGE;
 		}
 	} else
-		msg.hdr.when_written = msg.hdr.when_imported;
+		msg.hdr.when_written = smb_when(msg.hdr.when_imported.time, msg.hdr.when_imported.zone);
 
 	origaddr.zone=hdr->origzone; 	/* only valid if NetMail */
 	origaddr.net=hdr->orignet;
@@ -4658,7 +4658,7 @@ int import_netmail(const char* path, const fmsghdr_t* inhdr, FILE* fp, const cha
 							,timestr(&scfg, time32(NULL), tmp), smb_zonestr(sys_timezone(&scfg),NULL));
 						bodylen += sprintf(body+bodylen, "by: %s (sysop: %s) @ %s\r"
 							,scfg.sys_name, scfg.sys_op, smb_faddrtoa(&scfg.faddr[match], NULL));
-						time_t t = (time_t)fmsgtime(hdr.time);
+						time_t t = fmsgtime(hdr.time);
 						bodylen += sprintf(body+bodylen, "\rThe received message header contained:\r\r"
 							"Subj: %s\r"
 							"Attr: %04hX%s\r"
@@ -4994,8 +4994,9 @@ ulong export_echomail(const char* sub_code, const nodecfg_t* nodecfg, bool resca
 				continue;
 			}
 
+			time_t when_written = smb_time(msg.hdr.when_written);
 			if(!rescan && cfg.max_echomail_age != 0
-				&& ((now > msg.hdr.when_written.time && (now - msg.hdr.when_written.time) > cfg.max_echomail_age)
+				&& ((now > when_written && (now - when_written) > cfg.max_echomail_age)
 				|| (now > msg.hdr.when_imported.time && (now - msg.hdr.when_imported.time) > cfg.max_echomail_age))) {
 				smb_unlockmsghdr(&smb, &msg);
 				smb_freemsgmem(&msg);
@@ -5014,7 +5015,7 @@ ulong export_echomail(const char* sub_code, const nodecfg_t* nodecfg, bool resca
 
 			SAFECOPY(hdr.from,msg.from);
 
-			tt = msg.hdr.when_written.time;
+			tt = smb_time(msg.hdr.when_written);
 			if((tm = localtime(&tt)) != NULL)
 				sprintf(hdr.time,"%02u %3.3s %02u  %02u:%02u:%02u"
 					,tm->tm_mday,mon[tm->tm_mon],TM_YEAR(tm->tm_year)
diff --git a/src/sbbs3/smbutil.c b/src/sbbs3/smbutil.c
index 820f0d32e0..f5a1859673 100644
--- a/src/sbbs3/smbutil.c
+++ b/src/sbbs3/smbutil.c
@@ -246,9 +246,9 @@ void postmsg(char type, char* to, char* to_number, char* to_address,
 	}
 
 	memset(&msg,0,sizeof(smbmsg_t));
-	msg.hdr.when_written.time=(uint32_t)time(NULL);
-	msg.hdr.when_written.zone=tzone;
-	msg.hdr.when_imported=msg.hdr.when_written;
+	msg.hdr.when_written = smb_when(time(NULL), tzone);
+	msg.hdr.when_imported.time = (time32_t)time(NULL);
+	msg.hdr.when_imported.zone = msg.hdr.when_written.zone;
 
 	if((to==NULL || stricmp(to,"All")==0) && to_address!=NULL)
 		to=to_address;
@@ -566,7 +566,7 @@ void listmsgs(ulong start, ulong count)
 		}
 		printf("%4lu/#%-4"PRIu32" ", start + l, msg.hdr.number);
 		if(verbosity > 0)
-			printf("%s ",my_timestr(msg.hdr.when_written.time));
+			printf("%s ",my_timestr(smb_time(msg.hdr.when_written)));
 		if(verbosity > 1)
 			printf("%-9s ", smb_zonestr(msg.hdr.when_written.zone,NULL));
 		printf("%-25.25s", msg.from);
@@ -1570,7 +1570,7 @@ void readmsgs(ulong start, ulong count)
 			if(msg.hdr.auxattr != 0)
 				printf("\nAux  : %08X (%s)", msg.hdr.auxattr, smb_auxattrstr(msg.hdr.auxattr, tmp, sizeof(tmp)));
 			if(msg.hdr.netattr != 0)
-				printf("\nNet  : %08X (%s)", msg.hdr.netattr, smb_netattrstr(msg.hdr.netattr, tmp, sizeof(tmp)));
+				printf("\nNet  : %04X (%s)", msg.hdr.netattr, smb_netattrstr(msg.hdr.netattr, tmp, sizeof(tmp)));
 			if(*msg.to) {
 				printf("\nTo   : %s",msg.to);
 				if(msg.to_net.type)
@@ -1580,7 +1580,7 @@ void readmsgs(ulong start, ulong count)
 			if(msg.from_net.type)
 				printf(" (%s)",smb_netaddr(&msg.from_net));
 			printf("\nDate : %.24s %s"
-				,my_timestr(msg.hdr.when_written.time)
+				,my_timestr(smb_time(msg.hdr.when_written))
 				,smb_zonestr(msg.hdr.when_written.zone,NULL));
 
 			printf("\n%s\n", msg.summary ? msg.summary : "");
diff --git a/src/sbbs3/writemsg.cpp b/src/sbbs3/writemsg.cpp
index 52da551fdb..6f21110da2 100644
--- a/src/sbbs3/writemsg.cpp
+++ b/src/sbbs3/writemsg.cpp
@@ -1481,7 +1481,7 @@ bool sbbs_t::forwardmsg(smb_t* smb, smbmsg_t* orgmsg, const char* to, const char
 	msg.hdr.auxattr = orgmsg->hdr.auxattr & (MSG_HFIELDS_UTF8 | MSG_MIMEATTACH);
 	msg.hdr.when_imported.time = time32(NULL);
 	msg.hdr.when_imported.zone = sys_timezone(&cfg);
-	msg.hdr.when_written = msg.hdr.when_imported;
+	msg.hdr.when_written = smb_when(msg.hdr.when_imported.time, msg.hdr.when_imported.zone);
 
 	smb_hfield_str(&msg, SUBJECT, subject);
 	add_msg_ids(&cfg, smb, &msg, orgmsg);
diff --git a/src/smblib/smbadd.c b/src/smblib/smbadd.c
index 8eacdae663..3b0a038c49 100644
--- a/src/smblib/smbadd.c
+++ b/src/smblib/smbadd.c
@@ -26,6 +26,7 @@
 #include "crc32.h"
 #include "lzh.h"
 #include "datewrap.h"
+#include "xpdatetime.h"
 
 /****************************************************************************/
 /****************************************************************************/
@@ -235,11 +236,11 @@ int smb_addmsg(smb_t* smb, smbmsg_t* msg, int storage, int dupechk_hashes
 		}
 
 		if(msg->hdr.when_imported.time==0) {
-			msg->hdr.when_imported.time=(uint32_t)time(NULL);
-			msg->hdr.when_imported.zone=0;	/* how do we detect system TZ? */
+			msg->hdr.when_imported.time = (uint32_t)time(NULL);
+			msg->hdr.when_imported.zone = xpTimeZone_local();
 		}
-		if(msg->hdr.when_written.time==0)	/* Uninitialized */
-			msg->hdr.when_written = msg->hdr.when_imported;
+		if(msg->hdr.when_written.time==0) 	/* Uninitialized */
+			msg->hdr.when_written = smb_when(msg->hdr.when_imported.time, msg->hdr.when_imported.zone);
 
 		/* Look-up thread_back if RFC822 Reply-ID was specified */
 		if(msg->hdr.thread_back==0 && msg->reply_id!=NULL) {
@@ -339,10 +340,10 @@ int smb_addvote(smb_t* smb, smbmsg_t* msg, int storage)
 
 	if(msg->hdr.when_imported.time == 0) {
 		msg->hdr.when_imported.time = (uint32_t)time(NULL);
-		msg->hdr.when_imported.zone = 0;
+		msg->hdr.when_imported.zone = xpTimeZone_local();
 	}
 	if(msg->hdr.when_written.time == 0)	/* Uninitialized */
-		msg->hdr.when_written = msg->hdr.when_imported;
+		msg->hdr.when_written = smb_when(msg->hdr.when_imported.time, msg->hdr.when_imported.zone);
 
 	retval = smb_addmsghdr(smb, msg, storage);
 
@@ -379,10 +380,10 @@ int smb_addpoll(smb_t* smb, smbmsg_t* msg, int storage)
 
 	if(msg->hdr.when_imported.time == 0) {
 		msg->hdr.when_imported.time = (uint32_t)time(NULL);
-		msg->hdr.when_imported.zone = 0;
+		msg->hdr.when_imported.zone = xpTimeZone_local();
 	}
 	if(msg->hdr.when_written.time == 0)	/* Uninitialized */
-		msg->hdr.when_written = msg->hdr.when_imported;
+		msg->hdr.when_written = smb_when(msg->hdr.when_imported.time, msg->hdr.when_imported.zone);
 
 	retval = smb_addmsghdr(smb, msg, storage);
 
@@ -446,10 +447,10 @@ int smb_addpollclosure(smb_t* smb, smbmsg_t* msg, int storage)
 
 	if(msg->hdr.when_imported.time == 0) {
 		msg->hdr.when_imported.time = (uint32_t)time(NULL);
-		msg->hdr.when_imported.zone = 0;
+		msg->hdr.when_imported.zone = xpTimeZone_local();
 	}
 	if(msg->hdr.when_written.time == 0)	/* Uninitialized */
-		msg->hdr.when_written = msg->hdr.when_imported;
+		msg->hdr.when_written = smb_when(msg->hdr.when_imported.time, msg->hdr.when_imported.zone);
 
 	retval = smb_addmsghdr(smb, msg, storage);
 
diff --git a/src/smblib/smbdefs.h b/src/smblib/smbdefs.h
index 5d41aabd9b..899575e904 100644
--- a/src/smblib/smbdefs.h
+++ b/src/smblib/smbdefs.h
@@ -361,11 +361,26 @@ enum smb_priority {			/* msghdr_t.priority */
 
 typedef struct {		/* Time with time-zone */
 
-	uint32_t	time;			/* Local time (unix format) */
+	uint16_t	year;
+	uint32_t	time;			/* Month/Day/Hour/Minute/Second or (legacy) time_t */
 	int16_t		zone;			/* Time zone */
 
 } when_t;
 
+// We encode Month/Day/Hour/Min/Sec into 26 bits:
+#define SMB_DATE_MK_MASK(width, shift)	(((1 << (width + 1)) - 1) << shift)
+#define SMB_DATE_MON_SHIFT		22
+#define SMB_DATE_MON_MASK		SMB_DATE_MK_MASK(4, SMB_DATE_MON_SHIFT)
+#define SMB_DATE_DAY_SHIFT		17
+#define SMB_DATE_DAY_MASK		SMB_DATE_MK_MASK(5, SMB_DATE_DAY_SHIFT)
+#define SMB_DATE_HR_SHIFT		12
+#define SMB_DATE_HR_MASK		SMB_DATE_MK_MASK(5, SMB_DATE_HR_SHIFT)
+#define SMB_DATE_MIN_SHIFT		6
+#define SMB_DATE_MIN_MASK		SMB_DATE_MK_MASK(6, SMB_DATE_MIN_SHIFT)
+#define SMB_DATE_SEC_SHIFT		0
+#define SMB_DATE_SEC_MASK		SMB_DATE_MK_MASK(6, SMB_DATE_SEC_SHIFT)
+#define SMB_DATE_MASK			(SMB_DATE_MON_MASK | SMB_DATE_DAY_MASK | SMB_DATE_HR_MASK | SMB_DATE_MIN_MASK | SMB_DATE_SEC_MASK)
+
 typedef uint16_t smb_msg_attr_t;
 
 typedef struct {	/* Index record */
@@ -503,9 +518,12 @@ typedef struct {		/* Message/File header */
     /* 08 */ uint16_t	length;				/* Total length of fixed record + all fields */
 	/* 0a */ uint16_t	attr;				/* Attributes (bit field) (duped in SID) */
 	/* 0c */ uint32_t	auxattr;			/* Auxiliary attributes (bit field) */
-    /* 10 */ uint32_t	netattr;			/* Network attributes */
-	/* 14 */ when_t		when_written;		/* Date/time/zone message was written */
-	/* 1a */ when_t		when_imported;		/* Date/time/zone message was imported */
+    /* 10 */ uint16_t	netattr;			/* Network attributes */
+	/* 12 */ when_t		when_written;		/* Date/time/zone message was written */
+	/* 1a */ struct {
+				uint32_t time;
+				int16_t  zone;
+			} when_imported;				/* Date/time/zone message was imported */
     /* 20 */ uint32_t	number;				/* Message number */
     /* 24 */ uint32_t	thread_back;		/* Message number for backwards threading (aka thread_orig) */
     /* 28 */ uint32_t	thread_next;		/* Next message in thread */
diff --git a/src/smblib/smbdump.c b/src/smblib/smbdump.c
index 1c90695def..120a79120b 100644
--- a/src/smblib/smbdump.c
+++ b/src/smblib/smbdump.c
@@ -118,7 +118,7 @@ str_list_t smb_msghdr_str_list(smbmsg_t* msg)
 	}
 
 	/* fixed header fields */
-	tt=msg->hdr.when_written.time;
+	tt=smb_time(msg->hdr.when_written);
 	strListAppendFormat(&list, HFIELD_NAME_FMT "%08X %04hX %.24s %s"	,"when_written"
 		,msg->hdr.when_written.time, msg->hdr.when_written.zone
 		,ctime_r(&tt, tmp)
diff --git a/src/smblib/smblib.c b/src/smblib/smblib.c
index bc2c3b7e98..15fb38d438 100644
--- a/src/smblib/smblib.c
+++ b/src/smblib/smblib.c
@@ -34,10 +34,11 @@
 #include "smblib.h"
 #include "genwrap.h"
 #include "filewrap.h"
+#include "datewrap.h"
 
 /* Use smb_ver() and smb_lib_ver() to obtain these values */
-#define SMBLIB_VERSION		"3.00"      /* SMB library version */
-#define SMB_VERSION 		0x0300		/* SMB format version */
+#define SMBLIB_VERSION		"3.10"      /* SMB library version */
+#define SMB_VERSION 		0x0310		/* SMB format version */
 										/* High byte major, low byte minor */
 
 static char* nulstr="";
@@ -2117,6 +2118,46 @@ int smb_tzutc(int16_t zone)
 	return(tz);
 }
 
+/****************************************************************************/
+/* Decode the 2 possible encoding of when_t (when_written)					*/
+/****************************************************************************/
+time_t smb_time(when_t when)
+{
+	struct tm tm = {0};
+
+	if(when.time & ~SMB_DATE_MASK)
+		return when.time;
+
+	tm.tm_year = when.year;
+	tm.tm_mon = (when.time & SMB_DATE_MON_MASK) >> SMB_DATE_MON_SHIFT;
+	tm.tm_mday = (when.time & SMB_DATE_DAY_MASK) >> SMB_DATE_DAY_SHIFT;
+	tm.tm_hour = (when.time & SMB_DATE_HR_MASK) >> SMB_DATE_HR_SHIFT;
+	tm.tm_min = (when.time & SMB_DATE_MIN_MASK) >> SMB_DATE_MIN_SHIFT;
+	tm.tm_sec = (when.time & SMB_DATE_SEC_MASK) >> SMB_DATE_SEC_SHIFT;
+
+	return sane_mktime(&tm);
+}
+
+/****************************************************************************/
+/* Encode a time_t value into the new when_written.time (date) format		*/
+/****************************************************************************/
+when_t smb_when(time_t t, int16_t zone)
+{
+	struct tm tm = {0};
+	when_t when = {0};
+
+	localtime_r(&t, &tm);
+	when.year = 1900 + tm.tm_year;
+	when.time = (tm.tm_mon + 1) << SMB_DATE_MON_SHIFT;
+	when.time |= tm.tm_mday << SMB_DATE_DAY_SHIFT;
+	when.time |= tm.tm_hour << SMB_DATE_HR_SHIFT;
+	when.time |= tm.tm_min << SMB_DATE_MIN_SHIFT;
+	when.time |= tm.tm_sec << SMB_DATE_SEC_SHIFT;
+	when.zone = zone;
+
+	return when;
+}
+
 /****************************************************************************/
 /* The caller needs to call smb_unlockmsghdr(smb,remsg)						*/
 /****************************************************************************/
diff --git a/src/smblib/smblib.h b/src/smblib/smblib.h
index 7abec46af1..832e8986ba 100644
--- a/src/smblib/smblib.h
+++ b/src/smblib/smblib.h
@@ -162,6 +162,8 @@ SMBEXPORT uint		smb_hdrblocks(uint length);
 SMBEXPORT uint		smb_datblocks(off_t length);
 SMBEXPORT int		smb_copymsgmem(smb_t*, smbmsg_t* destmsg, smbmsg_t* srcmsg);
 SMBEXPORT int		smb_tzutc(int16_t timezone);
+SMBEXPORT time_t	smb_time(when_t);
+SMBEXPORT when_t	smb_when(time_t, int16_t zone);
 SMBEXPORT int		smb_updatethread(smb_t*, smbmsg_t* remsg, uint newmsgnum);
 SMBEXPORT int		smb_updatemsg(smb_t*, smbmsg_t*);
 SMBEXPORT bool		smb_valid_hdr_offset(smb_t*, uint offset);
-- 
GitLab