@@ -233,6 +233,29 @@ function build (schema, options) {
233233 ajvInstance = new Ajv ( { ...options . ajv , strictSchema : false , uriResolver : fastUri } )
234234 ajvFormats ( ajvInstance )
235235
236+ const validateDateTimeFormat = ajvFormats . get ( 'date-time' ) . validate
237+ const validateDateFormat = ajvFormats . get ( 'date' ) . validate
238+ const validateTimeFormat = ajvFormats . get ( 'time' ) . validate
239+
240+ ajvInstance . addKeyword ( {
241+ keyword : 'fjs_date_type' ,
242+ validate : ( schema , date ) => {
243+ if ( date instanceof Date ) {
244+ return true
245+ }
246+ if ( schema === 'date-time' ) {
247+ return validateDateTimeFormat ( date )
248+ }
249+ if ( schema === 'date' ) {
250+ return validateDateFormat ( date )
251+ }
252+ if ( schema === 'time' ) {
253+ return validateTimeFormat ( date )
254+ }
255+ return false
256+ }
257+ } )
258+
236259 isValidSchema ( schema )
237260 if ( options . schema ) {
238261 // eslint-disable-next-line
@@ -367,12 +390,6 @@ function inferTypeByKeyword (schema) {
367390 return schema . type
368391}
369392
370- const stringSerializerMap = {
371- 'date-time' : 'serializer.asDatetime.bind(serializer)' ,
372- date : 'serializer.asDate.bind(serializer)' ,
373- time : 'serializer.asTime.bind(serializer)'
374- }
375-
376393function getStringSerializer ( format , nullable ) {
377394 switch ( format ) {
378395 case 'date-time' : return nullable ? 'serializer.asDatetimeNullable.bind(serializer)' : 'serializer.asDatetime.bind(serializer)'
@@ -382,10 +399,6 @@ function getStringSerializer (format, nullable) {
382399 }
383400}
384401
385- function getTestSerializer ( format ) {
386- return stringSerializerMap [ format ]
387- }
388-
389402function addPatternProperties ( location ) {
390403 const schema = location . schema
391404 const pp = schema . patternProperties
@@ -1150,25 +1163,25 @@ function buildValue (laterCode, locationPath, input, location, isArray) {
11501163 const locations = dereferenceOfRefs ( location , schema . anyOf ? 'anyOf' : 'oneOf' )
11511164 locations . forEach ( ( location , index ) => {
11521165 const nestedResult = buildValue ( laterCode , locationPath + 'i' + index , input , location , isArray )
1153- // We need a test serializer as the String serializer will not work with
1154- // date/time ajv validations
1155- // see: https://github.com/fastify/fast-json-stringify/issues/325
1156- const testSerializer = getTestSerializer ( location . schema . format )
1157- const testValue = testSerializer !== undefined ? `${ testSerializer } (${ input } , true)` : `${ input } `
1158-
11591166 // Since we are only passing the relevant schema to ajv.validate, it needs to be full dereferenced
11601167 // otherwise any $ref pointing to an external schema would result in an error.
11611168 // Full dereference of the schema happens as side effect of two functions:
11621169 // 1. `dereferenceOfRefs` loops through the `schema.anyOf`` array and replaces any top level reference
11631170 // with the actual schema
1164- // 2. `nested `, through `buildCode`, replaces any reference in object properties with the actual schema
1171+ // 2. `buildValue `, through `buildCode`, replaces any reference in object properties with the actual schema
11651172 // (see https://github.com/fastify/fast-json-stringify/blob/6da3b3e8ac24b1ca5578223adedb4083b7adf8db/index.js#L631)
11661173
1174+ // Ajv does not support js date format. In order to properly validate objects containing a date,
1175+ // it needs to replace all occurrences of the string date format with a custom keyword fjs_date_type.
1176+ // (see https://github.com/fastify/fast-json-stringify/pull/441)
1177+ const extendedSchema = clone ( location . schema )
1178+ extendDateTimeType ( extendedSchema )
1179+
11671180 const schemaKey = location . schema . $id || randomUUID ( )
1168- ajvInstance . addSchema ( location . schema , schemaKey )
1181+ ajvInstance . addSchema ( extendedSchema , schemaKey )
11691182
11701183 code += `
1171- ${ index === 0 ? 'if' : 'else if' } (ajv.validate("${ schemaKey } ", ${ testValue } ))
1184+ ${ index === 0 ? 'if' : 'else if' } (ajv.validate("${ schemaKey } ", ${ input } ))
11721185 ${ nestedResult . code }
11731186 `
11741187 laterCode = nestedResult . laterCode
@@ -1261,6 +1274,19 @@ function buildValue (laterCode, locationPath, input, location, isArray) {
12611274 return { code, laterCode }
12621275}
12631276
1277+ function extendDateTimeType ( schema ) {
1278+ if ( schema . type === 'string' && [ 'date-time' , 'date' , 'time' ] . includes ( schema . format ) ) {
1279+ schema . fjs_date_type = schema . format
1280+ delete schema . type
1281+ delete schema . format
1282+ }
1283+ for ( const property in schema ) {
1284+ if ( typeof schema [ property ] === 'object' ) {
1285+ extendDateTimeType ( schema [ property ] )
1286+ }
1287+ }
1288+ }
1289+
12641290function isEmpty ( schema ) {
12651291 // eslint-disable-next-line
12661292 for ( var key in schema ) {
0 commit comments