go 55 lines · 8 steps

Custom time validation in Gin

Bind a date range from query params and enforce cross-field and business rules with a custom validator.

Explained by highlit
1package handler
2 
3import (
4 "net/http"
5 "reflect"
6 "time"
7 
8 "github.com/gin-gonic/gin"
9 "github.com/gin-gonic/gin/binding"
10 "github.com/go-playground/validator/v10"
11)
12 
13type ReportQuery struct {
14 From time.Time `form:"from" time_format:"2006-01-02" binding:"required,notfuture"`
15 To time.Time `form:"to" time_format:"2006-01-02" binding:"required,gtfield=From"`
16}
17 
18func notFuture(fl validator.FieldLevel) bool {
19 t, ok := fl.Field().Interface().(time.Time)
20 if !ok {
21 return false
22 }
23 return !t.After(time.Now())
24}
25 
26func RegisterTimeValidators() {
27 v, ok := binding.Validator.Engine().(*validator.Validate)
28 if !ok {
29 return
30 }
31 
32 v.RegisterCustomTypeFunc(func(field reflect.Value) interface{} {
33 if field.Kind() == reflect.String {
34 if ts, err := time.Parse(time.RFC3339, field.String()); err == nil {
35 return ts
36 }
37 }
38 return nil
39 }, time.Time{})
40 
41 _ = v.RegisterValidation("notfuture", notFuture)
42}
43 
44func GetReport(c *gin.Context) {
45 var q ReportQuery
46 if err := c.ShouldBindQuery(&q); err != nil {
47 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
48 return
49 }
50 
51 c.JSON(http.StatusOK, gin.H{
52 "from": q.From.Format(time.RFC3339),
53 "to": q.To.Format(time.RFC3339),
54 })
55}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Struct tags let Gin declare parsing and validation rules right beside each field.
  2. 2Registering a custom type func teaches the validator how to interpret unfamiliar types before rules run.
  3. 3Cross-field constraints like gtfield keep related inputs consistent without manual comparison code.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Custom time validation in Gin — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code