Repository: gohugoio/hugo (language: go)

Issue:
PublishDate does not roll-up to section, taxonomy, or term pages
### What version of Hugo are you using (`hugo version`)?

<pre>
hugo v0.125.4+extended darwin/arm64 BuildDate=2024-04-25T13:27:26Z VendorInfo=brew
</pre>

### Does this issue reproduce with the latest release?

Yes.

### Description

After upgrading to v0.125 I see changes like the following for `categories/index.xml` and `tags/index.xml` files:

```
-      <pubDate>Sat, 27 Oct 2018 08:30:00 +0000</pubDate>
+      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
```

For subindex files like `categories/foo/index.xml` the `pubDate` is set to the expected value, i.e. the generated output is identical to versions < v0.125.

From looking at the [changelog](https://github.com/gohugoio/hugo/releases/tag/v0.125.0) and [commit f0a26cf58e9bfbe55c091d51a4133bc22cad5d1f](https://github.com/gohugoio/hugo/commit/f0a26cf58e9bfbe55c091d51a4133bc22cad5d1f) it's not clear to me what needs to be changed in order to retain the previous (default) selection of values for `pubDate`? Am I missing something?

Also, with above hugo version `{{ hugo.Version }}` doesn't seem to be set, resulting in the following output for the `generator` tag:

```
-    <generator>Hugo -- gohugo.io</generator>
+    <generator>Hugo</generator>
```


Discussion / hints:
@schu

The version string was added to the generator tag in v0.125.0 to address #11692, and was removed in v0.125.3 to address https://github.com/gohugoio/hugo/pull/12300#issuecomment-2068869567. We should have closed #11692 as "won't fix".

I'll look into the zero pubdate  problem.
The `Date` and `Lastmod` roll-up to section, taxonomy, and term pages. The `PublishDate` does not "roll-up".

I think the `PublishDate` should roll-up to the latest date before now(). If we rolled-up dates after now(), that would cause the section, taxonomy, or term page to not be published even if there's other descendant content.

Applicable to:
- Automatic section, taxonomy, and term pages
- Section, taxonomy, and term pages without `Date` and `PublishDate`

This isn't something new (i.e., same behavior in v0.122.0).

The test case below does not test for what should happen with publish dates greater than now() as described above.
<details>
<summary>Failing test</summary>

```go
func TestFoo(t *testing.T) {
	t.Parallel()

	files := `
-- hugo.toml --
disableKinds = ['home','rss','sitemap']
[taxonomies]
tag = 'tags'
-- layouts/_default/list.html --
Date: {{ .Date.Format "2006-01-02" }}
PublishDate: {{ .PublishDate.Format "2006-01-02" }}
Lastmod: {{ .Lastmod.Format "2006-01-02" }}
-- layouts/_default/single.html --
{{ .Title }}
-- content/s1/p1.md --
---
title: p1
date: 2024-03-01
lastmod: 2024-03-02
tags: [t1]
---
-- content/s1/p2.md --
---
title: p2
date: 2024-04-03
lastmod: 2024-04-04
tags: [t1]
---
`

	// Test without publishDate in front matter.
	b := hugolib.Test(t, files)

	b.AssertFileContent("public/s1/index.html", `
		Date: 2024-04-03
		PublishDate: 2024-04-03
		Lastmod: 2024-04-04
	`)

	b.AssertFileContent("public/tags/index.html", `
		Date: 2024-04-03
		PublishDate: 2024-04-03
		Lastmod: 2024-04-04
	`)

	b.AssertFileContent("public/tags/t1/index.html", `
		Date: 2024-04-03
		PublishDate: 2024-04-03
		Lastmod: 2024-04-04
	`)

	// Test with publishDate in front matter.
	files = strings.ReplaceAll(files, "lastmod", "publishDate")

	b = hugolib.Test(t, files)

	b.AssertFileContent("public/s1/index.html", `
		Date: 2024-04-03
		PublishDate: 2024-04-03
		Lastmod: 2024-04-03
	`)

	b.AssertFileContent("public/tags/index.html", `
		Date: 2024-04-03
		PublishDate: 2024-04-03
		Lastmod: 2024-04-03
	`)

	b.AssertFileContent("public/tags/t1/index.html", `
		Date: 2024-04-03
		PublishDate: 2024-04-03
		Lastmod: 2024-04-03
	`)
}
```
</details>
<br>

>I think the PublishDate should roll-up to the latest date before now(). 

Yea, that sounds reasonable.
Is there a workaround that I could apply to get back the old behavior?

Or is there a chance to revert to the old behavior until a working fix is found?
Override the embedded RSS template, and change this line:

https://github.com/gohugoio/hugo/blob/3d40aba512931031921463dafc172c0d124437b8/tpl/tplimpl/embedded/templates/_default/rss.xml#L64

To:

```text
<pubDate>{{ (or .PublishDate .Date).Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate> 
```

If the page has a publication date, use that, else use the date.
> Override the embedded RSS template [...]

Thanks, that worked.

For other readers: download the [default rss.xml template](https://github.com/gohugoio/hugo/blob/3d40aba512931031921463dafc172c0d124437b8/tpl/tplimpl/embedded/templates/_default/rss.xml#L64) to `layouts/_default/rss.xml` and change the `pubDate` line as shown above.

Fix patch:
diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go
index 0a9063e2303..e3b29f186b3 100644
--- a/hugolib/content_map_page.go
+++ b/hugolib/content_map_page.go
@@ -1428,7 +1428,7 @@ func (sa *sitePagesAssembler) applyAggregates() error {
 					}
 
 					if wasZeroDates {
-						pageBundle.m.pageConfig.Dates.UpdateDateAndLastmodIfAfter(sp.m.pageConfig.Dates)
+						pageBundle.m.pageConfig.Dates.UpdateDateAndLastmodAndPublishDateIfAfter(sp.m.pageConfig.Dates)
 					}
 
 					if pageBundle.IsHome() {
@@ -1565,7 +1565,7 @@ func (sa *sitePagesAssembler) applyAggregatesToTaxonomiesAndTerms() error {
 							return
 						}
 
-						p.m.pageConfig.Dates.UpdateDateAndLastmodIfAfter(sp.m.pageConfig.Dates)
+						p.m.pageConfig.Dates.UpdateDateAndLastmodAndPublishDateIfAfter(sp.m.pageConfig.Dates)
 					})
 				}
 
