Several weeks ago [I settled on using the FIPA SL modification of ISO8601](https://screwlisp.small-web.org/kitten/actual-kitten-experience/#but-first-a-common-lisp-FIPA-SL-modified-ISO8601-date-timestamper) for compatibility with [my use of FIPA SL](https://screwlisp.small-web.org/software-individuals/fipa).

But now I need an [RFC822 section 5](https://www.rfc-editor.org/rfc/rfc822.html#section-5) date for RSS!

First turning an ISO8601 date string into a universal time

```
(defun fipa-ISO8601-to-UT (string)
  (let ((year (subseq string 0 4)) (month (subseq string 4 6)) (date (subseq string 6 8))
	(hour (subseq string 9 11)) (minute (subseq string 11 13)) (second (subseq string 13 15))
	(time-zone (case (char string (1- (length string)))
		     (#\\Z "0"))))
    (apply 'encode-universal-time
	   (mapcar 'parse-integer
		   (list second minute hour date month year time-zone)))))
```

Turning a universal time into an RFC882 section 5 date. Only Z supported, and a 4 digit year is used.

```
(defun UT-to-RFC882 (UT &aux (TZ 0))
  (multiple-value-bind
	(second minute hour date month year day)
      (decode-universal-time UT TZ)
    (format nil "~@{~?~^ ~}"
	    "~[Mon~;Tue~;Wed~;Thu~;Fri~;Sat~;Sun~]," `(,day)
	    "~2,'0d" `(,date)
	    "~[Jan~;Feb~;Mar~;Apr~;May~;Jun~;Jul~;Aug~;Sep~;Oct~;Nov~;Dec~]" `(,(1- month))
	    "~d" `(,year)
	    "~@{~2,'0d~^:~}" `(,hour ,minute ,second)
	    "~[Z~]" `(,TZ))))
```

<resuming the next day> My friend and gamedev of the ages https://mdhughes.tech provided his ISO-8601 from universal-time function on the Mastodon, which I added a 0 to for timezone as I will explain in a moment:

```
(defun iso8601-datetime (tm)
  (multiple-value-bind (second minute hour date month year day daylight-p zone)
    (decode-universal-time (or tm (get-universal-time)) 0)
    (format NIL "~4,'0D-~2,'0D-~2,'0D ~2,'0D:~2,'0D:~2,'0D+~2,'0D00" year month date hour minute second zone)
))
```

which has pretty formatting that my datetimes do not, because I am actually using the [FIPA SL modification](/software-individuals/fipa) to ISO-8601. So you can see:

```
CL-USER> "20260116T041740716896Z"
"20260116T041740716896Z"
CL-USER> (iso8601-to-ut *)
3977525860
CL-USER> (iso8601-datetime *)
"2026-01-16 04:17:40+0000"
CL-USER> (ut-to-rfc882 **)
"Fri, 16 Feb 2026 04:17:40 Z"
CL-USER> 
```

but it is clear how they are all related. I set mdhughes' timezone to 0 since the FIPA SL spec only allows *Z* (zulu = UTC).

> The garbage at the end of my FIPA SL time is that it adds milliseconds at the end. Oh, oops, I now see I have given microseconds instead of milliseconds. Well, I will fix that going forward.

My fipa-sl from universal time:

```
(defun fipa-8601-from-ut+0 (ut)
  (let ((decoded (multiple-value-list
		  (decode-universal-time ut 0))))
    (format nil "~@{~?~}"
	    "~10,4,'0r" (list (sixth decoded))
	    "~10,2,'0r" (list (fifth decoded))
	    "~10,2,'0r" (list (fourth decoded))
	    "~a" (list #\\T)
	    "~10,2,'0r" (list (third decoded))
	    "~10,2,'0r" (list (second decoded))
	    "~10,2,'0r" (list (first decoded))
	    "~10,3,'0r" 
	    (list (rem (truncate (get-internal-real-time) 1000)
		       (truncate internal-time-units-per-second 1000)))
	    "~a" (list #\\Z))))
```

alright, there we go.

```
CL-USER> "20260116T041740716896Z"
"20260116T041740716896Z"
CL-USER> (iso8601-to-ut *)
3977525860
CL-USER> (fipa-8601-from-ut+0 *)
"20260116T041740523Z"
CL-USER> (ut-to-rfc882 **)
"Fri, 16 Feb 2026 4:17:40 Z"
```

Now with milliseconds properly.

The connection to RSS was that RSS requires a form of the rfc882 one for `<pubDate>`.