Repository: gohugoio/hugo (language: go)

Issue:
Improve date validation error message and handle blank dates as zero dates
TL;DR #10717 makes aliases for _date_ front matter variables fail if they aren't correctly formatted, but it's not obvious and some aliases may already have other meanings, and the error message doesn't make it clear what's wrong. (My `published` was a boolean being parsed as date.)

Front matter validation failed for me when running most commands, e.g. 
```bash
❯ hugo list published -s dtb/
ERROR invalid front matter: published: %!!(MISSING)s(bool=false): see /home/sh/.../2016-05-09-quotes.md
# (many identical lines omitted)
```

Its front matter contained `published: false` and from my perspective the error message could be interpreted in many ways but none clearly helpful to me.

This was relatively hard to debug — why does the `published` key, that I used for a template previously, a boolean, fail? 

The `published` key isn't listed as one of the predefined front matter variables (https://gohugo.io/content-management/front-matter/#fields) and it was a correctly formatted boolean.

Tracked it to #10717 and then to https://github.com/gohugoio/hugo/blob/master/resources/page/pagemeta/page_frontmatter.go#L352 which told me that "published" is actually a special variable because it's an alias to `publishDate`, which is expected to be a date, and it fails when it isn't.

Also `published` is a boolean variable in Jekyll, and many might still have it set (though no idea how many except me)

I don't have really a proposed solution and it may be an one-off thing, but what might help to make it more transparent:
- documenting the aliases for frontmatter variables (maybe they are, couldn't find them quickly) 
- stating in the error messages that it's supposed to be a date, not a boolean


### What version of Hugo are you using (`hugo version`)?

<pre>
$ hugo version
hugo v0.135.0+extended linux/amd64 BuildDate=unknown
</pre>

### Does this issue reproduce with the latest release?
Yes


Discussion / hints:
We only validate the pre-defined 4 dates in Hugo, which have some configurable fall backs:

https://gohugo.io/getting-started/configuration/#configure-dates

>The published key isn't listed as one of the predefined front matter variables (https://www.engino.co.uk/form-elements/front-matter/#predefined) and it was a correctly formatted boolean.

That's not Hugo's documentation.

If you want to be sure about avoiding front matter collisions, but user defined params below `params` e.g.:

```
title: My pos
params:
   published: false
```



Sorry, wrong link — I meant https://gohugo.io/content-management/front-matter/#fields
Editing in my post as well. 
Thank you for the `params` hint, it may be a solution to another unrelated problem I have

(My main concern is that it was relatively confusing to figure out once it happened, and the edited issue title much better reflects that — thank you!)
Is there a way to tell Hugo to ignore some files/paths for the date validation? I use template files in Obsidian to pre-populate the front matter in my posts (alternative to `hugo new post/...`), and since updating to v135 today it hard errors on them:

```
hugo v0.135.0+extended linux/amd64 BuildDate=unknown

ERROR invalid front matter: created: {{date}}T{{time}}: see /home/neitsab/site/content/templates/empty-new.md
```

Edit:

> We only validate the pre-defined 4 dates in Hugo, which have some configurable fall backs:

I have

```toml
[frontmatter]
  date = [":filename","created"]
```

in my config, so it seems custom date alias are also validated?
Dates created by other tools, such as DecapCMS, default to empty string when corresponding UI-fields are left empty. In my case, that causes `expiryDate: ""`  to fail the validation. Is there a way to also allow setting a date default that would pass the syntax check but mean "intentionally left blank"? 
@LHSnow yes, I'll added that to the TODO list in the issue title, thanks for the input.
I've documented the front matter date field aliases:
<https://github.com/gohugoio/hugoDocs/commit/e19fb804312cea3c81de0eeeb762b975512da1b7>

Fix patch:
diff --git a/resources/page/pagemeta/page_frontmatter.go b/resources/page/pagemeta/page_frontmatter.go
index 9491bf1bcab..686f0d44d71 100644
--- a/resources/page/pagemeta/page_frontmatter.go
+++ b/resources/page/pagemeta/page_frontmatter.go
@@ -728,7 +728,7 @@ func (f *frontmatterFieldHandlers) newDateFieldHandler(key string, setter func(d
 	return func(d *FrontMatterDescriptor) (bool, error) {
 		v, found := d.PageConfig.Params[key]
 
-		if !found {
+		if !found || v == "" {
 			return false, nil
 		}
 
@@ -739,7 +739,7 @@ func (f *frontmatterFieldHandlers) newDateFieldHandler(key string, setter func(d
 			var err error
 			date, err = htime.ToTimeInDefaultLocationE(v, d.Location)
 			if err != nil {
-				return false, fmt.Errorf("invalid front matter: %s: %s: see %s", key, v, d.PathOrTitle)
+				return false, fmt.Errorf("the %q front matter field is not a parsable date: see %s", key, d.PathOrTitle)
 			}
 			d.PageConfig.Params[key] = date
 		}


Test patch:
diff --git a/resources/page/pagemeta/pagemeta_integration_test.go b/resources/page/pagemeta/pagemeta_integration_test.go
index 4d195b7f06b..9d8f1b92f28 100644
--- a/resources/page/pagemeta/pagemeta_integration_test.go
+++ b/resources/page/pagemeta/pagemeta_integration_test.go
@@ -14,6 +14,7 @@
 package pagemeta_test
 
 import (
+	"strings"
 	"testing"
 
 	"github.com/gohugoio/hugo/hugolib"
@@ -43,3 +44,55 @@ Lastmod: 2024-03-13 06:00:00 &#43;0000 GMT
 Eq: true
 `)
 }
+
+func TestDateValidation(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+-- content/_index.md --
++++
+date = DATE
++++
+-- layouts/index.html --
+{{ .Date.UTC.Format "2006-01-02" }}
+--
+`
+	errorMsg := `ERROR the "date" front matter field is not a parsable date`
+
+	// Valid (TOML)
+	f := strings.ReplaceAll(files, "DATE", "2024-10-01")
+	b := hugolib.Test(t, f)
+	b.AssertFileContent("public/index.html", "2024-10-01")
+
+	// Valid (string)
+	f = strings.ReplaceAll(files, "DATE", `"2024-10-01"`)
+	b = hugolib.Test(t, f)
+	b.AssertFileContent("public/index.html", "2024-10-01")
+
+	// Valid (empty string)
+	f = strings.ReplaceAll(files, "DATE", `""`)
+	b = hugolib.Test(t, f)
+	b.AssertFileContent("public/index.html", "0001-01-01")
+
+	// Valid (int)
+	f = strings.ReplaceAll(files, "DATE", "0")
+	b = hugolib.Test(t, f)
+	b.AssertFileContent("public/index.html", "1970-01-01")
+
+	// Invalid (string)
+	f = strings.ReplaceAll(files, "DATE", `"2024-42-42"`)
+	b, _ = hugolib.TestE(t, f)
+	b.AssertLogContains(errorMsg)
+
+	// Invalid (bool)
+	f = strings.ReplaceAll(files, "DATE", "true")
+	b, _ = hugolib.TestE(t, f)
+	b.AssertLogContains(errorMsg)
+
+	// Invalid (float)
+	f = strings.ReplaceAll(files, "DATE", "6.7")
+	b, _ = hugolib.TestE(t, f)
+	b.AssertLogContains(errorMsg)
+}