diff --git a/resources/page/pagemeta/page_frontmatter.go b/resources/page/pagemeta/page_frontmatter.go
index c901ab57b5f..0e0650934b3 100644
--- a/resources/page/pagemeta/page_frontmatter.go
+++ b/resources/page/pagemeta/page_frontmatter.go
@@ -57,13 +57,17 @@ func (d Dates) IsDateOrLastModAfter(in Dates) bool {
 	return d.Date.After(in.Date) || d.Lastmod.After(in.Lastmod)
 }
 
-func (d *Dates) UpdateDateAndLastmodIfAfter(in Dates) {
+func (d *Dates) UpdateDateAndLastmodAndPublishDateIfAfter(in Dates) {
 	if in.Date.After(d.Date) {
 		d.Date = in.Date
 	}
 	if in.Lastmod.After(d.Lastmod) {
 		d.Lastmod = in.Lastmod
 	}
+
+	if in.PublishDate.After(d.PublishDate) && in.PublishDate.Before(htime.Now()) {
+		d.PublishDate = in.PublishDate
+	}
 }
 
 func (d Dates) IsAllDatesZero() bool {


Test patch:
diff --git a/hugolib/dates_test.go b/hugolib/dates_test.go
index 784fc101c61..1a88cfd7da8 100644
--- a/hugolib/dates_test.go
+++ b/hugolib/dates_test.go
@@ -266,3 +266,86 @@ Home's Date should be equal mydata date: true
 Full time: 6:00:00 am UTC
 `)
 }
+
+func TestPublisDateRollupIssue12438(t *testing.T) {
+	t.Parallel()
+
+	// To future Hugo maintainers, this test will start to fail in 2099.
+	files := `
+-- hugo.toml --
+disableKinds = ['home','rss','sitemap']
+[taxonomies]
+tag = 'tags'
+-- layouts/_default/list.html --
+Date: {{ .Date.Format "2006-01-02" }}
+PublishDate: {{ .PublishDate.Format "2006-01-02" }}
+Lastmod: {{ .Lastmod.Format "2006-01-02" }}
+-- layouts/_default/single.html --
+{{ .Title }}
+-- content/s1/p1.md --
+---
+title: p1
+date: 2024-03-01
+lastmod: 2024-03-02
+tags: [t1]
+---
+-- content/s1/p2.md --
+---
+title: p2
+date: 2024-04-03
+lastmod: 2024-04-04
+tags: [t1]
+---
+-- content/s1/p3.md --
+---
+title: p3
+lastmod: 2099-05-06
+tags: [t1]
+---
+
+`
+
+	// Test without publishDate in front matter.
+	b := Test(t, files)
+
+	b.AssertFileContent("public/s1/index.html", `
+		Date: 2099-05-06
+		PublishDate: 2024-04-03
+		Lastmod: 2099-05-06
+	`)
+
+	b.AssertFileContent("public/tags/index.html", `
+		Date: 2024-04-03
+		PublishDate: 2024-04-03
+		Lastmod: 2024-04-04
+	`)
+
+	b.AssertFileContent("public/tags/t1/index.html", `
+		Date: 2024-04-03
+		PublishDate: 2024-04-03
+		Lastmod: 2024-04-04
+	`)
+
+	// Test with publishDate in front matter.
+	files = strings.ReplaceAll(files, "lastmod", "publishDate")
+
+	b = Test(t, files)
+
+	b.AssertFileContent("public/s1/index.html", `
+		Date: 2099-05-06
+		PublishDate: 2024-04-04
+		Lastmod: 2099-05-06
+	`)
+
+	b.AssertFileContent("public/tags/index.html", `
+		Date: 2024-04-03
+		PublishDate: 2024-04-04
+		Lastmod: 2024-04-03
+	`)
+
+	b.AssertFileContent("public/tags/t1/index.html", `
+		Date: 2024-04-03
+		PublishDate: 2024-04-04
+		Lastmod: 2024-04-03
+	`)
+}