util: validate Duration/Timestamp in TimeUtil::FromString against spec limits (#27443)

## Summary

- `TimeUtil::FromString` for both `Duration` and `Timestamp` accepts malformed inputs that produce protos failing `IsDurationValid`/`IsTimestampValid`.
- The companion JSON parser (`JsonStringToMessage`) correctly rejects all the same inputs, creating a **cross-API divergence**.
- **8 sub-bug classes** identified (4 Duration, 4 Timestamp):

| Class | Type | Trigger | Effect |
|-------|------|---------|--------|
| A | Duration | Leading whitespace + negative (`" -3.54s"`) | Signs mismatch: seconds<0, nanos>0 |
| B | Duration | Double minus (`"--3.54s"`) | Signs mismatch: seconds>0, nanos<0 |
| C | Duration | >9 fractional digits (`"0.1234567890s"`) | nanos exceeds 999999999 |
| D | Duration | seconds > 315576000000 (`"315576000001s"`) | Out-of-range seconds |
| E | Timestamp | Year 0000 (`"0000-12-31T23:59:59Z"`) | seconds < kTimestampMinSeconds |
| F | Timestamp | Year 10000 (`"10000-01-01T00:00:00Z"`) | seconds > kTimestampMaxSeconds |
| G | Timestamp | 5-digit year (`"99999-01-01T00:00:00Z"`) | Wildly past spec max |
| H | Timestamp | Negative year (`"-0001-01-01T00:00:00Z"`) | Not in RFC3339 spec |

**Fix**: Add `IsDurationValid()` check after parsing in the Duration overload, and `IsTimestampValid()` check in the Timestamp overload. Return false and clear output on failure. Both validation functions already exist in `TimeUtil` and are used by `ABSL_DCHECK` elsewhere.

**Impact**: Systems using Duration/Timestamp for security-relevant time bounds (gRPC deadlines, Envoy timeouts, retry intervals, IAM token TTLs). Class A is most dangerous: `seconds * 1e9 + nanos` gives `-2.46e9` instead of intended `-3.54e9` — a 31% magnitude shift. 17 fuzzer crash inputs collected.

Found by AFL++ property-check fuzzing (`pb_json_wkt_fuzzer` mode 1).

## Test plan

- [ ] Verify `TimeUtil::FromString(" -3.54s", &d)` returns false (class A)
- [ ] Verify `TimeUtil::FromString("--3.54s", &d)` returns false (class B)
- [ ] Verify `TimeUtil::FromString("0.1234567890s", &d)` returns false (class C)
- [ ] Verify `TimeUtil::FromString("315576000001s", &d)` returns false (class D)
- [ ] Verify `TimeUtil::FromString("0000-12-31T23:59:59Z", &t)` returns false (class E)
- [ ] Verify `TimeUtil::FromString("10000-01-01T00:00:00Z", &t)` returns false (class F)
- [ ] Verify valid inputs still parse successfully
- [ ] Run existing TimeUtil tests to confirm no regressions

Closes #27443

COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/27443 from jortles:fix/timeutil-fromstring-validation b57f4459f9672337fbd1b6ee77c299c4aebaa33f
PiperOrigin-RevId: 938703001
diff --git a/src/google/protobuf/util/time_util.cc b/src/google/protobuf/util/time_util.cc
index 8cbfe00..d615545 100644
--- a/src/google/protobuf/util/time_util.cc
+++ b/src/google/protobuf/util/time_util.cc
@@ -136,9 +136,9 @@
   if (!absl::ParseTime(absl::RFC3339_full, value, &result, nullptr)) {
     return false;
   }
-  timespec spec = absl::ToTimespec(result);
-  *seconds = spec.tv_sec;
-  *nanos = spec.tv_nsec;
+  *seconds = absl::ToUnixSeconds(result);
+  absl::Duration remainder = result - absl::FromUnixSeconds(*seconds);
+  *nanos = static_cast<int32_t>(absl::ToInt64Nanoseconds(remainder));
   return true;
 }
 
@@ -189,7 +189,15 @@
   if (!ParseTime(value, &seconds, &nanos)) {
     return false;
   }
+  // Validate before CreateNormalizedTimestamp, which has a DCHECK on range.
+  if (seconds < kTimestampMinSeconds || seconds > kTimestampMaxSeconds) {
+    return false;
+  }
   *timestamp = CreateNormalizedTimestamp(seconds, nanos);
+  if (!IsTimestampValid(*timestamp)) {
+    timestamp->Clear();
+    return false;
+  }
   return true;
 }
 
@@ -264,6 +272,10 @@
   }
   duration->set_seconds(seconds);
   duration->set_nanos(static_cast<int32_t>(nanos));
+  if (!IsDurationValid(*duration)) {
+    duration->Clear();
+    return false;
+  }
   return true;
 }
 
diff --git a/src/google/protobuf/util/time_util_test.cc b/src/google/protobuf/util/time_util_test.cc
index 7a9c961..0b3528d 100644
--- a/src/google/protobuf/util/time_util_test.cc
+++ b/src/google/protobuf/util/time_util_test.cc
@@ -509,6 +509,33 @@
 #endif  // !NDEBUG
 #endif  // GTEST_HAS_DEATH_TEST
 
+TEST(TimeUtilTest, FromStringRejectsMalformedDuration) {
+  Duration d;
+  // Class A: leading whitespace + negative — signs mismatch
+  EXPECT_FALSE(TimeUtil::FromString(" -3.54s", &d));
+  // Class B: double minus — signs mismatch
+  EXPECT_FALSE(TimeUtil::FromString("--3.54s", &d));
+  // Class C: >9 fractional digits — nanos exceeds 999999999
+  EXPECT_FALSE(TimeUtil::FromString("0.1234567890s", &d));
+  // Class D: seconds > 315576000000 — out of range
+  EXPECT_FALSE(TimeUtil::FromString("315576000001s", &d));
+  // Valid inputs still work
+  EXPECT_TRUE(TimeUtil::FromString("3.54s", &d));
+  EXPECT_TRUE(TimeUtil::FromString("-3.54s", &d));
+  EXPECT_TRUE(TimeUtil::FromString("315576000000s", &d));
+}
+
+TEST(TimeUtilTest, FromStringRejectsMalformedTimestamp) {
+  Timestamp t;
+  // Class E: year 0000
+  EXPECT_FALSE(TimeUtil::FromString("0000-12-31T23:59:59Z", &t));
+  // Class F: year 10000
+  EXPECT_FALSE(TimeUtil::FromString("10000-01-01T00:00:00Z", &t));
+  // Valid inputs still work
+  EXPECT_TRUE(TimeUtil::FromString("0001-01-01T00:00:00Z", &t));
+  EXPECT_TRUE(TimeUtil::FromString("9999-12-31T23:59:59Z", &t));
+}
+
 }  // namespace
 }  // namespace util
 }  // namespace protobuf