Playing with Time

Jul 14, 2025

When generating my site's RSS feed and post index, I prefer to sort posts by their <pubDate> sub-element so newer posts always appear on top. While it's an entirely optional field, as specified in the RSS 2.0 spec, most other feed generators I've used sort items by the <pubDate> sub-element, so I would feel a bit left out if my bespoke build script couldn't sort posts too.

For some context, my build script creates a struct called Rss to represent the RSS feed, with a Posts field containing a slice of Post structs, which (as the name implies) represent individual posts:


type Rss struct {
	LastBuildDate string
	Posts         *[]Post
	XmlHeader     html.HTML
}

type Post struct {
	name    string
	Title   string
	Author  string
	Link    string
	Body    string
	RawDate string
	PubDate string
}

Prior to generating the feed, the Posts field is sorted based on each post's PubDate field. The script then generates the XML file by iterating over the Posts field, creating an entry for each post using some templating magic.

The publish date values in the PubDate field look like this:

Sat, 05 Jul 2025 23:46:02 -0400

Sorting those dates can be frustrating. My original attempt naively used cmp.Compare to compare the two PubDate fields:


slices.SortFunc(posts, func(a, b Post) int {
	return cmp.Compare(b.PubDate, a.PubDate)
}

One can imagine this didn't work out well, since PubDate (a string) doesn't fit under the Ordered constraint that permits any ordered type.

To my relief, Go includes a time package in its standard library that makes measuring time a heck of lot easier.

Digging through the docs, I found the method Time.Compare, which is used to compare two time.Time structs. Unlike the methods Time.Before and Time.After, Time.Compare returns an int, which satisfies the cmp function's prototype in slices.SortFunc.


slices.SortFunc(posts, func(a, b Post) int {
	aTime, _ := time.Parse(time.RFC1123Z, a.PubDate)
	bTime, _ := time.Parse(time.RFC1123Z, b.PubDate)
	return bTime.Compare(aTime)
})

The PubDate fields for each struct are already formatted as RFC 1123Z compliant strings, so parsing them into time.Time structs before the comparison is a breeze and only adds a couple lines of code.