@@ -168,3 +168,97 @@ export const renderJSONContent = (content: unknown) => {
168168 return typeof content === 'string' ? content : JSON . stringify ( content ) ;
169169 }
170170} ;
171+
172+ export function validateDateFormatString ( format : string ) {
173+ if ( ! format || typeof format !== 'string' ) {
174+ return { isValid : false , error : 'Format must be a non-empty string' } ;
175+ }
176+
177+ /*
178+ * Valid tokens and separators
179+ * const validSeparators = ['/', '-', '.', ' ', ':', ','];
180+ */
181+
182+ // Check for valid structure
183+ const formatRegex = / ^ ( Y Y Y Y | Y Y | M M | D D | M | D | [ \/ \- . \s : , ] ) + $ / ;
184+ if ( ! formatRegex . test ( format ) ) {
185+ return {
186+ isValid : false ,
187+ error : 'Format contains invalid characters. Use YYYY, YY, MM, DD, M, D and separators (/, -, ., space, :, ,)' ,
188+ } ;
189+ }
190+
191+ // Check if format contains at least one valid token
192+ const hasValidToken = / ( Y Y Y Y | Y Y | M M | D D | M | D ) / . test ( format ) ;
193+ if ( ! hasValidToken ) {
194+ return { isValid : false , error : 'Format must contain at least one valid date token (YYYY, YY, MM, DD, M, D)' } ;
195+ }
196+
197+ // Extract all tokens by replacing them one by one
198+ let tempFormat = format ;
199+ let yearCount = 0 ;
200+ let monthCount = 0 ;
201+ let dayCount = 0 ;
202+
203+ // Count YYYY tokens
204+ while ( tempFormat . includes ( 'YYYY' ) ) {
205+ yearCount ++ ;
206+ tempFormat = tempFormat . replace ( 'YYYY' , '' ) ;
207+ }
208+
209+ // Count YY tokens (only if YYYY wasn't found)
210+ while ( tempFormat . includes ( 'YY' ) ) {
211+ yearCount ++ ;
212+ tempFormat = tempFormat . replace ( 'YY' , '' ) ;
213+ }
214+
215+ // Reset for month/day counting
216+ tempFormat = format . replace ( / Y Y Y Y / g, '' ) . replace ( / Y Y / g, '' ) ;
217+
218+ // Count MM tokens
219+ while ( tempFormat . includes ( 'MM' ) ) {
220+ monthCount ++ ;
221+ tempFormat = tempFormat . replace ( 'MM' , '' ) ;
222+ }
223+
224+ // Count M tokens (single M, not part of MM)
225+ const singleMMatches = tempFormat . match ( / (?< ! M ) M (? ! M ) / g) ;
226+ monthCount += singleMMatches ?. length ?? 0 ;
227+
228+ // Reset for day counting
229+ tempFormat = format
230+ . replace ( / Y Y Y Y / g, '' )
231+ . replace ( / Y Y / g, '' )
232+ . replace ( / M M / g, '' )
233+ . replace ( / (?< ! M ) M (? ! M ) / g, '' ) ;
234+
235+ // Count DD tokens
236+ while ( tempFormat . includes ( 'DD' ) ) {
237+ dayCount ++ ;
238+ tempFormat = tempFormat . replace ( 'DD' , '' ) ;
239+ }
240+
241+ // Count D tokens (single D, not part of DD)
242+ const singleDMatches = tempFormat . match ( / (?< ! D ) D (? ! D ) / g) ;
243+ dayCount += singleDMatches ?. length ?? 0 ;
244+
245+ // Validate counts
246+ if ( yearCount > 1 ) {
247+ return { isValid : false , error : 'Format cannot have multiple year tokens' } ;
248+ }
249+
250+ if ( monthCount > 1 ) {
251+ return { isValid : false , error : 'Format cannot have multiple month tokens' } ;
252+ }
253+
254+ if ( dayCount > 1 ) {
255+ return { isValid : false , error : 'Format cannot have multiple day tokens' } ;
256+ }
257+
258+ // Check that tokens aren't directly adjacent without separator
259+ if ( / Y Y Y Y M M | Y Y Y Y D D | M M D D | D D M M | M M Y Y | D D Y Y | Y Y M M | Y Y D D / i. test ( format ) ) {
260+ return { isValid : false , error : 'Date tokens must be separated by valid separators' } ;
261+ }
262+
263+ return { isValid : true , error : null } ;
264+ }
0 commit comments