How to extract year/hour from datetime fields in GROQ queries?

5 replies
Last updated: Oct 10, 2023
Hi all, I'm wondering if there is a way apply function to destructure data from datetime like hour, year etc. directly in GROQ something like this:

*[_type == "open_course" && is_active == true]{ 
in_person{
  'year' : new Date(start_date).getFullYear()
   }
  }
 }
Any gists or ideas as
datetime()
doesn't explain much. It is more about using custom function directly in GROQ to return values I need . Or there is no way to apply such functionality and I have to process data when I receive these from Sanity?
AI Update

Unfortunately, GROQ doesn't support JavaScript-style methods like getFullYear(), getMonth(), or getHours() directly on datetime values. The dateTime() function is mainly for converting strings to datetime objects for comparisons and ordering, not for extracting date components.

However, you have a couple of options:

Option 1: String manipulation with string::split()

Since datetime fields are stored in ISO 8601 format (like 2024-03-15T14:30:00Z), you can use GROQ's string functions to extract parts:

*[_type == "open_course" && is_active == true]{
  in_person{
    'year': string::split(start_date, '-')[0],
    'month': string::split(start_date, '-')[1],
    'day': string::split(string::split(start_date, 'T')[0], '-')[2],
    'hour': string::split(string::split(start_date, 'T')[1], ':')[0]
  }
}

This works by splitting the ISO datetime string on delimiters like -, T, and : to extract the components. You can also use substr() if you prefer working with string positions.

The more practical approach is to handle this in your application code after fetching from Sanity. This gives you full access to JavaScript's Date methods and is generally cleaner:

const data = await client.fetch(`
  *[_type == "open_course" && is_active == true]{
    in_person{
      start_date
    }
  }
`);

const processed = data.map(course => ({
  ...course,
  in_person: {
    ...course.in_person,
    year: new Date(course.in_person.start_date).getFullYear(),
    month: new Date(course.in_person.start_date).getMonth() + 1,
    day: new Date(course.in_person.start_date).getDate(),
    hour: new Date(course.in_person.start_date).getHours()
  }
}));

The string manipulation approach works in a pinch, but processing in your application code is typically more maintainable and gives you better control over formatting and timezone handling. GROQ is designed to be a query language rather than a full data transformation layer, so it's perfectly normal to do this kind of processing client-side.

Show original thread
5 replies
perhaps it's doable with some clever chaining of
string::split()
?
"year": string::split(start_date, '-')[0]
groq isn't javascript 😄
Yeh, I have found it too, I'm GROQ newbie and its syntax is not intuitive and looks scary to me :), sorry. String split is not I would like to do but thank you. In meantime I have sorted with utility functions and it works for me.
/**
 * @param {string | number | Date} date
 */
function formatTime(date) {
	var d = new Date(date),
		hours = '' + d.getHours(),
		minutes = '' + d.getMinutes();

	if (hours.length < 2) hours = '0' + hours;
	if (minutes.length < 2) minutes = '0' + minutes;

	return [hours, minutes].join(':');
}

/**
 * @param {string | number | Date} date
 */
function formatDateMonthName(date) {
	var d = new Date(date),
		month = '' + (d.getMonth() + 1),
		day = '' + d.getUTCDate(),
		year = d.getUTCFullYear();
	if (month.length < 2) month = '0' + month;
	if (day.length < 2) day = '0' + day;
	// @ts-ignore
	const monthStr = months[parseInt(month)];
	return `${monthStr} ${day}, ${year}`;
}
Will dig into GROQ a bit deeper when I will have time. Any suggestions of some great sources with beside official docs?

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?