package utils import ( "errors" "strings" "time" ) // ParseDateString mengubah string "YYYY-MM-DD" menjadi time.Time func ParseDateString(dateStr string) (time.Time, error) { if dateStr == "" { return time.Time{}, errors.New("date string is empty") } parsed, err := time.Parse("2006-01-02", dateStr) if err != nil { return time.Time{}, errors.New("invalid date format, expected YYYY-MM-DD") } return parsed, nil } // FormatDate mengubah time.Time menjadi string "YYYY-MM-DD" func FormatDate(t time.Time) string { return t.Format("2006-01-02") } // ParseDateRangeForQuery parses optional YYYY-MM-DD from/to strings for list filters. // It returns a start pointer (inclusive) and an end pointer advanced by one day // so callers can safely use "< end" to achieve an inclusive upper bound. func ParseDateRangeForQuery(fromStr, toStr string) (*time.Time, *time.Time, error) { var fromPtr *time.Time var toPtr *time.Time if strings.TrimSpace(fromStr) != "" { parsed, err := ParseDateString(strings.TrimSpace(fromStr)) if err != nil { return nil, nil, errors.New("created_from must use format YYYY-MM-DD") } fromValue := parsed fromPtr = &fromValue } if strings.TrimSpace(toStr) != "" { parsed, err := ParseDateString(strings.TrimSpace(toStr)) if err != nil { return nil, nil, errors.New("created_to must use format YYYY-MM-DD") } nextDay := parsed.AddDate(0, 0, 1) toPtr = &nextDay } if fromPtr != nil && toPtr != nil && fromPtr.After(*toPtr) { return nil, nil, errors.New("created_from must be earlier than created_to") } return fromPtr, toPtr, nil }