dateFormat with date and time from frontmatter

I am having trouble with dates/times and formatting. I would like to have a startDateTime and and endDateTime in my front matter for an event and print it out formatted in the template. There seems to be a ton of dateFormat related discussions but I cant seem to get this to work.

It seems this explains the format string a bit, though it still seems it might be tricky to get it just right:

but it seems I continue to either get a parse error or most often “error calling dateFormat: Unable to Cast nil to Time”

It would be nice to print out something like:

Thursday March 2 2017 6:00PM - 8:00PM

or potentially spanning several days:

Thursday March 2 2017 - Friday March 3 2017 6:00PM - 8:00PM

Is there a specific format the frontmatter needs to be in? Something else?

My hope is to have netlify-cms write the date and time with its datetime widget. It writes the frontmatter out in yaml.

Thanks for your time.

Yes, dates should be in ISO. If the actual date or publishdate or lastmod, you can just use .Format. Can you give me an example of a) your front matter and b) what you want the output to be?

Since the front matter is so far coming from netlify-cms, which I am just beginning to test out I had only placed this for .Date: 2017-01-22, though that is not the filed I am trying to show. Also I’m not far into Hugo seriously yet so I’m not sure I fully know the difference in the role of .Date vs .PublishDate and is .Lastmod something in the front matter or is it based on the file., but that is separate from my question.

Here is an example of the default value I get from the datetime widget in netlify-cms:

startDate: Fri Mar 03 2017 10:00:00 GMT-0500 (EST)
endDate: Fri Mar 03 2017 12:00:00 GMT-0500 (EST)
cost: Free

Perhaps there is a way to tell it to format like: 2017-03-03T10:00:00Z if the above can’t work.

I’d like the output in the template to be:

Friday, March 4 2017
10:00AM - 12:00PM

Here is a netlify-cms screenshot:

It’s true dateFormat is hard to understand from a logical standpoint because the constants GO Templates uses are from an actual date Mon Jan 2 15:04:05 MST 2006 and 01/02 03:04:05PM '06 -0700 (Documentation).

The date input can be written in the following formats:

like: “2015-01-21T20:54:45.847Z”

like: “2015-01-21”

like: “21 Jan 06 15:04 MST”

like: “21 Jan 06 15:04 -0700”

like: “Mon, 21 Jan 2006 15:04:05 MST”

like: “Mon, 21 Jan 2006 15:04:05 -0700”

I have recently published an article related dateFormat in Hugo with a Conclusion Table to understand how to get specific parts out of the date.

In case of a Unable to Cast nil to Time looks to me like there is no valid input to parse the date like:

{{ dateFormat "Monday, Jan 2, 2006" nil }} # error

to catch these thing I would recommend doing a check first before passing the date into the dateFormat method like:

$date := "2015-01-21";

{{ if ne $date nil }}
  {{ dateFormat "Monday, Jan 2, 2006" $date }}
{{ end }}

Hope this helps.

1 Like

Also @shawn @rwi, keep in mind that the dateFormat function takes a string and treats it like a date, but you don’t need to use it with Date, .PublishDate, and .LastMod since these variables provide a sort of shortcut in your templates; i.e.,

{{.PublishDate.Format "January 2, 2006"}}

That said, this is going to take place in your templating and not necessarily in the Netlify CMS layer, which is a React SPA that just communicates with the GitHub API (I believe; ultimately, you’ll have to reach out to Netlify to figure out the internals). If you’re using a theme and can point me to a repo, I can help show you where you need to make the change at the templating layer.


As for understanding the difference between date, publishdate, lastmod, and expirydate in your front matter, think of it like this:

: This is a required field for every content file. If you use Hugo’s archetypes (see the docs), this will be created auto-magically with every new instance of a content type you create with hugo new

: This has special significance since Hugo’s default behavior is to not publish content files with publishdate in the future, although if you are running Hugo locally, you can pass the buildFuture flag to the CLI.

: This is the other bookend to publishdate. By default, Hugo won’t build expired content files into pages.

: This is a way for you to easily track–and expose to your audience/readers—when the last modification took place for an individual piece of content while preserving the original creation date (date) and publish date (publishdate) intact with the piece of content for future-proofing and exportability.

**Also note:

draft has significance in that Hugo’s default behavior is to not build draft content, regardless of the aforementioned fields, although this can be overridden in local develop by passing the --buildDrafts flag to the CLI.

HTH. Cheers.

@rwi, I really like your site :smile:

A couple quick things that might help you:

  1. The post is more illuminating, but I think your examples in this thread might be confusing because you’re putting the input time in some and parts of the layout string in others…e.g., using 2015 and 2006.
  2. In your blog post, you write…

However, why Monday? why Jan? Why a 2 and not a 1 for the date? Why no ISO8601 as in the front matters.

This will explain a lot of the examples you give in your post. It’s because Go templates’ layout strings for dates point to a single reference time:

Mon Jan 2 15:04:05 MST 2006

Hence why you need to use “Mon” and not “Tue,” “Wed,” “Thurs,” etc…

This may seem arbitrary, but it makes sense since MST is actually 07, thereby making the reference string a sequence.

Here is the nice little visual explanation taken directly from the Go docs:

 Jan 2 15:04:05 2006 MST
=> 1 2  3  4  5    6  -7


@rwi @rdwatters You guys are awesome! And that site is great. Thanks for that dateFormat page! Here is what I have for the formatting:

{{ dateFormat “Monday, Jan 2 2006” “2015-01-21T20:54:45.847Z” }}
{{ dateFormat “3:04PM” “2015-01-21T20:54:45.847Z” }}

