Repository: networknt/json-schema-validator (java)

Issue:
`time` validation incomplete
I have a schema using `draft-07`. It has a property using `format: time`. When validating the following input `07:00:00-06:00`, `json-schema-validator` 1.0.72 generates an error `X: does not match the time pattern ^\d{2}:\d{2}:\d{2}(?:\.\d+)?$`. However, I believe my input to be valid per the [spec](https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-7.3.1). It appears the `time-offset` may not be implemented.

Pull request description:
Support time offsets in `format: time`
Resolves #669. Note that the regex has changed from what was originally agreed upon in #669. This is in order to reuse most of the test suite that was developed for #575.

Patch:
diff --git a/src/main/java/com/networknt/schema/JsonMetaSchema.java b/src/main/java/com/networknt/schema/JsonMetaSchema.java
index 5a5fabf..5680f4f 100644
--- a/src/main/java/com/networknt/schema/JsonMetaSchema.java
+++ b/src/main/java/com/networknt/schema/JsonMetaSchema.java
@@ -40,7 +40,7 @@ public class JsonMetaSchema {
 
     // this section contains formats that is common for all specification versions.
     static {
-        COMMON_BUILTIN_FORMATS.add(pattern("time", "^\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?$"));
+        COMMON_BUILTIN_FORMATS.add(pattern("time", "^(([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]|(24:00:00))(?:\\.\\d+)?(Z|[+-]((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?$"));
         COMMON_BUILTIN_FORMATS.add(pattern("ip-address",
                 "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"));
         COMMON_BUILTIN_FORMATS.add(pattern("ipv4", "^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$"));


Test patch:
diff --git a/src/test/java/com/networknt/schema/Issue669Test.java b/src/test/java/com/networknt/schema/Issue669Test.java
new file mode 100644
index 0000000..fc678b4
--- /dev/null
+++ b/src/test/java/com/networknt/schema/Issue669Test.java
@@ -0,0 +1,108 @@
+package com.networknt.schema;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.io.InputStream;
+import java.util.Set;
+import java.util.stream.Stream;
+
+public class Issue669Test {
+    private static JsonSchema schema;
+
+    @BeforeAll
+    static void init() {
+        JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
+        String schemaPath = "/draft7/issue669.json";
+        InputStream schemaInputStream = Issue669Test.class.getResourceAsStream(schemaPath);
+        schema = factory.getSchema(schemaInputStream);
+    }
+
+    public static Stream<Arguments> validTimeZoneOffsets() {
+        String json1 = "{\"testTime\":\"08:27:53-05:00\"}";         // America/New_York
+        String json2 = "{\"testTime\":\"08:27:53-04:00\"}";         // America/New_York (DST)
+        String json3 = "{\"testTime\":\"08:27:53-03:30\"}";         // America/St_Johns
+        String json4 = "{\"testTime\":\"08:27:53-02:30\"}";         // America/St_Johns (DST)
+        String json5 = "{\"testTime\":\"08:27:53+02:00\"}";         // Africa/Cairo
+        String json6 = "{\"testTime\":\"08:27:53+03:30\"}";         // Asia/Tehran
+        String json7 = "{\"testTime\":\"08:27:53+04:30\"}";         // Asia/Tehran (DST)
+        String json8 = "{\"testTime\":\"08:27:53+04:00\"}";         // Asia/Dubai
+        String json9 = "{\"testTime\":\"08:27:53+05:00\"}";         // Indian/Maldives
+        String json10 = "{\"testTime\":\"08:27:53+05:30\"}";        // Asia/Kolkata
+        String json11 = "{\"testTime\":\"08:27:53+10:00\"}";        // Australia/Sydney
+        String json12 = "{\"testTime\":\"08:27:53+11:00\"}";        // Australia/Sydney (DST)
+        String json13 = "{\"testTime\":\"08:27:53+14:00\"}";        // Pacific/Kiritimati
+        String json14 = "{\"testTime\":\"18:45:32.123-05:00\"}";    // America/New_York
+        String json15 = "{\"testTime\":\"18:45:32.123456-05:00\"}"; // America/New_York
+        String json16 = "{\"testTime\":\"08:27:53Z\"}";             // UTC
+        String json17 = "{\"testTime\":\"08:27:53+00:00\"}";        // UTC
+
+        return Stream.of(
+                Arguments.of(json1),
+                Arguments.of(json2),
+                Arguments.of(json3),
+                Arguments.of(json4),
+                Arguments.of(json5),
+                Arguments.of(json6),
+                Arguments.of(json7),
+                Arguments.of(json8),
+                Arguments.of(json9),
+                Arguments.of(json10),
+                Arguments.of(json11),
+                Arguments.of(json12),
+                Arguments.of(json13),
+                Arguments.of(json14),
+                Arguments.of(json15),
+                Arguments.of(json16),
+                Arguments.of(json17)
+        );
+    }
+
+    /**
+     * Confirms that valid time zone offsets do not result in a JSON validation error.
+     *
+     * @param jsonObject a sample JSON payload to test
+     */
+    @ParameterizedTest
+    @MethodSource("validTimeZoneOffsets")
+    void testValidTimeZoneOffsets(String jsonObject) throws JsonProcessingException {
+        Set<ValidationMessage> errors = schema.validate(new ObjectMapper().readTree(jsonObject));
+        Assertions.assertTrue(errors.isEmpty());
+    }
+
+    public static Stream<Arguments> invalidTimeRepresentations() {
+
+        // Invalid JSON payload: seconds missing from time
+        String json1 = "{\"testTime\":\"11:23Z\"}";
+        // Invalid JSON payload: Text instead of date-time value
+        String json2 = "{\"testTime\":\"Orlando\"}";
+        // Invalid JSON payload: A time zone offset of +23:00 is not valid
+        String json3 = "{\"testTime\":\"08:27:53+23:00\"}";
+        // Invalid JSON payload: A time zone offset of -23:00 is not valid
+        String json4 = "{\"testTime\":\"08:27:53-23:00\"}";
+
+        return Stream.of(
+                Arguments.of(json1),
+                Arguments.of(json2),
+                Arguments.of(json3),
+                Arguments.of(json4)
+        );
+    }
+
+    /**
+     * Confirms that invalid time representations result in one or more a JSON validation errors.
+     *
+     * @param jsonObject a sample JSON payload to test
+     */
+    @ParameterizedTest
+    @MethodSource("invalidTimeRepresentations")
+    void testInvalidTimeRepresentations(String jsonObject) throws JsonProcessingException {
+        Set<ValidationMessage> errors = schema.validate(new ObjectMapper().readTree(jsonObject));
+        Assertions.assertFalse(errors.isEmpty());
+    }
+}
diff --git a/src/test/resources/draft7/issue669.json b/src/test/resources/draft7/issue669.json
new file mode 100644
index 0000000..ec6726b
--- /dev/null
+++ b/src/test/resources/draft7/issue669.json
@@ -0,0 +1,12 @@
+{
+	"$schema": "http://json-schema.org/draft-07/schema#",
+	"title": "Test Time Zone Schema (for testing time zones with offsets)",
+	"type": "object",
+	"properties": {
+		"testTime": {
+			"description": "A time value with offsets.",
+			"type": "string",
+			"format": "time"
+		}
+	}
+}
\ No newline at end of file