That gives me what I want, but it seems something is happening as it comes from the frontmatter that I am unaware of. If I have that same date string of 2015-01-21T20:54:45.847Z in the frontmatter and then try to use it instead of hardcoding it in the template:

{{ dateFormat “Monday, Jan 2 2006” .Params.startDate }}

I am back to the error of: error calling dateFormat: Unable to Cast nil to Time

{{ .Params.startDate }} shows the expected string from the frontmatter. Perhaps this is not coming as a string from the frontmatter parsing?

Once I have this portion then I can move on to digging into netlify-cms to see if I can have it format it properly to the frontmatter. Thanks again for your help in clarifying this!!

@shawn I just tried to reproduce your issue.

For me this works fine:

{{ if ne .Params.startDate nil }}
  {{ dateFormat "Monday, Jan 2 2006" .Params.startDate }}
{{ end }}

But I got the error: at <dateFormat "Monday, ...>: error calling dateFormat: Unable to Cast <nil> to Time also when I was not checking for the startDate existence.

I think the error in the console comes from a page without a provided startDate in the front-matters.

1 Like

The startDate is not nil from the frontmatter, yet it it errors out as if it was. If I print it out as is, it prints fine, but if I pass it to dateFormat the error occurs.

@rwi @rdwatters Also if I make date in the front matter be the same value as startDate and then pass .Date to dateFormat it works fine. It seems there is something different about using a date with frontmatter that I define the name of.

{{ range $k, $v := .Params }}

{{ $k }}: {{ $v }}

{{ end }}

Shows everything I expect as well. So it seems the startDate value is there in the .Params. I see that its mentioned in the docs and $k shows that everything is lowercased, but it seems the startDate is there either way. ( startDate or startdate ) I must be missing something. Thanks again for your time.

Yes, Hugo lowercases any custom parameters you add to the front matter. Still you can access them case insensitive.

It is really just an assumption from my side but I think startDate is not set in one of your contents that should be rendered in the layout. Like:

title = "content I"
startDate =  "2015-01-21T20:54:45.847Z"
title = "content II"
startDate =  "2015-01-21T20:20:45.847Z"
title = "content III"

These are mistakes I often stumble on only looking at the content I for example and miss out the error in the console appears from another content.

Are you sure all of your content-files that will be rendered through the appropriate layout have a proper startDate defined?

I was unable to reproduce on Hugo v0.19 and the tests worked out well.

@rwi That just jogged me, thanks. I actually only have 3 content files. My brain is in serverside land and not thinking about the change causing every page to re-render. I appreciate your help and I’m glad to see it was just me being stupid.

So I can expect the value to be nil any time an item is not in the front matter? It seems just printing a value and having it not exist is no worry so I shouldn’t need to litter most of the templates with {{ if ne .Params.whatever nil }}

A side question about the lowercasing, do you anticipate it always allowing the camelcasing in the templates or should I go back and change them all to lowercase?

@shawn - glad I could help. You cannot imagine how much time I spent on issues like this but I was always happy that it was just me doing something stupid :slightly_smiling:

This checkup is not necessary in most of the cases. The variable just get ingnored silently. In this case it was necessary because of the dateFormat-Method that throws an error otherwise.

For me this is still a mystery as well what should be called best practices. I am very much used to write my parameters in camelCase but when I use them in the templates I switch to accessing them lowercase. Often I switch now to snake_case for in the front matter.

Perhaps the following as a slightly more terse option?

{{ with .Params.startdate }}
    {{ dateFormat "Monday, Jan 2 2006" . }}
{{ end }}

I will try to stop dragging this post on, but I wondered if you could give me one more opinion. In formatting the template for the dates and times I have this:

{{ if ne .Params.startdate nil }}

<!-- Starting day -->
{{ dateFormat "Monday, Jan 2" .Params.startdate }}

{{ if ne .Params.enddate nil }}
  <!-- If its more than one day -->
  {{ if ne (dateFormat "Jan 2" .Params.startdate) (dateFormat "Jan 2" .Params.enddate) }}
   &ndash; {{ dateFormat "Monday, Jan 2" .Params.enddate }}
  {{ end }}
{{ end }}

<!-- If its not the current year -->
{{ if ne (dateFormat "2006" .Params.startdate) (dateFormat "2006" now) }}
  <!-- Show the year -->
  {{ dateFormat "2006" .Params.startdate }}
{{ end }}
<!-- The time -->
<span class="ttl">
  <!-- starts -->
  {{ dateFormat "3:04PM" .Params.startdate }}
  <!-- ends -->
  {{ if ne .Params.enddate nil }}
    &ndash; {{ dateFormat "3:04PM" .Params.enddate }}
  {{ end }}

{{ end }}

It ends up doing one of these:

Saturday, Jan 21 
8:54pm – 11:54pm

Saturday, Jan 21 – Sunday, Jan 22 
8:54pm – 11:54pm

Sunday, Jan 21 – Monday, Jan 22 2018 
8:54pm – 11:54pm

depending on a few things in the startdate and enddate. Would you say using dateFormat is the best approach for this formatting?

I think you could be more terse. Why break out the times separately? Aren’t these all being added as a single date with hours, etc?

Also, are the above examples pulled from your actual source? Does it make sense to have a date range followed by a range of hours? I mean, from the perspective of your end user and readability. Are you saying that an event starts on Saturday, Jan 21 at 8:54 PM and ends on Sunday, Jan 22 at 11:54 PM?

Those times were just random from some date string I had copied. I’m working from something thats preexisting for the formatting, it currently doesn’t show the year, but if the date happened to go off into the next year it should and it may or may not be multiple days. The formatting could change if I talk them into it. Is using dateFormat like this for the comparisons is the best option if its needed? Or can it be converted to a date object and have properties to work with?