This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new aef4190336 Unit tests
aef4190336 is described below

commit aef4190336956ca1eb64d6b22bd4214ace02b260
Author: James Bognar <[email protected]>
AuthorDate: Thu Dec 4 08:05:28 2025 -0800

    Unit tests
---
 .../org/apache/juneau/bean/atom/CommonEntry.java   |   2 +-
 .../java/org/apache/juneau/bean/atom/Entry.java    |   2 +-
 .../commons/utils/GranularZonedDateTime.java       | 726 +++++++--------------
 .../main/java/org/apache/juneau/BeanSession.java   |   4 +-
 .../apache/juneau/oapi/OpenApiParserSession.java   |   2 +-
 .../juneau/objecttools/TimeMatcherFactory.java     |   6 +-
 .../microservice/resources/LogsResource.java       |   8 +-
 .../a/rttests/RoundTripTransformBeans_Test.java    |   8 +-
 .../juneau/commons/utils/DateUtils_Test.java       | 328 ----------
 .../commons/utils/GranularZonedDateTime_Test.java  | 598 ++++++++---------
 .../httppart/OpenApiPartSerializer_Test.java       |  10 +-
 .../java/org/apache/juneau/oapi/OpenApi_Test.java  |   2 +-
 .../juneau/objecttools/ObjectSearcher_Test.java    |   2 +-
 13 files changed, 557 insertions(+), 1141 deletions(-)

diff --git 
a/juneau-bean/juneau-bean-atom/src/main/java/org/apache/juneau/bean/atom/CommonEntry.java
 
b/juneau-bean/juneau-bean-atom/src/main/java/org/apache/juneau/bean/atom/CommonEntry.java
index a6c83208c8..3055db5603 100644
--- 
a/juneau-bean/juneau-bean-atom/src/main/java/org/apache/juneau/bean/atom/CommonEntry.java
+++ 
b/juneau-bean/juneau-bean-atom/src/main/java/org/apache/juneau/bean/atom/CommonEntry.java
@@ -374,7 +374,7 @@ public class CommonEntry extends Common {
         * @return This object.
         */
        public CommonEntry setUpdated(String value) {
-               setUpdated(opt(value).filter(x1 -> ! isBlank(x1)).map(x -> 
GranularZonedDateTime.parse(value).getZonedDateTime()).map(GregorianCalendar::from).orElse(null));
+               setUpdated(opt(value).filter(x1 -> ! isBlank(x1)).map(x -> 
GranularZonedDateTime.of(value).getZonedDateTime()).map(GregorianCalendar::from).orElse(null));
                return this;
        }
 }
\ No newline at end of file
diff --git 
a/juneau-bean/juneau-bean-atom/src/main/java/org/apache/juneau/bean/atom/Entry.java
 
b/juneau-bean/juneau-bean-atom/src/main/java/org/apache/juneau/bean/atom/Entry.java
index e7ed17deef..c0cdcfeba3 100644
--- 
a/juneau-bean/juneau-bean-atom/src/main/java/org/apache/juneau/bean/atom/Entry.java
+++ 
b/juneau-bean/juneau-bean-atom/src/main/java/org/apache/juneau/bean/atom/Entry.java
@@ -331,7 +331,7 @@ public class Entry extends CommonEntry {
         * @return This object.
         */
        public Entry setPublished(String value) {
-               setPublished(opt(value).filter(x1 -> ! isBlank(x1)).map(x -> 
GranularZonedDateTime.parse(value).getZonedDateTime()).map(GregorianCalendar::from).orElse(null));
+               setPublished(opt(value).filter(x1 -> ! isBlank(x1)).map(x -> 
GranularZonedDateTime.of(value).getZonedDateTime()).map(GregorianCalendar::from).orElse(null));
                return this;
        }
 
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/GranularZonedDateTime.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/GranularZonedDateTime.java
index ccd46759de..52d44eb08b 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/GranularZonedDateTime.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/GranularZonedDateTime.java
@@ -18,8 +18,6 @@ package org.apache.juneau.commons.utils;
 
 import static org.apache.juneau.commons.utils.AssertionUtils.*;
 import static org.apache.juneau.commons.utils.StateEnum.*;
-import static org.apache.juneau.commons.utils.StringUtils.*;
-import static org.apache.juneau.commons.utils.ThrowableUtils.*;
 import static org.apache.juneau.commons.utils.Utils.*;
 
 import java.time.*;
@@ -34,14 +32,53 @@ import java.util.*;
  * This class combines a {@link ZonedDateTime} with a {@link ChronoField} 
precision identifier,
  * allowing for granular time operations such as rolling by specific time 
units.
  *
+ * <p>
+ * The precision field indicates the granularity of the time value, which 
determines how the
+ * {@link #roll(int)} method behaves. For example, a precision of {@link 
ChronoField#YEAR} means
+ * rolling by 1 will advance the year, while a precision of {@link 
ChronoField#HOUR_OF_DAY} means
+ * rolling by 1 will advance the hour.
+ *
+ * <h5 class='section'>ISO8601 Parsing:</h5>
+ * <p>
+ * The {@link #of(String)} method can parse various ISO8601 timestamp formats:
+ * <ul>
+ *     <li>Date formats: <js>"2011"</js>, <js>"2011-01"</js>, 
<js>"2011-01-15"</js>
+ *     <li>DateTime formats: <js>"2011-01-15T12"</js>, 
<js>"2011-01-15T12:30"</js>, <js>"2011-01-15T12:30:45"</js>
+ *     <li>With fractional seconds: <js>"2011-01-15T12:30:45.123"</js>, 
<js>"2011-01-15T12:30:45,123"</js>
+ *     <li>Time-only formats: <js>"T12"</js>, <js>"T12:30"</js>, 
<js>"T12:30:45"</js>
+ *     <li>With timezone: <js>"2011-01-15T12:30:45Z"</js>, 
<js>"2011-01-15T12:30:45+05:30"</js>, <js>"2011-01-15T12:30:45-05:30"</js>
+ * </ul>
+ *
+ * <p>
+ * The precision is automatically determined from the input format. For 
example:
+ * <ul>
+ *     <li><js>"2011"</js> → {@link ChronoField#YEAR}
+ *     <li><js>"2011-01"</js> → {@link ChronoField#MONTH_OF_YEAR}
+ *     <li><js>"2011-01-15"</js> → {@link ChronoField#DAY_OF_MONTH}
+ *     <li><js>"2011-01-15T12"</js> → {@link ChronoField#HOUR_OF_DAY}
+ *     <li><js>"2011-01-15T12:30"</js> → {@link ChronoField#MINUTE_OF_HOUR}
+ *     <li><js>"2011-01-15T12:30:45"</js> → {@link 
ChronoField#SECOND_OF_MINUTE}
+ *     <li><js>"2011-01-15T12:30:45.123"</js> → {@link 
ChronoField#MILLI_OF_SECOND} (1-3 digits)
+ *     <li><js>"2011-01-15T12:30:45.123456789"</js> → {@link 
ChronoField#NANO_OF_SECOND} (4-9 digits)
+ * </ul>
+ *
  * <h5 class='section'>Example:</h5>
  * <p class='bjava'>
- *     <jc>// Create with year precision</jc>
- *     GranularZonedDateTime <jv>gdt</jv> = 
GranularZonedDateTime.parse(<js>"2011"</js>);
+ *     <jc>// Parse an ISO8601 timestamp with year precision</jc>
+ *     GranularZonedDateTime <jv>gdt</jv> = 
GranularZonedDateTime.<jsm>of</jsm>(<js>"2011"</js>);
  *     <jc>// Roll forward by one year</jc>
- *     <jv>gdt</jv>.roll(1);
+ *     <jv>gdt</jv> = <jv>gdt</jv>.<jsm>roll</jsm>(1);
  *     <jc>// Get the ZonedDateTime</jc>
- *     ZonedDateTime <jv>zdt</jv> = <jv>gdt</jv>.getZonedDateTime();
+ *     ZonedDateTime <jv>zdt</jv> = <jv>gdt</jv>.<jsm>getZonedDateTime</jsm>();
+ *     <jc>// Result: 2012-01-01T00:00:00 with system default timezone</jc>
+ * </p>
+ *
+ * <p class='bjava'>
+ *     <jc>// Parse a datetime with hour precision</jc>
+ *     GranularZonedDateTime <jv>gdt2</jv> = 
GranularZonedDateTime.<jsm>of</jsm>(<js>"2011-01-15T12Z"</js>);
+ *     <jc>// Roll forward by 2 hours</jc>
+ *     <jv>gdt2</jv> = 
<jv>gdt2</jv>.<jsm>roll</jsm>(<jv>ChronoField</jv>.<jf>HOUR_OF_DAY</jf>, 2);
+ *     <jc>// Result: 2011-01-15T14:00:00Z</jc>
  * </p>
  *
  * <h5 class='section'>Thread Safety:</h5>
@@ -57,512 +94,120 @@ import java.util.*;
 public class GranularZonedDateTime {
 
        /**
-        * Parses a timestamp string and returns a GranularZonedDateTime.
+        * Creates a GranularZonedDateTime from a Date with the specified 
precision.
         *
         * <p>
-        * This method uses {@link DateUtils#fromIso8601(String)} for parsing 
and
-        * determines precision based on the input string format.
-        *
-        * @param seg The string segment to parse.
-        * @return A GranularZonedDateTime representing the parsed timestamp.
-        * @throws BasicRuntimeException If the string cannot be parsed as a 
valid timestamp.
-        */
-       public static GranularZonedDateTime parse(String seg) {
-               return parse2(seg);
-       }
-
-       /** The ZonedDateTime value */
-       public final ZonedDateTime zdt;
-
-       /** The precision of this time value */
-       public final ChronoField precision;
-
-       /**
-        * Constructor.
-        *
-        * @param date The date to wrap.
-        * @param precision The precision of this time value.
-        */
-       public GranularZonedDateTime(Date date, ChronoField precision) {
-               this.zdt = date.toInstant().atZone(ZoneId.systemDefault());
-               this.precision = precision;
-       }
-
-       /**
-        * Constructor.
-        *
-        * @param zdt The ZonedDateTime value.
-        * @param precision The precision of this time value.
-        */
-       public GranularZonedDateTime(ZonedDateTime zdt, ChronoField precision) {
-               this.zdt = zdt;
-               this.precision = precision;
-       }
-
-       /**
-        * Creates a copy of this object.
-        *
-        * @return A new GranularZonedDateTime with the same values.
-        */
-       public GranularZonedDateTime copy() {
-               return new GranularZonedDateTime(zdt, precision);
-       }
-
-       /**
-        * Returns the ZonedDateTime value.
-        *
-        * @return The ZonedDateTime value.
-        */
-       public ZonedDateTime getZonedDateTime() { return zdt; }
-
-       /**
-        * Rolls this time value by the specified amount using the specified 
field.
+        * The date is converted to a ZonedDateTime using the system default 
timezone.
         *
-        * @param field The field to roll by.
-        * @param amount The amount to roll by.
-        * @return A new GranularZonedDateTime with the rolled value.
+        * @param date The date to convert.
+        * @param precision The precision of the time value.
+        * @return A new GranularZonedDateTime instance.
+        * @throws IllegalArgumentException if date or precision is null.
         */
-       public GranularZonedDateTime roll(ChronoField field, int amount) {
-               ChronoUnit unit = toChronoUnit(field);
-               if (nn(unit)) {
-                       ZonedDateTime newZdt = zdt.plus(amount, unit);
-                       return new GranularZonedDateTime(newZdt, precision);
-               }
-               return this;
+       public static GranularZonedDateTime of(Date date, ChronoField 
precision) {
+               return of(date, precision, ZoneId.systemDefault());
        }
 
        /**
-        * Rolls this time value by the specified amount using the current 
precision.
-        *
-        * @param amount The amount to roll by.
-        * @return A new GranularZonedDateTime with the rolled value.
-        */
-       public GranularZonedDateTime roll(int amount) {
-               return roll(precision, amount);
-       }
-
-       /**
-        * Converts a ChronoField to its corresponding ChronoUnit.
+        * Creates a GranularZonedDateTime from a Date with the specified 
precision and timezone.
         *
         * <p>
-        * This method provides a mapping from date/time fields to time units.
-        * Not all ChronoField values have direct ChronoUnit equivalents.
+        * The date is converted to a ZonedDateTime using the specified 
timezone.
         *
-        * @param field The ChronoField to convert
-        * @return The corresponding ChronoUnit, or null if no direct mapping 
exists
+        * @param date The date to convert.
+        * @param precision The precision of the time value.
+        * @param zoneId The timezone to use.
+        * @return A new GranularZonedDateTime instance.
+        * @throws IllegalArgumentException if date, precision, or zoneId is 
null.
         */
-       private static ChronoUnit toChronoUnit(ChronoField field) {
-               return switch (field) {
-                       case YEAR -> ChronoUnit.YEARS;
-                       case MONTH_OF_YEAR -> ChronoUnit.MONTHS;
-                       case DAY_OF_MONTH -> ChronoUnit.DAYS;
-                       case HOUR_OF_DAY -> ChronoUnit.HOURS;
-                       case MINUTE_OF_HOUR -> ChronoUnit.MINUTES;
-                       case SECOND_OF_MINUTE -> ChronoUnit.SECONDS;
-                       case MILLI_OF_SECOND -> ChronoUnit.MILLIS;
-                       default -> null;
-               };
+       public static GranularZonedDateTime of(Date date, ChronoField 
precision, ZoneId zoneId) {
+               return of(date.toInstant().atZone(zoneId), precision);
        }
 
        /**
-        * Determines the precision level of an ISO8601 date/time string using 
a state machine.
+        * Parses an ISO8601 timestamp string into a GranularZonedDateTime.
         *
         * <p>
-        * This method analyzes the structure of a date/time string to 
determine the finest level of precision
-        * represented. It uses a state machine to parse the string character 
by character, tracking the precision
-        * level as it encounters different components.
+        * This method parses various ISO8601 formats and automatically 
determines the precision
+        * based on the format. If no timezone is specified in the string, the 
system default
+        * timezone is used.
         *
-        * <p>
-        * The method supports the following ISO8601 formats:
+        * <h5 class='section'>Supported Formats:</h5>
         * <ul>
-        *      <li><js>"YYYY"</js> → {@link ChronoField#YEAR}
-        *      <li><js>"YYYY-MM"</js> → {@link ChronoField#MONTH_OF_YEAR}
-        *      <li><js>"YYYY-MM-DD"</js> → {@link ChronoField#DAY_OF_MONTH}
-        *      <li><js>"YYYY-MM-DDTHH"</js> → {@link ChronoField#HOUR_OF_DAY}
-        *      <li><js>"YYYY-MM-DDTHH:MM"</js> → {@link 
ChronoField#MINUTE_OF_HOUR}
-        *      <li><js>"YYYY-MM-DDTHH:MM:SS"</js> → {@link 
ChronoField#SECOND_OF_MINUTE}
-        *      <li><js>"YYYY-MM-DDTHH:MM:SS.SSS"</js> → {@link 
ChronoField#MILLI_OF_SECOND}
+        *      <li>Date: <js>"2011"</js>, <js>"2011-01"</js>, 
<js>"2011-01-15"</js>
+        *      <li>DateTime: <js>"2011-01-15T12"</js>, 
<js>"2011-01-15T12:30"</js>, <js>"2011-01-15T12:30:45"</js>
+        *      <li>With fractional seconds: 
<js>"2011-01-15T12:30:45.123"</js>, <js>"2011-01-15T12:30:45,123"</js>
+        *      <li>Time-only: <js>"T12"</js>, <js>"T12:30"</js>, 
<js>"T12:30:45"</js> (uses current date)
+        *      <li>With timezone: <js>"2011-01-15T12:30:45Z"</js>, 
<js>"2011-01-15T12:30:45+05:30"</js>, <js>"2011-01-15T12:30:45-05:30"</js>
         * </ul>
         *
+        * <h5 class='section'>Timezone Handling:</h5>
+        * <ul>
+        *      <li>If the string contains a timezone (Z, +HH:mm, -HH:mm, 
etc.), that timezone is used.
+        *      <li>If no timezone is specified, the system default timezone is 
used.
+        *      <li>For time-only formats (starting with "T"), the current date 
is used with the specified or default timezone.
+        * </ul>
+        *
+        * <h5 class='section'>Precision Detection:</h5>
         * <p>
-        * Timezone information (Z, +HH:mm, -HH:mm) is preserved but doesn't 
affect the precision level.
-        * Invalid or unrecognized formats default to {@link 
ChronoField#MILLI_OF_SECOND}.
+        * The precision is automatically determined from the format:
+        * <ul>
+        *      <li>Year only → {@link ChronoField#YEAR}
+        *      <li>Year-Month → {@link ChronoField#MONTH_OF_YEAR}
+        *      <li>Year-Month-Day → {@link ChronoField#DAY_OF_MONTH}
+        *      <li>With hour → {@link ChronoField#HOUR_OF_DAY}
+        *      <li>With minute → {@link ChronoField#MINUTE_OF_HOUR}
+        *      <li>With second → {@link ChronoField#SECOND_OF_MINUTE}
+        *      <li>With 1-3 fractional digits → {@link 
ChronoField#MILLI_OF_SECOND}
+        *      <li>With 4-9 fractional digits → {@link 
ChronoField#NANO_OF_SECOND}
+        * </ul>
         *
-        * @param seg The date/time string to analyze (can be null or empty)
-        * @return The ChronoField representing the precision level, or {@link 
ChronoField#MILLI_OF_SECOND} for invalid/empty strings
+        * @param timestamp The ISO8601 timestamp string to parse.
+        * @return A new GranularZonedDateTime instance.
+        * @throws IllegalArgumentException if timestamp is null.
+        * @throws DateTimeParseException if the timestamp format is invalid.
         */
-       private static ChronoField getPrecisionFromString(String seg) {
-               if (isEmpty(seg))
-                       return ChronoField.MILLI_OF_SECOND;
-
-               // States:
-               // S1: Looking for year digits (YYYY)
-               // S2: Found year, looking for - or T or end (YYYY)
-               // S3: Found -, looking for month digits (YYYY-MM)
-               // S4: Found month, looking for - or T or end (YYYY-MM)
-               // S5: Found -, looking for day digits (YYYY-MM-DD)
-               // S6: Found day, looking for T or end (YYYY-MM-DD)
-               // S7: Found T, looking for hour digits (YYYY-MM-DDTHH)
-               // S8: Found hour, looking for : or end (YYYY-MM-DDTHH)
-               // S9: Found :, looking for minute digits (YYYY-MM-DDTHH:MM)
-               // S10: Found minute, looking for : or end (YYYY-MM-DDTHH:MM)
-               // S11: Found :, looking for second digits (YYYY-MM-DDTHH:MM:SS)
-               // S12: Found second, looking for . or end (YYYY-MM-DDTHH:MM:SS)
-               // S13: Found ., looking for millisecond digits 
(YYYY-MM-DDTHH:MM:SS.SSS)
-               // S14: Found timezone (Z, +HH:mm, -HH:mm)
-
-               seg = seg.replace(' ', 'T').replace(',', '.');
-
-               var state = S1;
-               var precision = ChronoField.YEAR; // Track precision as we go
-
-               int year, month, day, hour, minute, second, ms;
-
-               for (var i = 0; i < seg.length(); i++) {
-                       var c = seg.charAt(i);
-
-                       if (state == S1) {
-                               // S1: Looking for year digits (YYYY)
-                               if (Character.isDigit(c)) {
-                                       state = S2;
-                               } else if (c == '-') {
-                                       state = S3;
-                                       precision = ChronoField.MONTH_OF_YEAR;
-                               } else if (c == 'T') {
-                                       state = S7;
-                                       precision = ChronoField.HOUR_OF_DAY;
-                               } else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S14;
-                                       // Keep current precision (YEAR)
-                               }
-                       } else if (state == S2) {
-                               // S2: Found year, looking for - or T or end 
(YYYY)
-                               if (c == '-') {
-                                       state = S3;
-                                       precision = ChronoField.MONTH_OF_YEAR;
-                               } else if (c == 'T') {
-                                       state = S7;
-                                       precision = ChronoField.HOUR_OF_DAY;
-                               } else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S14;
-                                       // Keep current precision (YEAR)
-                               }
-                       } else if (state == S3) {
-                               // S3: Found -, looking for month digits 
(YYYY-MM)
-                               if (Character.isDigit(c)) {
-                                       state = S4;
-                               } else if (c == '-') {
-                                       state = S5;
-                                       precision = ChronoField.DAY_OF_MONTH;
-                               } else if (c == 'T') {
-                                       state = S7;
-                                       precision = ChronoField.HOUR_OF_DAY;
-                               } else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S14;
-                                       // Keep current precision 
(MONTH_OF_YEAR)
-                               }
-                       } else if (state == S4) {
-                               // S4: Found month, looking for - or T or end 
(YYYY-MM)
-                               if (c == '-') {
-                                       state = S5;
-                                       precision = ChronoField.DAY_OF_MONTH;
-                               } else if (c == 'T') {
-                                       state = S7;
-                                       precision = ChronoField.HOUR_OF_DAY;
-                               } else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S14;
-                                       // Keep current precision 
(MONTH_OF_YEAR)
-                               }
-                       } else if (state == S5) {
-                               // S5: Found -, looking for day digits 
(YYYY-MM-DD)
-                               if (Character.isDigit(c)) {
-                                       state = S6;
-                               } else if (c == 'T') {
-                                       state = S7;
-                                       precision = ChronoField.HOUR_OF_DAY;
-                               } else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S14;
-                                       // Keep current precision (DAY_OF_MONTH)
-                               }
-                       } else if (state == S6) {
-                               // S6: Found day, looking for T or end 
(YYYY-MM-DD)
-                               if (c == 'T') {
-                                       state = S7;
-                                       precision = ChronoField.HOUR_OF_DAY;
-                               } else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S14;
-                                       // Keep current precision (DAY_OF_MONTH)
-                               }
-                       } else if (state == S7) {
-                               // S7: Found T, looking for hour digits 
(YYYY-MM-DDTHH)
-                               if (Character.isDigit(c)) {
-                                       state = S8;
-                               } else if (c == ':') {
-                                       state = S9;
-                                       precision = ChronoField.MINUTE_OF_HOUR;
-                               } else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S14;
-                                       // Keep current precision (HOUR_OF_DAY)
-                               }
-                       } else if (state == S8) {
-                               // S8: Found hour, looking for : or end 
(YYYY-MM-DDTHH)
-                               if (c == ':') {
-                                       state = S9;
-                                       precision = ChronoField.MINUTE_OF_HOUR;
-                               } else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S14;
-                                       // Keep current precision (HOUR_OF_DAY)
-                               }
-                       } else if (state == S9) {
-                               // S9: Found :, looking for minute digits 
(YYYY-MM-DDTHH:MM)
-                               if (Character.isDigit(c)) {
-                                       state = S10;
-                               } else if (c == ':') {
-                                       state = S11;
-                                       precision = 
ChronoField.SECOND_OF_MINUTE;
-                               } else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S14;
-                                       // Keep current precision 
(MINUTE_OF_HOUR)
-                               }
-                       } else if (state == S10) {
-                               // S10: Found minute, looking for : or end 
(YYYY-MM-DDTHH:MM)
-                               if (c == ':') {
-                                       state = S11;
-                                       precision = 
ChronoField.SECOND_OF_MINUTE;
-                               } else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S14;
-                                       // Keep current precision 
(MINUTE_OF_HOUR)
-                               }
-                       } else if (state == S11) {
-                               // S11: Found :, looking for second digits 
(YYYY-MM-DDTHH:MM:SS)
-                               if (Character.isDigit(c)) {
-                                       state = S12;
-                               } else if (c == '.') {
-                                       state = S13;
-                                       precision = ChronoField.MILLI_OF_SECOND;
-                               } else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S14;
-                                       // Keep current precision 
(SECOND_OF_MINUTE)
-                               }
-                       } else if (state == S12) {
-                               // S12: Found second, looking for . or end 
(YYYY-MM-DDTHH:MM:SS)
-                               if (c == '.') {
-                                       state = S13;
-                                       precision = ChronoField.MILLI_OF_SECOND;
-                               } else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S14;
-                                       // Keep current precision 
(SECOND_OF_MINUTE)
-                               }
-                       } else if (state == S13) {
-                               // S13: Found ., looking for millisecond digits 
(YYYY-MM-DDTHH:MM:SS.SSS)
-                               if (Character.isDigit(c)) {
-                                       // Continue reading millisecond digits
-                               } else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S14;
-                                       // Keep current precision 
(MILLI_OF_SECOND)
-                               }
-                       } else if (state == S14) {
-                               // S14: Found timezone (Z, +HH:mm, -HH:mm) - 
precision already determined
-                               // Just continue reading timezone characters
-                       }
-               }
-
-               return precision;
+       public static GranularZonedDateTime of(String timestamp) {
+               return of(timestamp, null);
        }
 
        /**
-        * Parses an ISO8601 date string into a ZonedDateTime object.
+        * Parses an ISO8601 timestamp string into a GranularZonedDateTime with 
a default timezone.
         *
         * <p>
-        * This method converts an ISO8601 formatted date/time string into a 
ZonedDateTime object,
-        * which provides full timezone context including DST handling. The 
method automatically
-        * normalizes the input string to ensure it can be parsed correctly.
+        * This method is similar to {@link #of(String)}, but allows you to 
specify a default
+        * timezone to use when no timezone is present in the timestamp string.
         *
         * <p>
-        * The method supports the same ISO8601 formats as {@link 
#fromIso8601Calendar(String)},
-        * but returns a modern {@link ZonedDateTime} instead of a legacy 
{@link Calendar}.
+        * If the timestamp string contains a timezone (Z, +HH:mm, -HH:mm, 
etc.), that timezone
+        * takes precedence over the defaultZoneId parameter. The defaultZoneId 
is only used when
+        * no timezone is specified in the string.
         *
-        * <h5 class='section'>Examples:</h5>
+        * <h5 class='section'>Example:</h5>
         * <p class='bjava'>
-        *      <jc>// Parse UTC timezone</jc>
-        *      ZonedDateTime <jv>utcZdt</jv> = 
DateUtils.<jsm>fromIso8601</jsm>(<js>"2024-01-15T14:30:45Z"</js>);
-        *      <jc>// Result: ZonedDateTime with UTC timezone</jc>
-        *
-        *      <jc>// Parse offset timezone</jc>
-        *      ZonedDateTime <jv>estZdt</jv> = 
DateUtils.<jsm>fromIso8601</jsm>(<js>"2024-01-15T14:30:45-05:00"</js>);
-        *      <jc>// Result: ZonedDateTime with EST timezone (-05:00)</jc>
+        *      <jc>// Parse with default timezone</jc>
+        *      GranularZonedDateTime <jv>gdt1</jv> = 
GranularZonedDateTime.<jsm>of</jsm>(
+        *              <js>"2011-01-15T12:30:45"</js>,
+        *              
<jv>ZoneId</jv>.<jsm>of</jsm>(<js>"America/New_York"</js>)
+        *      );
+        *      <jc>// Result uses America/New_York timezone</jc>
         *
-        *      <jc>// Parse date only</jc>
-        *      ZonedDateTime <jv>dateZdt</jv> = 
DateUtils.<jsm>fromIso8601</jsm>(<js>"2024-01-15"</js>);
-        *      <jc>// Result: ZonedDateTime with time set to 00:00:00 in 
system timezone</jc>
+        *      <jc>// Parse with timezone in string (defaultZoneId is 
ignored)</jc>
+        *      GranularZonedDateTime <jv>gdt2</jv> = 
GranularZonedDateTime.<jsm>of</jsm>(
+        *              <js>"2011-01-15T12:30:45Z"</js>,
+        *              
<jv>ZoneId</jv>.<jsm>of</jsm>(<js>"America/New_York"</js>)
+        *      );
+        *      <jc>// Result uses UTC (Z), not America/New_York</jc>
         * </p>
         *
-        * <h5 class='section'>Advantages over Calendar:</h5>
-        * <ul>
-        *      <li><c>Immutable</c> - Thread-safe by design
-        *      <li><c>DST Aware</c> - Automatic Daylight Saving Time handling
-        *      <li><c>Modern API</c> - Part of Java 8+ time package
-        *      <li><c>Better Performance</c> - Optimized for modern JVMs
-        *      <li><c>Type Safety</c> - Compile-time validation of operations
-        * </ul>
-        *
-        * <h5 class='section'>Timezone Handling:</h5>
-        * <p>
-        * The method preserves the original timezone information from the 
ISO8601 string.
-        * If no timezone is specified, the system's default timezone is used. 
The resulting
-        * ZonedDateTime object will have the appropriate timezone set and will 
automatically
-        * handle DST transitions.
-        * </p>
-        *
-        * <h5 class='section'>Input Normalization:</h5>
-        * <p>
-        * The method automatically normalizes incomplete ISO8601 strings by:
-        * <ul>
-        *      <li>Adding missing time components (defaults to 00:00:00)
-        *      <li>Adding timezone information if missing (uses system default)
-        *      <li>Ensuring proper format compliance
-        * </ul>
-        * </p>
-        *
-        * See Also:  <a class="doclink" 
href="https://en.wikipedia.org/wiki/ISO_8601";>ISO 8601 - Wikipedia</a>
-        *
-        * @param s The ISO8601 formatted string to parse (can be null or empty)
-        * @return ZonedDateTime object representing the parsed date/time, or 
null if input is null/empty
-        * @throws DateTimeParseException if the string cannot be parsed as a 
valid ISO8601 date
-        * @see #fromIso8601Calendar(String)
-        * @see ZonedDateTime
+        * @param seg The ISO8601 timestamp string to parse.
+        * @param defaultZoneId The default timezone to use if no timezone is 
specified in the string.
+        *      If null, {@link ZoneId#systemDefault()} is used.
+        * @return A new GranularZonedDateTime instance.
+        * @throws IllegalArgumentException if seg is null.
+        * @throws DateTimeParseException if the timestamp format is invalid.
         */
-       private static ZonedDateTime fromIso8601(String s) {
-               if (StringUtils.isBlank(s))
-                       return null;
-
-               // Inline toValidIso8601DT logic
-               var in = s.trim();
-
-               // "2001-07-04T15:30:45Z"
-               // S1: Looking for -
-               // S2: Found -, looking for -
-               // S3: Found -, looking for T
-               // S4: Found T, looking for :
-               // S5: Found :, looking for :
-               // S6: Found :, looking for Z or - or + or . or ,
-               // S7: Found time zone
-               // S8: Found . or , after seconds, skipping milliseconds digits
-
-               var state = S1;
-               var needsT = false;
-               var timezoneAfterHour = false;  // Track if timezone found 
after hour (S4)
-               var timezoneAfterMinute = false;  // Track if timezone found 
after minute (S5)
-               var millisCommaIndex = -1;  // Track where comma-separated 
milliseconds start (to convert to dot)
-
-               for (var i = 0; i < in.length(); i++) {
-                       var c = in.charAt(i);
-                       if (state == S1) {
-                               if (c == '-')
-                                       state = S2;
-                       } else if (state == S2) {
-                               if (c == '-')
-                                       state = S3;
-                       } else if (state == S3) {
-                               if (c == 'T')
-                                       state = S4;
-                               if (c == ' ') {
-                                       state = S4;
-                                       needsT = true;
-                               }
-                       } else if (state == S4) {
-                               if (c == ':')
-                                       state = S5;
-                               else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S7;  // Timezone immediately 
after hour (e.g., "2011-01-15T12Z")
-                                       timezoneAfterHour = true;
-                               }
-                       } else if (state == S5) {
-                               if (c == ':')
-                                       state = S6;
-                               else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S7;  // Timezone immediately 
after minute (e.g., "2011-01-15T12:30Z")
-                                       timezoneAfterMinute = true;
-                               }
-                       } else if (state == S6) {
-                               if (c == '.' || c == ',') {
-                                       state = S8;  // Found milliseconds 
separator
-                                       if (c == ',') {
-                                               millisCommaIndex = i;  // Track 
comma to convert to dot
-                                       }
-                               } else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S7;
-                               }
-                       } else if (state == S8) {
-                               // S8: Reading milliseconds digits, looking for 
timezone
-                               if (Character.isDigit(c)) {
-                                       // Continue reading milliseconds
-                               } else if (c == 'Z' || c == '+' || c == '-') {
-                                       state = S7;
-                               }
-                       }
-               }
-
-               // Convert comma-separated milliseconds to dot-separated 
(ISO_DATE_TIME expects dots)
-               if (millisCommaIndex >= 0) {
-                       var chars = in.toCharArray();
-                       chars[millisCommaIndex] = '.';
-                       in = new String(chars);
-               }
-
-               if (needsT)
-                       in = in.replace(' ', 'T');
-
-               var validDate = switch (state) {
-                       case S1 -> in + "-01-01T00:00:00";
-                       case S2 -> in + "-01T00:00:00";
-                       case S3 -> in + "T00:00:00";
-                       case S4 -> in + ":00:00";
-                       case S5 -> in + ":00";
-                       case S6 -> in;  // Complete time, no timezone
-                       case S7 -> {
-                               // Complete time with timezone, but may need to 
add missing components
-                               if (timezoneAfterHour) {
-                                       // Timezone found after hour, need to 
add :00:00 before timezone
-                                       var tzIndex = in.length();
-                                       for (var i = in.length() - 1; i >= 0; 
i--) {
-                                               var ch = in.charAt(i);
-                                               if (ch == 'Z' || ch == '+' || 
ch == '-') {
-                                                       tzIndex = i;
-                                                       break;
-                                               }
-                                       }
-                                       yield in.substring(0, tzIndex) + 
":00:00" + in.substring(tzIndex);
-                               } else if (timezoneAfterMinute) {
-                                       // Timezone found after minute, need to 
add :00 before timezone
-                                       var tzIndex = in.length();
-                                       for (var i = in.length() - 1; i >= 0; 
i--) {
-                                               var ch = in.charAt(i);
-                                               if (ch == 'Z' || ch == '+' || 
ch == '-') {
-                                                       tzIndex = i;
-                                                       break;
-                                               }
-                                       }
-                                       yield in.substring(0, tzIndex) + ":00" 
+ in.substring(tzIndex);
-                               } else {
-                                       yield in;  // Complete time with 
timezone (already has seconds)
-                               }
-                       }
-                       default -> in;
-               };
-
-
-               if (state != S7)
-                       validDate += 
ZonedDateTime.now(ZoneId.systemDefault()).getOffset().toString();
-
-               return ZonedDateTime.parse(validDate, 
DateTimeFormatter.ISO_DATE_TIME);
-       }
-
-       public static GranularZonedDateTime parse2(String seg) {
-               return parse2(seg, null);
-       }
-
-       public static GranularZonedDateTime parse2(String seg, ZoneId 
defaultZoneId) {
+       public static GranularZonedDateTime of(String seg, ZoneId 
defaultZoneId) {
                assertArgNotNull("seg", seg);
                var digit = StringUtils.DIGIT;
 
@@ -983,6 +628,23 @@ public class GranularZonedDateTime {
                return new GranularZonedDateTime(zdt, precision);
        }
 
+       /**
+        * Creates a GranularZonedDateTime from a ZonedDateTime with the 
specified precision.
+        *
+        * <p>
+        * This is the most direct way to create a GranularZonedDateTime when 
you already have
+        * a ZonedDateTime and want to specify its precision.
+        *
+        * @param date The ZonedDateTime value.
+        * @param precision The precision of the time value.
+        * @return A new GranularZonedDateTime instance.
+        * @throws IllegalArgumentException if date or precision is null.
+        */
+       public static GranularZonedDateTime of(ZonedDateTime date, ChronoField 
precision) {
+               return new GranularZonedDateTime(date, precision);
+       }
+
+
        private static DateTimeParseException bad(String s, int pos) {
                return new DateTimeParseException("Invalid ISO8601 timestamp", 
s, pos);
        }
@@ -1012,4 +674,116 @@ public class GranularZonedDateTime {
                if (len == 8) return n * 10;
                return n;
        }
+
+       /**
+        * Converts a ChronoField to its corresponding ChronoUnit.
+        *
+        * <p>
+        * This method provides a mapping from date/time fields to time units.
+        * Not all ChronoField values have direct ChronoUnit equivalents.
+        *
+        * @param field The ChronoField to convert
+        * @return The corresponding ChronoUnit, or null if no direct mapping 
exists
+        */
+       private static ChronoUnit toChronoUnit(ChronoField field) {
+               return switch (field) {
+                       case YEAR -> ChronoUnit.YEARS;
+                       case MONTH_OF_YEAR -> ChronoUnit.MONTHS;
+                       case DAY_OF_MONTH -> ChronoUnit.DAYS;
+                       case HOUR_OF_DAY -> ChronoUnit.HOURS;
+                       case MINUTE_OF_HOUR -> ChronoUnit.MINUTES;
+                       case SECOND_OF_MINUTE -> ChronoUnit.SECONDS;
+                       case MILLI_OF_SECOND -> ChronoUnit.MILLIS;
+                       default -> null;
+               };
+       }
+
+       /** The ZonedDateTime value */
+       public final ZonedDateTime zdt;
+
+       /** The precision of this time value */
+       public final ChronoField precision;
+
+       /**
+        * Constructor.
+        *
+        * @param zdt The ZonedDateTime value.
+        * @param precision The precision of this time value.
+        */
+       public GranularZonedDateTime(ZonedDateTime zdt, ChronoField precision) {
+               this.zdt = zdt;
+               this.precision = precision;
+       }
+
+       /**
+        * Creates a copy of this object.
+        *
+        * @return A new GranularZonedDateTime with the same values.
+        */
+       public GranularZonedDateTime copy() {
+               return new GranularZonedDateTime(zdt, precision);
+       }
+
+       /**
+        * Returns the ZonedDateTime value.
+        *
+        * @return The ZonedDateTime value.
+        */
+       public ZonedDateTime getZonedDateTime() { return zdt; }
+
+       /**
+        * Rolls this time value by the specified amount using the specified 
field.
+        *
+        * <p>
+        * This method creates a new GranularZonedDateTime by adding the 
specified amount to the
+        * specified field. The precision of the returned object remains the 
same as this object.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      <jc>// Create a datetime with hour precision</jc>
+        *      GranularZonedDateTime <jv>gdt</jv> = 
GranularZonedDateTime.<jsm>of</jsm>(<js>"2011-01-15T12Z"</js>);
+        *      <jc>// Roll forward by 2 hours</jc>
+        *      <jv>gdt</jv> = 
<jv>gdt</jv>.<jsm>roll</jsm>(<jv>ChronoField</jv>.<jf>HOUR_OF_DAY</jf>, 2);
+        *      <jc>// Result: 2011-01-15T14:00:00Z (precision still 
HOUR_OF_DAY)</jc>
+        * </p>
+        *
+        * <p>
+        * If the field cannot be converted to a ChronoUnit (e.g., unsupported 
field), this method
+        * returns this object unchanged.
+        *
+        * @param field The field to roll by (e.g., {@link ChronoField#YEAR}, 
{@link ChronoField#HOUR_OF_DAY}).
+        * @param amount The amount to roll by. Positive values roll forward, 
negative values roll backward.
+        * @return A new GranularZonedDateTime with the rolled value, or this 
object if the field is unsupported.
+        */
+       public GranularZonedDateTime roll(ChronoField field, int amount) {
+               ChronoUnit unit = toChronoUnit(field);
+               if (nn(unit)) {
+                       ZonedDateTime newZdt = zdt.plus(amount, unit);
+                       return new GranularZonedDateTime(newZdt, precision);
+               }
+               return this;
+       }
+
+       /**
+        * Rolls this time value by the specified amount using the current 
precision.
+        *
+        * <p>
+        * This is a convenience method that calls {@link #roll(ChronoField, 
int)} using this
+        * object's precision field.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      <jc>// Create a datetime with year precision</jc>
+        *      GranularZonedDateTime <jv>gdt</jv> = 
GranularZonedDateTime.<jsm>of</jsm>(<js>"2011"</js>);
+        *      <jc>// Roll forward by 1 (using YEAR precision)</jc>
+        *      <jv>gdt</jv> = <jv>gdt</jv>.<jsm>roll</jsm>(1);
+        *      <jc>// Result: 2012-01-01T00:00:00 (precision still YEAR)</jc>
+        * </p>
+        *
+        * @param amount The amount to roll by. Positive values roll forward, 
negative values roll backward.
+        * @return A new GranularZonedDateTime with the rolled value.
+        */
+       public GranularZonedDateTime roll(int amount) {
+               return roll(precision, amount);
+       }
 }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
index 0dc316ba1e..8255eb64b3 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
@@ -1550,13 +1550,13 @@ public class BeanSession extends ContextSession {
                                                return (T)c2;
                                        }
                                }
-                               return 
(T)GregorianCalendar.from(GranularZonedDateTime.parse(value.toString()).getZonedDateTime());
+                               return 
(T)GregorianCalendar.from(GranularZonedDateTime.of(value.toString()).getZonedDateTime());
                        }
 
                        if (to.isDate() && to.getInnerClass() == Date.class) {
                                if (from.isCalendar())
                                        return (T)((Calendar)value).getTime();
-                               return 
(T)GregorianCalendar.from(GranularZonedDateTime.parse(value.toString()).getZonedDateTime()).getTime();
+                               return 
(T)GregorianCalendar.from(GranularZonedDateTime.of(value.toString()).getZonedDateTime()).getTime();
                        }
 
                        if (to.hasMutaterFrom(from))
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/oapi/OpenApiParserSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/oapi/OpenApiParserSession.java
index 174fef0815..f7dec2af82 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/oapi/OpenApiParserSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/oapi/OpenApiParserSession.java
@@ -281,7 +281,7 @@ public class OpenApiParserSession extends UonParserSession {
                                                return toType(base64Decode(in), 
type);
                                        if (f == DATE || f == DATE_TIME) {
                                                var in2 = in;
-                                               return toType(opt(in).filter(x1 
-> ! isBlank(x1)).map(x -> 
GranularZonedDateTime.parse(in2).getZonedDateTime()).map(GregorianCalendar::from).orElse(null),
 type);
+                                               return toType(opt(in).filter(x1 
-> ! isBlank(x1)).map(x -> 
GranularZonedDateTime.of(in2).getZonedDateTime()).map(GregorianCalendar::from).orElse(null),
 type);
                                        }
                                        if (f == BINARY)
                                                return toType(fromHex(in), 
type);
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/TimeMatcherFactory.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/TimeMatcherFactory.java
index 76a57c656f..9076689656 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/TimeMatcherFactory.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/TimeMatcherFactory.java
@@ -329,7 +329,7 @@ public class TimeMatcherFactory extends MatcherFactory {
                ZonedDateTime end;
 
                public TimestampRange(Equality eq, String singleDate) {
-                       var singleDate1 = 
GranularZonedDateTime.parse(singleDate);
+                       var singleDate1 = GranularZonedDateTime.of(singleDate);
                        if (eq == Equality.GT) {
                                this.start = 
singleDate1.roll(1).roll(MILLI_OF_SECOND, -1).getZonedDateTime();
                                this.end = 
Instant.ofEpochMilli(Long.MAX_VALUE).atZone(ZoneId.systemDefault());
@@ -349,8 +349,8 @@ public class TimeMatcherFactory extends MatcherFactory {
                }
 
                public TimestampRange(String start, String end) {
-                       var start1 = GranularZonedDateTime.parse(start);
-                       var end1 = GranularZonedDateTime.parse(end);
+                       var start1 = GranularZonedDateTime.of(start);
+                       var end1 = GranularZonedDateTime.of(end);
                        this.start = start1.copy().roll(MILLI_OF_SECOND, 
-1).getZonedDateTime();
                        this.end = end1.roll(1).getZonedDateTime();
                }
diff --git 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
 
b/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
index 6907d3b09b..69218a7f4f 100644
--- 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
+++ 
b/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
@@ -227,8 +227,8 @@ public class LogsResource extends BasicRestServlet {
 
                var f = getFile(path);
 
-               var startDate = opt(start).filter(x1 -> ! isBlank(x1)).map(x2 
-> 
GranularZonedDateTime.parse(start).getZonedDateTime()).map(GregorianCalendar::from).map(x
 -> x.getTime()).orElse(null);
-               var endDate = opt(end).filter(x11 -> ! isBlank(x11)).map(x4 -> 
GranularZonedDateTime.parse(end).getZonedDateTime()).map(GregorianCalendar::from).map(x3
 -> x3.getTime()).orElse(null);
+               var startDate = opt(start).filter(x1 -> ! isBlank(x1)).map(x2 
-> 
GranularZonedDateTime.of(start).getZonedDateTime()).map(GregorianCalendar::from).map(x
 -> x.getTime()).orElse(null);
+               var endDate = opt(end).filter(x11 -> ! isBlank(x11)).map(x4 -> 
GranularZonedDateTime.of(end).getZonedDateTime()).map(GregorianCalendar::from).map(x3
 -> x3.getTime()).orElse(null);
 
                if (! highlight) {
                        var o = getReader(f, startDate, endDate, thread, 
loggers, severity);
@@ -297,8 +297,8 @@ public class LogsResource extends BasicRestServlet {
                var f = getFile(path);
                req.setAttribute("fullPath", f.getAbsolutePath());
 
-               var startDate = opt(start).filter(x1 -> ! isBlank(x1)).map(x2 
-> 
GranularZonedDateTime.parse(start).getZonedDateTime()).map(GregorianCalendar::from).map(x
 -> x.getTime()).orElse(null);
-               var endDate = opt(end).filter(x11 -> ! isBlank(x11)).map(x4 -> 
GranularZonedDateTime.parse(end).getZonedDateTime()).map(GregorianCalendar::from).map(x3
 -> x3.getTime()).orElse(null);
+               var startDate = opt(start).filter(x1 -> ! isBlank(x1)).map(x2 
-> 
GranularZonedDateTime.of(start).getZonedDateTime()).map(GregorianCalendar::from).map(x
 -> x.getTime()).orElse(null);
+               var endDate = opt(end).filter(x11 -> ! isBlank(x11)).map(x4 -> 
GranularZonedDateTime.of(end).getZonedDateTime()).map(GregorianCalendar::from).map(x3
 -> x3.getTime()).orElse(null);
 
                return getLogParser(f, startDate, endDate, thread, loggers, 
severity);
        }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeans_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeans_Test.java
index 5cb0e03d8a..aceb0cf70c 100755
--- 
a/juneau-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeans_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeans_Test.java
@@ -554,7 +554,7 @@ class RoundTripTransformBeans_Test extends TestBase {
 
                public static F1 create() {
                        var x = new F1();
-                       x.setC(opt("2018-12-12T05:12:00Z").filter(x2 -> ! 
isBlank(x2)).map(x1 -> 
GranularZonedDateTime.parse("2018-12-12T05:12:00Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null));
+                       x.setC(opt("2018-12-12T05:12:00Z").filter(x2 -> ! 
isBlank(x2)).map(x1 -> 
GranularZonedDateTime.of("2018-12-12T05:12:00Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null));
                        return x;
                }
        }
@@ -589,7 +589,7 @@ class RoundTripTransformBeans_Test extends TestBase {
 
                public static F1c create() {
                        var x = new F1c();
-                       x.setC(opt("2018-12-12T05:12:00Z").filter(x2 -> ! 
isBlank(x2)).map(x1 -> 
GranularZonedDateTime.parse("2018-12-12T05:12:00Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null));
+                       x.setC(opt("2018-12-12T05:12:00Z").filter(x2 -> ! 
isBlank(x2)).map(x1 -> 
GranularZonedDateTime.of("2018-12-12T05:12:00Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null));
                        return x;
                }
        }
@@ -623,7 +623,7 @@ class RoundTripTransformBeans_Test extends TestBase {
 
                public static F2 create() {
                        var x = new F2();
-                       x.setC(opt("2018-12-12T05:12:00Z").filter(x2 -> ! 
isBlank(x2)).map(x1 -> 
GranularZonedDateTime.parse("2018-12-12T05:12:00Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null));
+                       x.setC(opt("2018-12-12T05:12:00Z").filter(x2 -> ! 
isBlank(x2)).map(x1 -> 
GranularZonedDateTime.of("2018-12-12T05:12:00Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null));
                        return x;
                }
        }
@@ -661,7 +661,7 @@ class RoundTripTransformBeans_Test extends TestBase {
 
                public static F2c create() {
                        var x = new F2c();
-                       x.setC(opt("2018-12-12T05:12:00Z").filter(x2 -> ! 
isBlank(x2)).map(x1 -> 
GranularZonedDateTime.parse("2018-12-12T05:12:00Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null));
+                       x.setC(opt("2018-12-12T05:12:00Z").filter(x2 -> ! 
isBlank(x2)).map(x1 -> 
GranularZonedDateTime.of("2018-12-12T05:12:00Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null));
                        return x;
                }
        }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/utils/DateUtils_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/utils/DateUtils_Test.java
index c8460cf7ae..e6752df23d 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/utils/DateUtils_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/utils/DateUtils_Test.java
@@ -16,20 +16,10 @@
  */
 package org.apache.juneau.commons.utils;
 
-import static java.time.temporal.ChronoField.*;
-import static java.util.Calendar.*;
-import static org.apache.juneau.commons.utils.StringUtils.*;
-import static org.apache.juneau.commons.utils.Utils.*;
 import static org.junit.jupiter.api.Assertions.*;
 
-import java.time.*;
-import java.time.temporal.*;
-import java.util.*;
-
 import org.apache.juneau.*;
 import org.junit.jupiter.api.*;
-import org.junit.jupiter.params.*;
-import org.junit.jupiter.params.provider.*;
 
 class DateUtils_Test extends TestBase {
 
@@ -43,322 +33,4 @@ class DateUtils_Test extends TestBase {
                var instance = new DateUtils();
                assertNotNull(instance);
        }
-
-       
//-----------------------------------------------------------------------------------------------------------------
-       // Test getPrecisionFromString method (now in GranularZonedDateTime)
-       
//-----------------------------------------------------------------------------------------------------------------
-       // Note: getPrecisionFromString has been moved to GranularZonedDateTime 
as a private method.
-       // Tests for this functionality are now in GranularZonedDateTime_Test 
via the parse() method.
-
-       
//-----------------------------------------------------------------------------------------------------------------
-       // toChronoUnit(ChronoField) tests (now in GranularZonedDateTime)
-       
//-----------------------------------------------------------------------------------------------------------------
-       // Note: toChronoUnit has been moved to GranularZonedDateTime as a 
private method.
-       // Tests for this functionality are now in GranularZonedDateTime_Test 
via the roll() method.
-
-       
//-----------------------------------------------------------------------------------------------------------------
-       // Round-trip conversion tests
-       
//-----------------------------------------------------------------------------------------------------------------
-
-       static class G_roundTripConversions {
-
-               private static final Input[] INPUT = {
-                       /* 01 */ input(1, ChronoField.YEAR),
-                       /* 02 */ input(2, MONTH_OF_YEAR),
-                       /* 03 */ input(3, ChronoField.DAY_OF_MONTH),
-                       /* 04 */ input(4, ChronoField.HOUR_OF_DAY),
-                       /* 05 */ input(5, MINUTE_OF_HOUR),
-                       /* 06 */ input(6, SECOND_OF_MINUTE),
-                       /* 07 */ input(7, MILLI_OF_SECOND)
-               };
-
-               private static Input input(int index, ChronoField field) {
-                       return new Input(index, field);
-               }
-
-               private static class Input {
-                       final int index;
-                       final ChronoField field;
-
-                       public Input(int index, ChronoField field) {
-                               this.index = index;
-                               this.field = field;
-                       }
-               }
-
-               static Input[] input() {
-                       return INPUT;
-               }
-
-       }
-
-       
//-----------------------------------------------------------------------------------------------------------------
-       // Helper method for converting Calendar to ISO8601 string
-       
//-----------------------------------------------------------------------------------------------------------------
-
-       private static String toIso8601(Calendar c) {
-               var sdf = new 
java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
-               sdf.setTimeZone(c.getTimeZone());
-               return sdf.format(c.getTime());
-       }
-
-       
//-----------------------------------------------------------------------------------------------------------------
-       // fromIso8601Calendar(String) tests
-       
//-----------------------------------------------------------------------------------------------------------------
-
-       static class J_fromIso8601Calendar {
-
-               private static final Input[] INPUT = {
-                       /* 01 */ input(1, "2024-01-15T14:30:45Z", "UTC", 2024, 
JANUARY, 15, 14, 30, 45),
-                       /* 02 */ input(2, "2024-01-15T14:30:45-05:00", 
"GMT-05:00", 2024, JANUARY, 15, 14, 30, 45),
-                       /* 03 */ input(3, "2024-01-15T14:30:45+09:00", 
"GMT+09:00", 2024, JANUARY, 15, 14, 30, 45),
-                       /* 04 */ input(4, "2024-01-15", "System", 2024, 
JANUARY, 15, 0, 0, 0),
-                       /* 05 */ input(5, "2024-01-15T14:30", "System", 2024, 
JANUARY, 15, 14, 30, 0),
-                       /* 06 */ input(6, "2024-01-15T14:30:45.123Z", "UTC", 
2024, JANUARY, 15, 14, 30, 45),
-                       /* 07 */ input(7, "2024-07-15T14:30:45-04:00", 
"GMT-04:00", 2024, JULY, 15, 14, 30, 45), // DST
-                       /* 08 */ input(8, "2024-07-15T14:30:45-07:00", 
"GMT-07:00", 2024, JULY, 15, 14, 30, 45), // DST
-                       /* 09 */ input(9, "2024-02-29T12:00:00Z", "UTC", 2024, 
FEBRUARY, 29, 12, 0, 0), // Leap year
-                       /* 10 */ input(10, "2024-12-31T23:59:59Z", "UTC", 2024, 
DECEMBER, 31, 23, 59, 59), // End of year
-                       /* 11 */ input(11, "2024-01-01T00:00:00Z", "UTC", 2024, 
JANUARY, 1, 0, 0, 0), // Start of year
-                       /* 12 */ input(12, "2024-01-15T00:00:00Z", "UTC", 2024, 
JANUARY, 15, 0, 0, 0), // Midnight
-                       /* 13 */ input(13, "2024-01-15T23:59:59Z", "UTC", 2024, 
JANUARY, 15, 23, 59, 59), // End of day
-                       /* 14 */ input(14, "2024-01-15T12:00:00+05:30", 
"GMT+05:30", 2024, JANUARY, 15, 12, 0, 0), // Custom offset
-                       /* 15 */ input(15, "2024-01-15T12:00:00-05:30", 
"GMT-05:30", 2024, JANUARY, 15, 12, 0, 0) // Custom offset
-               };
-
-               private static Input input(int index, String iso8601String, 
String expectedTimezone, int year, int month, int day, int hour, int minute, 
int second) {
-                       return new Input(index, iso8601String, 
expectedTimezone, year, month, day, hour, minute, second);
-               }
-
-               private static class Input {
-                       final int index;
-                       final String iso8601String;
-                       final String expectedTimezone;
-                       final int year;
-                       final int month;
-                       final int day;
-                       final int hour;
-                       final int minute;
-                       final int second;
-
-                       public Input(int index, String iso8601String, String 
expectedTimezone, int year, int month, int day, int hour, int minute, int 
second) {
-                               this.index = index;
-                               this.iso8601String = iso8601String;
-                               this.expectedTimezone = expectedTimezone;
-                               this.year = year;
-                               this.month = month;
-                               this.day = day;
-                               this.hour = hour;
-                               this.minute = minute;
-                               this.second = second;
-                       }
-               }
-
-               static Input[] input() {
-                       return INPUT;
-               }
-
-               @ParameterizedTest
-               @MethodSource("input")
-               void j01_fromIso8601Calendar(Input input) {
-                       // Parse the ISO8601 string
-                       String s = input.iso8601String;
-                       Calendar result = opt(s).filter(x1 -> ! 
isBlank(x1)).map(x -> 
GranularZonedDateTime.parse(s).getZonedDateTime()).map(GregorianCalendar::from).orElse(null);
-
-                       // Verify the result is not null
-                       assertNotNull(result, "Test " + input.index + ": Result 
should not be null");
-
-                       // Verify date components
-                       assertEquals(input.year, result.get(Calendar.YEAR), 
"Test " + input.index + ": Year should match");
-                       assertEquals(input.month, result.get(Calendar.MONTH), 
"Test " + input.index + ": Month should match");
-                       assertEquals(input.day, 
result.get(Calendar.DAY_OF_MONTH), "Test " + input.index + ": Day should 
match");
-                       assertEquals(input.hour, 
result.get(Calendar.HOUR_OF_DAY), "Test " + input.index + ": Hour should 
match");
-                       assertEquals(input.minute, result.get(Calendar.MINUTE), 
"Test " + input.index + ": Minute should match");
-                       assertEquals(input.second, result.get(Calendar.SECOND), 
"Test " + input.index + ": Second should match");
-
-                       // Verify timezone (for non-system timezones)
-                       if (!"System".equals(input.expectedTimezone)) {
-                               var expectedTz = 
TimeZone.getTimeZone(input.expectedTimezone);
-                               assertEquals(expectedTz.getID(), 
result.getTimeZone().getID(), "Test " + input.index + ": Timezone should 
match");
-                       }
-               }
-
-       }
-
-       
//-----------------------------------------------------------------------------------------------------------------
-       // fromIso8601(String) tests
-       
//-----------------------------------------------------------------------------------------------------------------
-
-       static class K_fromIso8601 {
-
-               private static final Input[] INPUT = {
-                       /* 01 */ input(1, "2024-01-15T14:30:45Z", "Z", 2024, 1, 
15, 14, 30, 45),
-                       /* 02 */ input(2, "2024-01-15T14:30:45-05:00", 
"-05:00", 2024, 1, 15, 14, 30, 45),
-                       /* 03 */ input(3, "2024-01-15T14:30:45+09:00", 
"+09:00", 2024, 1, 15, 14, 30, 45),
-                       /* 04 */ input(4, "2024-01-15", "System", 2024, 1, 15, 
0, 0, 0),
-                       /* 05 */ input(5, "2024-01-15T14:30", "System", 2024, 
1, 15, 14, 30, 0),
-                       /* 06 */ input(6, "2024-01-15T14:30:45.123Z", "Z", 
2024, 1, 15, 14, 30, 45),
-                       /* 07 */ input(7, "2024-07-15T14:30:45-04:00", 
"-04:00", 2024, 7, 15, 14, 30, 45), // DST
-                       /* 08 */ input(8, "2024-07-15T14:30:45-07:00", 
"-07:00", 2024, 7, 15, 14, 30, 45), // DST
-                       /* 09 */ input(9, "2024-02-29T12:00:00Z", "Z", 2024, 2, 
29, 12, 0, 0), // Leap year
-                       /* 10 */ input(10, "2024-12-31T23:59:59Z", "Z", 2024, 
12, 31, 23, 59, 59), // End of year
-                       /* 11 */ input(11, "2024-01-01T00:00:00Z", "Z", 2024, 
1, 1, 0, 0, 0), // Start of year
-                       /* 12 */ input(12, "2024-01-15T00:00:00Z", "Z", 2024, 
1, 15, 0, 0, 0), // Midnight
-                       /* 13 */ input(13, "2024-01-15T23:59:59Z", "Z", 2024, 
1, 15, 23, 59, 59), // End of day
-                       /* 14 */ input(14, "2024-01-15T12:00:00+05:30", 
"+05:30", 2024, 1, 15, 12, 0, 0), // Custom offset
-                       /* 15 */ input(15, "2024-01-15T12:00:00-05:30", 
"-05:30", 2024, 1, 15, 12, 0, 0) // Custom offset
-               };
-
-               private static Input input(int index, String iso8601String, 
String expectedTimezone, int year, int month, int day, int hour, int minute, 
int second) {
-                       return new Input(index, iso8601String, 
expectedTimezone, year, month, day, hour, minute, second);
-               }
-
-               private static class Input {
-                       final int index;
-                       final String iso8601String;
-                       final String expectedTimezone;
-                       final int year;
-                       final int month;
-                       final int day;
-                       final int hour;
-                       final int minute;
-                       final int second;
-
-                       public Input(int index, String iso8601String, String 
expectedTimezone, int year, int month, int day, int hour, int minute, int 
second) {
-                               this.index = index;
-                               this.iso8601String = iso8601String;
-                               this.expectedTimezone = expectedTimezone;
-                               this.year = year;
-                               this.month = month;
-                               this.day = day;
-                               this.hour = hour;
-                               this.minute = minute;
-                               this.second = second;
-                       }
-               }
-
-               static Input[] input() {
-                       return INPUT;
-               }
-
-               @ParameterizedTest
-               @MethodSource("input")
-               void k01_fromIso8601(Input input) {
-                       // Parse the ISO8601 string
-                       ZonedDateTime result = 
GranularZonedDateTime.parse(input.iso8601String).getZonedDateTime();
-
-                       // Verify the result is not null
-                       assertNotNull(result, "Test " + input.index + ": Result 
should not be null");
-
-                       // Verify date components
-                       assertEquals(input.year, result.getYear(), "Test " + 
input.index + ": Year should match");
-                       assertEquals(input.month, result.getMonthValue(), "Test 
" + input.index + ": Month should match");
-                       assertEquals(input.day, result.getDayOfMonth(), "Test " 
+ input.index + ": Day should match");
-                       assertEquals(input.hour, result.getHour(), "Test " + 
input.index + ": Hour should match");
-                       assertEquals(input.minute, result.getMinute(), "Test " 
+ input.index + ": Minute should match");
-                       assertEquals(input.second, result.getSecond(), "Test " 
+ input.index + ": Second should match");
-
-                       // Verify timezone (for non-system timezones)
-                       if (!"System".equals(input.expectedTimezone)) {
-                               var expectedZone = 
ZoneId.of(input.expectedTimezone);
-                               assertEquals(expectedZone, result.getZone(), 
"Test " + input.index + ": Timezone should match");
-                       }
-               }
-
-               @ParameterizedTest
-               @MethodSource("input")
-               void k02_fromIso8601_immutability(Input input) {
-                       // Parse the ISO8601 string
-                       ZonedDateTime result = 
GranularZonedDateTime.parse(input.iso8601String).getZonedDateTime();
-                       assertNotNull(result, "Test " + input.index + ": Result 
should not be null");
-
-                       // Verify immutability - operations should return new 
instances
-                       ZonedDateTime plusOneDay = result.plusDays(1);
-                       assertNotSame(result, plusOneDay, "Test " + input.index 
+ ": Plus operation should return new instance");
-                       assertNotEquals(result, plusOneDay, "Test " + 
input.index + ": Plus operation should change the value");
-
-                       ZonedDateTime minusOneDay = result.minusDays(1);
-                       assertNotSame(result, minusOneDay, "Test " + 
input.index + ": Minus operation should return new instance");
-                       assertNotEquals(result, minusOneDay, "Test " + 
input.index + ": Minus operation should change the value");
-               }
-       }
-
-       
//-----------------------------------------------------------------------------------------------------------------
-       // fromIso8601Calendar and fromIso8601 edge cases and error handling
-       
//-----------------------------------------------------------------------------------------------------------------
-
-       static class L_fromIso8601_edgeCases {
-
-               @Test
-               void l04_invalidFormat() {
-                       // These should throw DateTimeParseException
-                       assertThrows(Exception.class, () -> {
-                               opt("invalid-date").filter(x1 -> ! 
isBlank(x1)).map(x -> 
GranularZonedDateTime.parse("invalid-date").getZonedDateTime()).map(GregorianCalendar::from).orElse(null);
-                       });
-                       assertThrows(Exception.class, () -> {
-                               
GranularZonedDateTime.parse("invalid-date").getZonedDateTime();
-                       });
-               }
-
-               @Test
-               void l05_minimumDate() {
-                       Calendar cal = opt("0001-01-01T00:00:00Z").filter(x1 -> 
! isBlank(x1)).map(x -> 
GranularZonedDateTime.parse("0001-01-01T00:00:00Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null);
-                       assertNotNull(cal);
-                       assertEquals(1, cal.get(Calendar.YEAR));
-                       assertEquals(Calendar.JANUARY, cal.get(Calendar.MONTH));
-                       assertEquals(1, cal.get(Calendar.DAY_OF_MONTH));
-
-                       ZonedDateTime zdt = 
GranularZonedDateTime.parse("0001-01-01T00:00:00Z").getZonedDateTime();
-                       assertNotNull(zdt);
-                       assertEquals(1, zdt.getYear());
-                       assertEquals(1, zdt.getMonthValue());
-                       assertEquals(1, zdt.getDayOfMonth());
-               }
-
-               @Test
-               void l06_maximumDate() {
-                       Calendar cal = opt("9999-12-31T23:59:59Z").filter(x1 -> 
! isBlank(x1)).map(x -> 
GranularZonedDateTime.parse("9999-12-31T23:59:59Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null);
-                       assertNotNull(cal);
-                       assertEquals(9999, cal.get(Calendar.YEAR));
-                       assertEquals(Calendar.DECEMBER, 
cal.get(Calendar.MONTH));
-                       assertEquals(31, cal.get(Calendar.DAY_OF_MONTH));
-
-                       ZonedDateTime zdt = 
GranularZonedDateTime.parse("9999-12-31T23:59:59Z").getZonedDateTime();
-                       assertNotNull(zdt);
-                       assertEquals(9999, zdt.getYear());
-                       assertEquals(12, zdt.getMonthValue());
-                       assertEquals(31, zdt.getDayOfMonth());
-               }
-
-               @Test
-               void l07_leapYear() {
-                       Calendar cal = opt("2024-02-29T12:00:00Z").filter(x1 -> 
! isBlank(x1)).map(x -> 
GranularZonedDateTime.parse("2024-02-29T12:00:00Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null);
-                       assertNotNull(cal);
-                       assertEquals(2024, cal.get(Calendar.YEAR));
-                       assertEquals(Calendar.FEBRUARY, 
cal.get(Calendar.MONTH));
-                       assertEquals(29, cal.get(Calendar.DAY_OF_MONTH));
-
-                       ZonedDateTime zdt = 
GranularZonedDateTime.parse("2024-02-29T12:00:00Z").getZonedDateTime();
-                       assertNotNull(zdt);
-                       assertEquals(2024, zdt.getYear());
-                       assertEquals(2, zdt.getMonthValue());
-                       assertEquals(29, zdt.getDayOfMonth());
-               }
-
-               @Test
-               void l08_dstTransition() {
-                       // Test DST transition in America/New_York (Spring 
forward)
-                       Calendar cal = 
opt("2024-03-10T02:30:00-05:00").filter(x1 -> ! isBlank(x1)).map(x -> 
GranularZonedDateTime.parse("2024-03-10T02:30:00-05:00").getZonedDateTime()).map(GregorianCalendar::from).orElse(null);
-                       assertNotNull(cal);
-                       assertEquals(2024, cal.get(Calendar.YEAR));
-                       assertEquals(Calendar.MARCH, cal.get(Calendar.MONTH));
-                       assertEquals(10, cal.get(Calendar.DAY_OF_MONTH));
-
-                       ZonedDateTime zdt = 
GranularZonedDateTime.parse("2024-03-10T02:30:00-05:00").getZonedDateTime();
-                       assertNotNull(zdt);
-                       assertEquals(2024, zdt.getYear());
-                       assertEquals(3, zdt.getMonthValue());
-                       assertEquals(10, zdt.getDayOfMonth());
-               }
-       }
 }
\ No newline at end of file
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/utils/GranularZonedDateTime_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/utils/GranularZonedDateTime_Test.java
index c77d9a0f47..f4668e2178 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/utils/GranularZonedDateTime_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/utils/GranularZonedDateTime_Test.java
@@ -31,36 +31,6 @@ import org.junit.jupiter.api.*;
  */
 class GranularZonedDateTime_Test extends TestBase {
 
-       
//====================================================================================================
-       // Constructor(Date, ChronoField) tests
-       
//====================================================================================================
-
-       @Test
-       void a01_constructorWithDate() {
-               var date = new Date(1000000000L); // Fixed timestamp
-               var gdt = new GranularZonedDateTime(date, ChronoField.YEAR);
-               assertNotNull(gdt.zdt);
-               assertEquals(ChronoField.YEAR, gdt.precision);
-               assertEquals(date.toInstant().atZone(ZoneId.systemDefault()), 
gdt.zdt);
-       }
-
-       @Test
-       void a02_constructorWithDate_differentPrecisions() {
-               var date = new Date(1000000000L);
-               var gdt1 = new GranularZonedDateTime(date, 
ChronoField.MONTH_OF_YEAR);
-               var gdt2 = new GranularZonedDateTime(date, 
ChronoField.DAY_OF_MONTH);
-               assertEquals(ChronoField.MONTH_OF_YEAR, gdt1.precision);
-               assertEquals(ChronoField.DAY_OF_MONTH, gdt2.precision);
-               assertEquals(gdt1.zdt, gdt2.zdt);
-       }
-
-       @Test
-       void a03_constructorWithDate_usesSystemDefaultZone() {
-               var date = new Date(1000000000L);
-               var gdt = new GranularZonedDateTime(date, ChronoField.YEAR);
-               assertEquals(ZoneId.systemDefault(), gdt.zdt.getZone());
-       }
-
        
//====================================================================================================
        // Constructor(ZonedDateTime, ChronoField) tests
        
//====================================================================================================
@@ -328,20 +298,20 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        
//====================================================================================================
-       // parse(String) tests
+       // of(String) tests
        
//====================================================================================================
 
        @Test
-       void g01_parse_yearOnly() {
-               var gdt = GranularZonedDateTime.parse("2011");
+       void g01_of_yearOnly() {
+               var gdt = GranularZonedDateTime.of("2011");
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(ChronoField.YEAR, gdt.precision);
        }
 
        @Test
-       void g02_parse_yearMonth() {
-               var gdt = GranularZonedDateTime.parse("2011-01");
+       void g02_of_yearMonth() {
+               var gdt = GranularZonedDateTime.of("2011-01");
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(1, gdt.zdt.getMonthValue());
@@ -349,8 +319,8 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void g03_parse_date() {
-               var gdt = GranularZonedDateTime.parse("2011-01-15");
+       void g03_of_date() {
+               var gdt = GranularZonedDateTime.of("2011-01-15");
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(1, gdt.zdt.getMonthValue());
@@ -359,16 +329,16 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void g04_parse_dateTime_hour() {
-               var gdt = GranularZonedDateTime.parse("2011-01-15T12Z");
+       void g04_of_dateTime_hour() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12Z");
                assertNotNull(gdt);
                assertEquals(12, gdt.zdt.getHour());
                assertEquals(ChronoField.HOUR_OF_DAY, gdt.precision);
        }
 
        @Test
-       void g05_parse_dateTime_minute() {
-               var gdt = GranularZonedDateTime.parse("2011-01-15T12:30Z");
+       void g05_of_dateTime_minute() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30Z");
                assertNotNull(gdt);
                assertEquals(12, gdt.zdt.getHour());
                assertEquals(30, gdt.zdt.getMinute());
@@ -376,8 +346,8 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void g06_parse_dateTime_second() {
-               var gdt = GranularZonedDateTime.parse("2011-01-15T12:30:45Z");
+       void g06_of_dateTime_second() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45Z");
                assertNotNull(gdt);
                assertEquals(12, gdt.zdt.getHour());
                assertEquals(30, gdt.zdt.getMinute());
@@ -386,8 +356,8 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void g07_parse_dateTime_millisecond() {
-               var gdt = 
GranularZonedDateTime.parse("2011-01-15T12:30:45.123Z");
+       void g07_of_dateTime_millisecond() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45.123Z");
                assertNotNull(gdt);
                assertEquals(12, gdt.zdt.getHour());
                assertEquals(30, gdt.zdt.getMinute());
@@ -397,26 +367,26 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void g08_parse_withOffset() {
-               var gdt = 
GranularZonedDateTime.parse("2011-01-15T12:30:45-05:00");
+       void g08_of_withOffset() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45-05:00");
                assertNotNull(gdt);
                assertEquals(12, gdt.zdt.getHour());
                assertEquals(ZoneOffset.of("-05:00"), gdt.zdt.getOffset());
        }
 
        @Test
-       void g09_parse_withPositiveOffset() {
-               var gdt = 
GranularZonedDateTime.parse("2011-01-15T12:30:45+09:00");
+       void g09_of_withPositiveOffset() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45+09:00");
                assertNotNull(gdt);
                assertEquals(ZoneOffset.of("+09:00"), gdt.zdt.getOffset());
        }
 
        @Test
-       void g10_parse_invalidString() {
+       void g10_of_invalidString() {
                // fromIso8601 throws DateTimeParseException for invalid input
-               // The exception may be DateTimeParseException (from 
fromIso8601) or BasicRuntimeException (from parse)
+               // The exception may be DateTimeParseException (from 
fromIso8601) or BasicRuntimeException (from of)
                var e = assertThrows(RuntimeException.class, () -> {
-                       GranularZonedDateTime.parse("invalid-date");
+                       GranularZonedDateTime.of("invalid-date");
                });
                // Verify that an exception was thrown (the exact message 
format may vary)
                assertNotNull(e);
@@ -424,14 +394,14 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void g11_parse_null() {
-               assertThrows(IllegalArgumentException.class, () -> 
GranularZonedDateTime.parse(null));
+       void g11_of_null() {
+               assertThrows(IllegalArgumentException.class, () -> 
GranularZonedDateTime.of(null));
        }
 
        @Test
-       void g12_parse_emptyString() {
+       void g12_of_emptyString() {
                assertThrowsWithMessage(RuntimeException.class, "Invalid 
ISO8601 timestamp", () -> {
-                       GranularZonedDateTime.parse("");
+                       GranularZonedDateTime.of("");
                });
        }
 
@@ -440,8 +410,8 @@ class GranularZonedDateTime_Test extends TestBase {
        
//====================================================================================================
 
        @Test
-       void h01_rollAndParse() {
-               var gdt1 = GranularZonedDateTime.parse("2011");
+       void h01_rollAndof() {
+               var gdt1 = GranularZonedDateTime.of("2011");
                var gdt2 = gdt1.roll(1);
                assertEquals(2012, gdt2.zdt.getYear());
                assertEquals(ChronoField.YEAR, gdt2.precision);
@@ -449,7 +419,7 @@ class GranularZonedDateTime_Test extends TestBase {
 
        @Test
        void h02_rollMultipleTimes() {
-               var gdt = GranularZonedDateTime.parse("2011-01-15");
+               var gdt = GranularZonedDateTime.of("2011-01-15");
                var rolled1 = gdt.roll(1);
                var rolled2 = rolled1.roll(1);
                assertEquals(17, rolled2.zdt.getDayOfMonth());
@@ -457,7 +427,7 @@ class GranularZonedDateTime_Test extends TestBase {
 
        @Test
        void h03_rollWithDifferentField() {
-               var gdt = GranularZonedDateTime.parse("2011-01-15T12:30Z");
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30Z");
                // Roll by hours even though precision is minutes
                var rolled = gdt.roll(ChronoField.HOUR_OF_DAY, 2);
                assertEquals(14, rolled.zdt.getHour());
@@ -467,7 +437,7 @@ class GranularZonedDateTime_Test extends TestBase {
 
        @Test
        void h04_copyAndRoll() {
-               var gdt1 = GranularZonedDateTime.parse("2011-01-15");
+               var gdt1 = GranularZonedDateTime.of("2011-01-15");
                var gdt2 = gdt1.copy();
                var rolled = gdt2.roll(1);
                // Original should be unchanged
@@ -476,24 +446,24 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        
//====================================================================================================
-       // parse2(String) tests
+       // of(String) tests
        
//====================================================================================================
 
        @Test
-       void i01_parse2_null() {
-               // parse2(String) overload throws IllegalArgumentException when 
seg is null
+       void i01_of_null() {
+               // of(String) overload throws IllegalArgumentException when seg 
is null
                assertThrows(IllegalArgumentException.class, () -> {
-                       GranularZonedDateTime.parse2((String)null);
+                       GranularZonedDateTime.of((String)null);
                });
-               // parse2(String, ZoneId) also throws when seg is null
+               // of(String, ZoneId) also throws when seg is null
                assertThrows(IllegalArgumentException.class, () -> {
-                       GranularZonedDateTime.parse2((String)null, 
(ZoneId)null);
+                       GranularZonedDateTime.of((String)null, (ZoneId)null);
                });
        }
 
        @Test
-       void i02_parse2_yearOnly() {
-               var gdt = GranularZonedDateTime.parse2("2011", null);
+       void i02_of_yearOnly() {
+               var gdt = GranularZonedDateTime.of("2011", null);
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(1, gdt.zdt.getMonthValue());
@@ -503,8 +473,8 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i03_parse2_yearMonth() {
-               var gdt = GranularZonedDateTime.parse2("2011-01", null);
+       void i03_of_yearMonth() {
+               var gdt = GranularZonedDateTime.of("2011-01", null);
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(1, gdt.zdt.getMonthValue());
@@ -513,8 +483,8 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i04_parse2_date() {
-               var gdt = GranularZonedDateTime.parse2("2011-01-15", null);
+       void i04_of_date() {
+               var gdt = GranularZonedDateTime.of("2011-01-15", null);
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(1, gdt.zdt.getMonthValue());
@@ -523,8 +493,8 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i05_parse2_dateTime_hour() {
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12", null);
+       void i05_of_dateTime_hour() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12", null);
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(1, gdt.zdt.getMonthValue());
@@ -535,8 +505,8 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i06_parse2_dateTime_minute() {
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12:30", 
null);
+       void i06_of_dateTime_minute() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30", null);
                assertNotNull(gdt);
                assertEquals(12, gdt.zdt.getHour());
                assertEquals(30, gdt.zdt.getMinute());
@@ -545,8 +515,8 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i07_parse2_dateTime_second() {
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12:30:45", 
null);
+       void i07_of_dateTime_second() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45", null);
                assertNotNull(gdt);
                assertEquals(12, gdt.zdt.getHour());
                assertEquals(30, gdt.zdt.getMinute());
@@ -555,8 +525,8 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i08_parse2_dateTime_millisecond_dot() {
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45.123", null);
+       void i08_of_dateTime_millisecond_dot() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45.123", 
null);
                assertNotNull(gdt);
                assertEquals(45, gdt.zdt.getSecond());
                assertEquals(123000000, gdt.zdt.getNano());
@@ -564,8 +534,8 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i09_parse2_dateTime_millisecond_comma() {
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45,123", null);
+       void i09_of_dateTime_millisecond_comma() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45,123", 
null);
                assertNotNull(gdt);
                assertEquals(45, gdt.zdt.getSecond());
                assertEquals(123000000, gdt.zdt.getNano());
@@ -573,9 +543,9 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i10_parse2_timeOnly_hour() {
+       void i10_of_timeOnly_hour() {
                var now = ZonedDateTime.now();
-               var gdt = GranularZonedDateTime.parse2("T12", null);
+               var gdt = GranularZonedDateTime.of("T12", null);
                assertNotNull(gdt);
                assertEquals(now.getYear(), gdt.zdt.getYear());
                assertEquals(now.getMonthValue(), gdt.zdt.getMonthValue());
@@ -586,9 +556,9 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i11_parse2_timeOnly_minute() {
+       void i11_of_timeOnly_minute() {
                var now = ZonedDateTime.now();
-               var gdt = GranularZonedDateTime.parse2("T12:30", null);
+               var gdt = GranularZonedDateTime.of("T12:30", null);
                assertNotNull(gdt);
                assertEquals(now.getYear(), gdt.zdt.getYear());
                assertEquals(now.getMonthValue(), gdt.zdt.getMonthValue());
@@ -599,9 +569,9 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i12_parse2_timeOnly_second() {
+       void i12_of_timeOnly_second() {
                var now = ZonedDateTime.now();
-               var gdt = GranularZonedDateTime.parse2("T12:30:45", null);
+               var gdt = GranularZonedDateTime.of("T12:30:45", null);
                assertNotNull(gdt);
                assertEquals(now.getYear(), gdt.zdt.getYear());
                assertEquals(now.getMonthValue(), gdt.zdt.getMonthValue());
@@ -613,9 +583,9 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i13_parse2_timeOnly_millisecond() {
+       void i13_of_timeOnly_millisecond() {
                var now = ZonedDateTime.now();
-               var gdt = GranularZonedDateTime.parse2("T12:30:45.123", null);
+               var gdt = GranularZonedDateTime.of("T12:30:45.123", null);
                assertNotNull(gdt);
                assertEquals(now.getYear(), gdt.zdt.getYear());
                assertEquals(now.getMonthValue(), gdt.zdt.getMonthValue());
@@ -625,66 +595,66 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i14_parse2_withZ() {
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12:30:45Z", 
null);
+       void i14_of_withZ() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45Z", 
null);
                assertNotNull(gdt);
                assertEquals(ZoneId.of("Z"), gdt.zdt.getZone());
                assertEquals(12, gdt.zdt.getHour());
        }
 
        @Test
-       void i15_parse2_withOffset_plusHH() {
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45+05", null);
+       void i15_of_withOffset_plusHH() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45+05", 
null);
                assertNotNull(gdt);
                assertEquals(ZoneOffset.ofHours(5), gdt.zdt.getOffset());
        }
 
        @Test
-       void i16_parse2_withOffset_minusHH() {
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45-05", null);
+       void i16_of_withOffset_minusHH() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45-05", 
null);
                assertNotNull(gdt);
                assertEquals(ZoneOffset.ofHours(-5), gdt.zdt.getOffset());
        }
 
        @Test
-       void i17_parse2_withOffset_plusHHMM() {
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45+0530", null);
+       void i17_of_withOffset_plusHHMM() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45+0530", 
null);
                assertNotNull(gdt);
                assertEquals(ZoneOffset.ofHoursMinutes(5, 30), 
gdt.zdt.getOffset());
        }
 
        @Test
-       void i18_parse2_withOffset_minusHHMM() {
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45-0530", null);
+       void i18_of_withOffset_minusHHMM() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45-0530", 
null);
                assertNotNull(gdt);
                assertEquals(ZoneOffset.ofHoursMinutes(-5, -30), 
gdt.zdt.getOffset());
        }
 
        @Test
-       void i19_parse2_withOffset_plusHH_MM() {
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45+05:30", null);
+       void i19_of_withOffset_plusHH_MM() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45+05:30", 
null);
                assertNotNull(gdt);
                assertEquals(ZoneOffset.ofHoursMinutes(5, 30), 
gdt.zdt.getOffset());
        }
 
        @Test
-       void i20_parse2_withOffset_minusHH_MM() {
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45-05:30", null);
+       void i20_of_withOffset_minusHH_MM() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45-05:30", 
null);
                assertNotNull(gdt);
                assertEquals(ZoneOffset.ofHoursMinutes(-5, -30), 
gdt.zdt.getOffset());
        }
 
        @Test
-       void i21_parse2_timezoneAfterYear() {
-               var gdt = GranularZonedDateTime.parse2("2011Z", null);
+       void i21_of_timezoneAfterYear() {
+               var gdt = GranularZonedDateTime.of("2011Z", null);
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(ZoneId.of("Z"), gdt.zdt.getZone());
        }
 
        @Test
-       void i22_parse2_timezoneAfterMonth() {
-               var gdt = GranularZonedDateTime.parse2("2011-01Z", null);
+       void i22_of_timezoneAfterMonth() {
+               var gdt = GranularZonedDateTime.of("2011-01Z", null);
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(1, gdt.zdt.getMonthValue());
@@ -692,32 +662,32 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i23_parse2_timezoneAfterDay() {
-               var gdt = GranularZonedDateTime.parse2("2011-01-15Z", null);
+       void i23_of_timezoneAfterDay() {
+               var gdt = GranularZonedDateTime.of("2011-01-15Z", null);
                assertNotNull(gdt);
                assertEquals(15, gdt.zdt.getDayOfMonth());
                assertEquals(ZoneId.of("Z"), gdt.zdt.getZone());
        }
 
        @Test
-       void i24_parse2_timezoneAfterHour() {
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12Z", null);
+       void i24_of_timezoneAfterHour() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12Z", null);
                assertNotNull(gdt);
                assertEquals(12, gdt.zdt.getHour());
                assertEquals(ZoneId.of("Z"), gdt.zdt.getZone());
        }
 
        @Test
-       void i25_parse2_timezoneAfterMinute() {
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12:30Z", 
null);
+       void i25_of_timezoneAfterMinute() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30Z", null);
                assertNotNull(gdt);
                assertEquals(30, gdt.zdt.getMinute());
                assertEquals(ZoneId.of("Z"), gdt.zdt.getZone());
        }
 
        @Test
-       void i26_parse2_timezoneAfterT() {
-               var gdt = GranularZonedDateTime.parse2("2011-01T+05:30", null);
+       void i26_of_timezoneAfterT() {
+               var gdt = GranularZonedDateTime.of("2011-01T+05:30", null);
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(1, gdt.zdt.getMonthValue());
@@ -725,8 +695,8 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i27_parse2_nanoseconds_1digit() {
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12:30:45.1", 
null);
+       void i27_of_nanoseconds_1digit() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45.1", 
null);
                assertNotNull(gdt);
                assertEquals(100000000, gdt.zdt.getNano());
                // 1 digit is treated as milliseconds (hundreds of milliseconds)
@@ -734,248 +704,248 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i28_parse2_nanoseconds_2digits() {
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45.12", null);
+       void i28_of_nanoseconds_2digits() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45.12", 
null);
                assertNotNull(gdt);
                assertEquals(120000000, gdt.zdt.getNano());
        }
 
        @Test
-       void i29_parse2_nanoseconds_3digits() {
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45.123", null);
+       void i29_of_nanoseconds_3digits() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45.123", 
null);
                assertNotNull(gdt);
                assertEquals(123000000, gdt.zdt.getNano());
        }
 
        @Test
-       void i30_parse2_nanoseconds_4digits() {
+       void i30_of_nanoseconds_4digits() {
                // Lines 1018: len == 4
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45.1234", null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45.1234", 
null);
                assertNotNull(gdt);
                assertEquals(123400000, gdt.zdt.getNano());
                assertEquals(ChronoField.NANO_OF_SECOND, gdt.precision);
        }
 
        @Test
-       void i30a_parse2_nanoseconds_5digits() {
+       void i30a_of_nanoseconds_5digits() {
                // Lines 1019: len == 5
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45.12345", null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45.12345", 
null);
                assertNotNull(gdt);
                assertEquals(123450000, gdt.zdt.getNano());
                assertEquals(ChronoField.NANO_OF_SECOND, gdt.precision);
        }
 
        @Test
-       void i30b_parse2_nanoseconds_6digits() {
+       void i30b_of_nanoseconds_6digits() {
                // Lines 1020: len == 6
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45.123456", null);
+               var gdt = 
GranularZonedDateTime.of("2011-01-15T12:30:45.123456", null);
                assertNotNull(gdt);
                assertEquals(123456000, gdt.zdt.getNano());
                assertEquals(ChronoField.NANO_OF_SECOND, gdt.precision);
        }
 
        @Test
-       void i30c_parse2_nanoseconds_7digits() {
+       void i30c_of_nanoseconds_7digits() {
                // Lines 1021: len == 7
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45.1234567", null);
+               var gdt = 
GranularZonedDateTime.of("2011-01-15T12:30:45.1234567", null);
                assertNotNull(gdt);
                assertEquals(123456700, gdt.zdt.getNano());
                assertEquals(ChronoField.NANO_OF_SECOND, gdt.precision);
        }
 
        @Test
-       void i30d_parse2_nanoseconds_8digits() {
+       void i30d_of_nanoseconds_8digits() {
                // Lines 1022: len == 8
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45.12345678", null);
+               var gdt = 
GranularZonedDateTime.of("2011-01-15T12:30:45.12345678", null);
                assertNotNull(gdt);
                assertEquals(123456780, gdt.zdt.getNano());
                assertEquals(ChronoField.NANO_OF_SECOND, gdt.precision);
        }
 
        @Test
-       void i30e_parse2_nanoseconds_9digits() {
+       void i30e_of_nanoseconds_9digits() {
                // Line 1023: len == 9 (default return)
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45.123456789", null);
+               var gdt = 
GranularZonedDateTime.of("2011-01-15T12:30:45.123456789", null);
                assertNotNull(gdt);
                assertEquals(123456789, gdt.zdt.getNano());
                assertEquals(ChronoField.NANO_OF_SECOND, gdt.precision);
        }
 
        @Test
-       void i31_parse2_badTimestamps() {
+       void i31_of_badTimestamps() {
                // Invalid formats
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("invalid", null));
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("invalid", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("", null));
 
                // Invalid year length
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("123", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("123", null));
 
                // Invalid month - below minimum (0)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-00", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-00", null));
 
                // Invalid month - above maximum (13)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-13", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-13", null));
 
                // Invalid month - way above maximum
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-99", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-99", null));
 
                // Invalid day - below minimum (0)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-00", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-00", null));
 
                // Invalid day - above maximum (32)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-32", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-32", null));
 
                // Invalid day - way above maximum
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-99", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-99", null));
 
                // Invalid hour - below minimum (-1, but this would be caught 
as invalid format)
                // Invalid hour - above maximum (24)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T24", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T24", null));
 
                // Invalid hour - way above maximum
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T99", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T99", null));
 
                // Invalid minute - above maximum (60)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:60", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:60", null));
 
                // Invalid minute - way above maximum
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:99", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:99", null));
 
                // Invalid second - above maximum (60)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:30:60", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:30:60", null));
 
                // Invalid second - way above maximum
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:30:99", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:30:99", null));
 
                // Invalid character after year (line 642)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011X", null));
 
                // Invalid character after '-' in S3 (line 651)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-X", null));
 
                // Invalid character after month (line 676)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01X", null));
 
                // Invalid character after '-' in S5 (line 685)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-X", null));
 
                // Invalid character after day (line 707)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15X", null));
 
                // Invalid character after 'T' in S7 (line 725)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("TX", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("TX", null));
 
                // Invalid character after hour (line 747)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12X", null));
 
                // Invalid character after ':' in S9 (line 756)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:X", null));
 
                // Invalid character after minute (line 778)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:30X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:30X", null));
 
                // Invalid character after ':' in S11 (line 787)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:30:X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:30:X", null));
 
                // Invalid character in S12 after seconds (line 810)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:30:45X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:30:45X", null));
 
                // Invalid character in S13 after '.' or ',' (line 827)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:30:45.X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:30:45.X", null));
 
                // Invalid character in S14 while reading milliseconds (line 
846)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:30:45.12X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:30:45.12X", null));
 
                // Invalid character in S16 after '+' (line 857)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:30:45+X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:30:45+X", null));
 
                // Invalid character in S17 after '-' (line 865)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:30:45-X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:30:45-X", null));
 
                // Invalid character in S18 while reading offset hours (line 
875)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:30:45+05X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:30:45+05X", null));
 
                // Invalid character in S19 after ':' in offset (line 884)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:30:45+05:X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:30:45+05:X", null));
 
                // Invalid character in S20 while reading offset minutes (line 
891)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:30:45+05:30X", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:30:45+05:30X", null));
 
                // Invalid offset format in S18 finalization - not 2 or 4 
digits (line 941)
                // Ending in S18 with 1 digit (should be 2 or 4)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:30:45+1", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:30:45+1", null));
                // Ending in S18 with 3 digits (should be 2 or 4)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:30:45+123", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:30:45+123", null));
                // Ending in S18 with 5 digits (should be 2 or 4)
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:30:45+12345", null));
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:30:45+12345", null));
 
-               // Invalid nanosecond length in parseNanos (line 1010)
-               // This is defensive code - parseNanos checks length is 1-9 
digits
+               // Invalid nanosecond length in ofNanos (line 1010)
+               // This is defensive code - ofNanos checks length is 1-9 digits
                // The state machine should prevent this, but we test the 
defensive check
                // by ending in S14 with 0 digits (which shouldn't happen, but 
tests the check)
                // Actually, we can't easily trigger 0 digits since we 
transition to S14 only after seeing a digit.
                // For >9 digits, if we have 10+ digits, the 10th non-digit 
would trigger line 846, not 1010.
-               // However, if we end the string with exactly 10 digits, we'd 
call parseNanos with len=10, triggering 1010.
+               // However, if we end the string with exactly 10 digits, we'd 
call ofNanos with len=10, triggering 1010.
                // But wait, the state machine allows digits in S14, so we'd 
need to end the string with 10+ digits.
                // Let's test with a string that ends with 10 digits of 
fractional seconds (no timezone)
-               // This would end in S14 with 10 digits, calling parseNanos 
with len=10, which is >9, triggering line 1010.
-               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.parse2("2011-01-15T12:30:45.1234567890", null));
+               // This would end in S14 with 10 digits, calling ofNanos with 
len=10, which is >9, triggering line 1010.
+               assertThrows(java.time.format.DateTimeParseException.class, () 
-> GranularZonedDateTime.of("2011-01-15T12:30:45.1234567890", null));
        }
 
        @Test
-       void i38_parse2_timeOnly_withZ() {
+       void i38_of_timeOnly_withZ() {
                var now = ZonedDateTime.now();
-               var gdt = GranularZonedDateTime.parse2("T12:30:45Z", null);
+               var gdt = GranularZonedDateTime.of("T12:30:45Z", null);
                assertNotNull(gdt);
                assertEquals(now.getYear(), gdt.zdt.getYear());
                assertEquals(ZoneId.of("Z"), gdt.zdt.getZone());
        }
 
        @Test
-       void i39_parse2_timeOnly_withOffset() {
+       void i39_of_timeOnly_withOffset() {
                var now = ZonedDateTime.now();
-               var gdt = GranularZonedDateTime.parse2("T12:30:45+05:30", null);
+               var gdt = GranularZonedDateTime.of("T12:30:45+05:30", null);
                assertNotNull(gdt);
                assertEquals(now.getYear(), gdt.zdt.getYear());
                assertEquals(ZoneOffset.ofHoursMinutes(5, 30), 
gdt.zdt.getOffset());
        }
 
        @Test
-       void i40_parse2_noTimezone_usesSystemDefault() {
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12:30:45", 
null);
+       void i40_of_noTimezone_usesSystemDefault() {
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45", null);
                assertNotNull(gdt);
                assertEquals(ZoneId.systemDefault(), gdt.zdt.getZone());
        }
 
        @Test
-       void i41_parse2_yearFollowedByT() {
+       void i41_of_yearFollowedByT() {
                // Lines 627-628: Year followed by 'T'
-               var gdt = GranularZonedDateTime.parse2("2011T12", null);
+               var gdt = GranularZonedDateTime.of("2011T12", null);
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(12, gdt.zdt.getHour());
        }
 
        @Test
-       void i42_parse2_yearFollowedByZ() {
+       void i42_of_yearFollowedByZ() {
                // Lines 629-632: Year followed by 'Z'
-               var gdt = GranularZonedDateTime.parse2("2011Z", null);
+               var gdt = GranularZonedDateTime.of("2011Z", null);
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(ZoneId.of("Z"), gdt.zdt.getZone());
        }
 
        @Test
-       void i43_parse2_yearFollowedByPlus() {
+       void i43_of_yearFollowedByPlus() {
                // Lines 633-636: Year followed by '+'
-               var gdt = GranularZonedDateTime.parse2("2011+05", null);
+               var gdt = GranularZonedDateTime.of("2011+05", null);
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(ZoneOffset.ofHours(5), gdt.zdt.getOffset());
        }
 
        @Test
-       void i44_parse2_yearFollowedByMinus() {
+       void i44_of_yearFollowedByMinus() {
                // Lines 637-640: Year followed by '-' (timezone)
                // Note: The code has two '-' checks in S2, but the first one 
(line 621) goes to S3 (month),
                // so the second '-' check (line 637) is unreachable in normal 
parsing.
@@ -983,7 +953,7 @@ class GranularZonedDateTime_Test extends TestBase {
                // After parsing a complete component, we can have timezone. 
Let's test with hour followed by negative timezone.
                // Actually, let's test the negative timezone path that IS 
reachable - after hour, minute, or second.
                // This test covers the concept even if the specific line isn't 
reachable.
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12-05", 
null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12-05", null);
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(12, gdt.zdt.getHour());
@@ -991,9 +961,9 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i45_parse2_monthFollowedByZ() {
+       void i45_of_monthFollowedByZ() {
                // Lines 663-666: Month followed by 'Z'
-               var gdt = GranularZonedDateTime.parse2("2011-01Z", null);
+               var gdt = GranularZonedDateTime.of("2011-01Z", null);
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(1, gdt.zdt.getMonthValue());
@@ -1001,9 +971,9 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i46_parse2_monthFollowedByPlus() {
+       void i46_of_monthFollowedByPlus() {
                // Lines 667-670: Month followed by '+'
-               var gdt = GranularZonedDateTime.parse2("2011-01+05", null);
+               var gdt = GranularZonedDateTime.of("2011-01+05", null);
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(1, gdt.zdt.getMonthValue());
@@ -1011,12 +981,12 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i47_parse2_monthFollowedByMinus() {
+       void i47_of_monthFollowedByMinus() {
                // Lines 671-674: Month followed by '-' (timezone)
                // Note: Similar to i44, the second '-' check in S4 may be 
unreachable because the first '-' goes to S5 (day).
                // However, we can test the negative timezone concept with a 
reachable path.
                // Let's test with minute followed by negative timezone to 
cover the negative timezone logic.
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12:30-05", 
null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30-05", null);
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(1, gdt.zdt.getMonthValue());
@@ -1025,306 +995,306 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i48_parse2_dayFollowedByZ() {
+       void i48_of_dayFollowedByZ() {
                // Lines 692-697: Day followed by 'Z'
-               var gdt = GranularZonedDateTime.parse2("2011-01-15Z", null);
+               var gdt = GranularZonedDateTime.of("2011-01-15Z", null);
                assertNotNull(gdt);
                assertEquals(15, gdt.zdt.getDayOfMonth());
                assertEquals(ZoneId.of("Z"), gdt.zdt.getZone());
        }
 
        @Test
-       void i49_parse2_dayFollowedByPlus() {
+       void i49_of_dayFollowedByPlus() {
                // Lines 698-701: Day followed by '+'
-               var gdt = GranularZonedDateTime.parse2("2011-01-15+05", null);
+               var gdt = GranularZonedDateTime.of("2011-01-15+05", null);
                assertNotNull(gdt);
                assertEquals(15, gdt.zdt.getDayOfMonth());
                assertEquals(ZoneOffset.ofHours(5), gdt.zdt.getOffset());
        }
 
        @Test
-       void i50_parse2_dayFollowedByMinus() {
+       void i50_of_dayFollowedByMinus() {
                // Lines 702-705: Day followed by '-'
-               var gdt = GranularZonedDateTime.parse2("2011-01-15-05", null);
+               var gdt = GranularZonedDateTime.of("2011-01-15-05", null);
                assertNotNull(gdt);
                assertEquals(15, gdt.zdt.getDayOfMonth());
                assertEquals(ZoneOffset.ofHours(-5), gdt.zdt.getOffset());
        }
 
        @Test
-       void i51_parse2_TFollowedByZ() {
+       void i51_of_TFollowedByZ() {
                // Lines 715-717: 'T' followed by 'Z'
-               var gdt = GranularZonedDateTime.parse2("TZ", null);
+               var gdt = GranularZonedDateTime.of("TZ", null);
                assertNotNull(gdt);
                assertEquals(ZoneId.of("Z"), gdt.zdt.getZone());
        }
 
        @Test
-       void i52_parse2_TFollowedByPlus() {
+       void i52_of_TFollowedByPlus() {
                // Lines 718-720: 'T' followed by '+'
-               var gdt = GranularZonedDateTime.parse2("T+05", null);
+               var gdt = GranularZonedDateTime.of("T+05", null);
                assertNotNull(gdt);
                assertEquals(ZoneOffset.ofHours(5), gdt.zdt.getOffset());
        }
 
        @Test
-       void i53_parse2_TFollowedByMinus() {
+       void i53_of_TFollowedByMinus() {
                // Lines 721-723: 'T' followed by '-'
-               var gdt = GranularZonedDateTime.parse2("T-05", null);
+               var gdt = GranularZonedDateTime.of("T-05", null);
                assertNotNull(gdt);
                assertEquals(ZoneOffset.ofHours(-5), gdt.zdt.getOffset());
        }
 
        @Test
-       void i54_parse2_hourFollowedByZ() {
+       void i54_of_hourFollowedByZ() {
                // Lines 732-737: Hour followed by 'Z'
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12Z", null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12Z", null);
                assertNotNull(gdt);
                assertEquals(12, gdt.zdt.getHour());
                assertEquals(ZoneId.of("Z"), gdt.zdt.getZone());
        }
 
        @Test
-       void i55_parse2_hourFollowedByPlus() {
+       void i55_of_hourFollowedByPlus() {
                // Lines 738-741: Hour followed by '+'
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12+05", 
null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12+05", null);
                assertNotNull(gdt);
                assertEquals(12, gdt.zdt.getHour());
                assertEquals(ZoneOffset.ofHours(5), gdt.zdt.getOffset());
        }
 
        @Test
-       void i56_parse2_hourFollowedByMinus() {
+       void i56_of_hourFollowedByMinus() {
                // Lines 742-745: Hour followed by '-'
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12-05", 
null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12-05", null);
                assertNotNull(gdt);
                assertEquals(12, gdt.zdt.getHour());
                assertEquals(ZoneOffset.ofHours(-5), gdt.zdt.getOffset());
        }
 
        @Test
-       void i57_parse2_minuteFollowedByZ() {
+       void i57_of_minuteFollowedByZ() {
                // Lines 765-768: Minute followed by 'Z'
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12:30Z", 
null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30Z", null);
                assertNotNull(gdt);
                assertEquals(30, gdt.zdt.getMinute());
                assertEquals(ZoneId.of("Z"), gdt.zdt.getZone());
        }
 
        @Test
-       void i58_parse2_minuteFollowedByPlus() {
+       void i58_of_minuteFollowedByPlus() {
                // Lines 769-772: Minute followed by '+'
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12:30+05", 
null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30+05", null);
                assertNotNull(gdt);
                assertEquals(30, gdt.zdt.getMinute());
                assertEquals(ZoneOffset.ofHours(5), gdt.zdt.getOffset());
        }
 
        @Test
-       void i59_parse2_minuteFollowedByMinus() {
+       void i59_of_minuteFollowedByMinus() {
                // Lines 773-776: Minute followed by '-'
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12:30-05", 
null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30-05", null);
                assertNotNull(gdt);
                assertEquals(30, gdt.zdt.getMinute());
                assertEquals(ZoneOffset.ofHours(-5), gdt.zdt.getOffset());
        }
 
        
//====================================================================================================
-       // parse2(String) - Timezone after fractional seconds tests
+       // of(String) - Timezone after fractional seconds tests
        
//====================================================================================================
 
        @Test
-       void i73_parse2_S13_fractionalSeparatorFollowedByZ() {
+       void i73_of_S13_fractionalSeparatorFollowedByZ() {
                // Lines 817-819: S13 - '.' or ',' followed by 'Z'
-               var gdt1 = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45.Z", null);
+               var gdt1 = GranularZonedDateTime.of("2011-01-15T12:30:45.Z", 
null);
                assertNotNull(gdt1);
                assertEquals(ZoneId.of("Z"), gdt1.zdt.getZone());
                assertEquals(0, gdt1.zdt.getNano());
 
-               var gdt2 = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45,Z", null);
+               var gdt2 = GranularZonedDateTime.of("2011-01-15T12:30:45,Z", 
null);
                assertNotNull(gdt2);
                assertEquals(ZoneId.of("Z"), gdt2.zdt.getZone());
                assertEquals(0, gdt2.zdt.getNano());
        }
 
        @Test
-       void i74_parse2_S13_fractionalSeparatorFollowedByPlus() {
+       void i74_of_S13_fractionalSeparatorFollowedByPlus() {
                // Lines 820-822: S13 - '.' or ',' followed by '+'
-               var gdt1 = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45.+05:00", null);
+               var gdt1 = 
GranularZonedDateTime.of("2011-01-15T12:30:45.+05:00", null);
                assertNotNull(gdt1);
                assertEquals(ZoneOffset.ofHours(5), gdt1.zdt.getOffset());
                assertEquals(0, gdt1.zdt.getNano());
 
-               var gdt2 = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45,+05:00", null);
+               var gdt2 = 
GranularZonedDateTime.of("2011-01-15T12:30:45,+05:00", null);
                assertNotNull(gdt2);
                assertEquals(ZoneOffset.ofHours(5), gdt2.zdt.getOffset());
                assertEquals(0, gdt2.zdt.getNano());
        }
 
        @Test
-       void i75_parse2_S13_fractionalSeparatorFollowedByMinus() {
+       void i75_of_S13_fractionalSeparatorFollowedByMinus() {
                // Lines 823-825: S13 - '.' or ',' followed by '-'
-               var gdt1 = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45.-05:00", null);
+               var gdt1 = 
GranularZonedDateTime.of("2011-01-15T12:30:45.-05:00", null);
                assertNotNull(gdt1);
                assertEquals(ZoneOffset.ofHours(-5), gdt1.zdt.getOffset());
                assertEquals(0, gdt1.zdt.getNano());
 
-               var gdt2 = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45,-05:00", null);
+               var gdt2 = 
GranularZonedDateTime.of("2011-01-15T12:30:45,-05:00", null);
                assertNotNull(gdt2);
                assertEquals(ZoneOffset.ofHours(-5), gdt2.zdt.getOffset());
                assertEquals(0, gdt2.zdt.getNano());
        }
 
        @Test
-       void i76_parse2_S14_fractionalSecondsFollowedByZ() {
+       void i76_of_S14_fractionalSecondsFollowedByZ() {
                // Lines 833-836: S14 - fractional seconds followed by 'Z'
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45.123Z", null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45.123Z", 
null);
                assertNotNull(gdt);
                assertEquals(123000000, gdt.zdt.getNano());
                assertEquals(ZoneId.of("Z"), gdt.zdt.getZone());
        }
 
        @Test
-       void i77_parse2_S14_fractionalSecondsFollowedByPlus() {
+       void i77_of_S14_fractionalSecondsFollowedByPlus() {
                // Lines 837-840: S14 - fractional seconds followed by '+'
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45.123+05:00", null);
+               var gdt = 
GranularZonedDateTime.of("2011-01-15T12:30:45.123+05:00", null);
                assertNotNull(gdt);
                assertEquals(123000000, gdt.zdt.getNano());
                assertEquals(ZoneOffset.ofHours(5), gdt.zdt.getOffset());
        }
 
        @Test
-       void i78_parse2_S14_fractionalSecondsFollowedByMinus() {
+       void i78_of_S14_fractionalSecondsFollowedByMinus() {
                // Lines 841-844: S14 - fractional seconds followed by '-'
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45.123-05:00", null);
+               var gdt = 
GranularZonedDateTime.of("2011-01-15T12:30:45.123-05:00", null);
                assertNotNull(gdt);
                assertEquals(123000000, gdt.zdt.getNano());
                assertEquals(ZoneOffset.ofHours(-5), gdt.zdt.getOffset());
        }
 
        @Test
-       void i79_parse2_S14_fractionalSecondsWithCommaFollowedByZ() {
+       void i79_of_S14_fractionalSecondsWithCommaFollowedByZ() {
                // Lines 833-836: S14 - fractional seconds (comma separator) 
followed by 'Z'
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45,123Z", null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45,123Z", 
null);
                assertNotNull(gdt);
                assertEquals(123000000, gdt.zdt.getNano());
                assertEquals(ZoneId.of("Z"), gdt.zdt.getZone());
        }
 
        @Test
-       void i80_parse2_S14_fractionalSecondsWithCommaFollowedByPlus() {
+       void i80_of_S14_fractionalSecondsWithCommaFollowedByPlus() {
                // Lines 837-840: S14 - fractional seconds (comma separator) 
followed by '+'
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45,123+05:00", null);
+               var gdt = 
GranularZonedDateTime.of("2011-01-15T12:30:45,123+05:00", null);
                assertNotNull(gdt);
                assertEquals(123000000, gdt.zdt.getNano());
                assertEquals(ZoneOffset.ofHours(5), gdt.zdt.getOffset());
        }
 
        @Test
-       void i81_parse2_S14_fractionalSecondsWithCommaFollowedByMinus() {
+       void i81_of_S14_fractionalSecondsWithCommaFollowedByMinus() {
                // Lines 841-844: S14 - fractional seconds (comma separator) 
followed by '-'
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45,123-05:00", null);
+               var gdt = 
GranularZonedDateTime.of("2011-01-15T12:30:45,123-05:00", null);
                assertNotNull(gdt);
                assertEquals(123000000, gdt.zdt.getNano());
                assertEquals(ZoneOffset.ofHours(-5), gdt.zdt.getOffset());
        }
 
        @Test
-       void i82_parse2_S15_invalidCharacterAfterZ() {
+       void i82_of_S15_invalidCharacterAfterZ() {
                // Line 846: S15 - invalid character after 'Z' (should throw 
error)
                // After finding 'Z', any additional characters should trigger 
this error
                // Test all possible transitions to S15:
 
                // S2 -> S15 (year followed by Z, then invalid char)
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       GranularZonedDateTime.parse2("2011Z5", null);
+                       GranularZonedDateTime.of("2011Z5", null);
                });
 
                // S4 -> S15 (month followed by Z, then invalid char)
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       GranularZonedDateTime.parse2("2011-01ZX", null);
+                       GranularZonedDateTime.of("2011-01ZX", null);
                });
 
                // S6 -> S15 (day followed by Z, then invalid char)
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       GranularZonedDateTime.parse2("2011-01-15ZX", null);
+                       GranularZonedDateTime.of("2011-01-15ZX", null);
                });
 
                // S7 -> S15 (T followed by Z, then invalid char)
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       GranularZonedDateTime.parse2("TZX", null);
+                       GranularZonedDateTime.of("TZX", null);
                });
 
                // S8 -> S15 (hour followed by Z, then invalid char)
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       GranularZonedDateTime.parse2("2011-01-15T12ZX", null);
+                       GranularZonedDateTime.of("2011-01-15T12ZX", null);
                });
 
                // S10 -> S15 (minute followed by Z, then invalid char)
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       GranularZonedDateTime.parse2("2011-01-15T12:30ZX", 
null);
+                       GranularZonedDateTime.of("2011-01-15T12:30ZX", null);
                });
 
                // S12 -> S15 (second followed by Z, then invalid char)
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       GranularZonedDateTime.parse2("2011-01-15T12:30:45ZX", 
null);
+                       GranularZonedDateTime.of("2011-01-15T12:30:45ZX", null);
                });
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       GranularZonedDateTime.parse2("2011-01-15T12:30:45Z+", 
null);
+                       GranularZonedDateTime.of("2011-01-15T12:30:45Z+", null);
                });
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       GranularZonedDateTime.parse2("2011-01-15T12:30:45Z-", 
null);
+                       GranularZonedDateTime.of("2011-01-15T12:30:45Z-", null);
                });
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       GranularZonedDateTime.parse2("2011-01-15T12:30:45Z5", 
null);
+                       GranularZonedDateTime.of("2011-01-15T12:30:45Z5", null);
                });
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       GranularZonedDateTime.parse2("2011-01-15T12:30:45Z ", 
null);
+                       GranularZonedDateTime.of("2011-01-15T12:30:45Z ", null);
                });
 
                // S13 -> S15 (fractional separator followed by Z, then invalid 
char)
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       GranularZonedDateTime.parse2("2011-01-15T12:30:45.ZX", 
null);
+                       GranularZonedDateTime.of("2011-01-15T12:30:45.ZX", 
null);
                });
 
                // S14 -> S15 (fractional digits followed by Z, then invalid 
char)
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       
GranularZonedDateTime.parse2("2011-01-15T12:30:45.123ZX", null);
+                       GranularZonedDateTime.of("2011-01-15T12:30:45.123ZX", 
null);
                });
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       GranularZonedDateTime.parse2("T12:30:45ZX", null);
+                       GranularZonedDateTime.of("T12:30:45ZX", null);
                });
        }
 
        @Test
-       void i83_parse2_invalidDateForMonth() {
+       void i83_of_invalidDateForMonth() {
                // Invalid dates for specific months - LocalDateTime.of() 
throws DateTimeException
                // November only has 30 days
                assertThrows(java.time.DateTimeException.class, () -> {
-                       GranularZonedDateTime.parse2("2011-11-31", null);
+                       GranularZonedDateTime.of("2011-11-31", null);
                });
                // February in non-leap year only has 28 days
                assertThrows(java.time.DateTimeException.class, () -> {
-                       GranularZonedDateTime.parse2("2011-02-29", null);
+                       GranularZonedDateTime.of("2011-02-29", null);
                });
                // February in non-leap year only has 28 days (day 30)
                assertThrows(java.time.DateTimeException.class, () -> {
-                       GranularZonedDateTime.parse2("2011-02-30", null);
+                       GranularZonedDateTime.of("2011-02-30", null);
                });
                // April only has 30 days
                assertThrows(java.time.DateTimeException.class, () -> {
-                       GranularZonedDateTime.parse2("2011-04-31", null);
+                       GranularZonedDateTime.of("2011-04-31", null);
                });
                // June only has 30 days
                assertThrows(java.time.DateTimeException.class, () -> {
-                       GranularZonedDateTime.parse2("2011-06-31", null);
+                       GranularZonedDateTime.of("2011-06-31", null);
                });
                // September only has 30 days
                assertThrows(java.time.DateTimeException.class, () -> {
-                       GranularZonedDateTime.parse2("2011-09-31", null);
+                       GranularZonedDateTime.of("2011-09-31", null);
                });
                // Valid: February 29 in leap year
-               var gdt = GranularZonedDateTime.parse2("2024-02-29", null);
+               var gdt = GranularZonedDateTime.of("2024-02-29", null);
                assertNotNull(gdt);
                assertEquals(2024, gdt.zdt.getYear());
                assertEquals(2, gdt.zdt.getMonthValue());
@@ -1332,36 +1302,36 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i84_parse2_zoneIdAlreadySet() {
+       void i84_of_zoneIdAlreadySet() {
                // Line 941: zoneId is not null, so offset building is skipped 
(false branch)
                // Test with 'Z' timezone (zoneId already set)
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12:30:45Z", 
null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45Z", 
null);
                assertNotNull(gdt);
                assertEquals(ZoneId.of("Z"), gdt.zdt.getZone());
                // ohour and ominute remain -1, but zoneId is already set, so 
the if condition on line 941 is false
        }
 
        @Test
-       void i85_parse2_offsetOnlyHours() {
+       void i85_of_offsetOnlyHours() {
                // Line 946: ohour >= 0 but ominute < 0 (only hours, no 
minutes) - else if branch
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45+05", null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45+05", 
null);
                assertNotNull(gdt);
                assertEquals(ZoneOffset.ofHours(5), gdt.zdt.getOffset());
        }
 
        @Test
-       void i86_parse2_offsetHoursAndMinutes() {
+       void i86_of_offsetHoursAndMinutes() {
                // Line 942: ohour >= 0 && ominute >= 0 (both hours and minutes)
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45+05:30", null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45+05:30", 
null);
                assertNotNull(gdt);
                assertEquals(ZoneOffset.ofHoursMinutes(5, 30), 
gdt.zdt.getOffset());
        }
 
        @Test
-       void i87_parse2_timeOnlyMissingYear() {
+       void i87_of_timeOnlyMissingYear() {
                // Lines 964-966: timeOnly=true, year/month/day are -1, use 
current date
                var now = ZonedDateTime.now();
-               var gdt = GranularZonedDateTime.parse2("T12:30:45", null);
+               var gdt = GranularZonedDateTime.of("T12:30:45", null);
                assertNotNull(gdt);
                assertEquals(now.getYear(), gdt.zdt.getYear());
                assertEquals(now.getMonthValue(), gdt.zdt.getMonthValue());
@@ -1372,9 +1342,9 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i88_parse2_dateFormatMissingMonth() {
+       void i88_of_dateFormatMissingMonth() {
                // Line 970: timeOnly=false, month is -1, default to 1
-               var gdt = GranularZonedDateTime.parse2("2011", null);
+               var gdt = GranularZonedDateTime.of("2011", null);
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(1, gdt.zdt.getMonthValue()); // Defaults to 1
@@ -1382,9 +1352,9 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i89_parse2_dateFormatMissingDay() {
+       void i89_of_dateFormatMissingDay() {
                // Line 971: timeOnly=false, day is -1, default to 1
-               var gdt = GranularZonedDateTime.parse2("2011-01", null);
+               var gdt = GranularZonedDateTime.of("2011-01", null);
                assertNotNull(gdt);
                assertEquals(2011, gdt.zdt.getYear());
                assertEquals(1, gdt.zdt.getMonthValue());
@@ -1392,10 +1362,10 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i90_parse2_withDefaultZoneId() {
+       void i90_of_withDefaultZoneId() {
                // Line 958: defaultZoneId != null branch - use provided 
defaultZoneId when no zone in string
                var defaultZone = ZoneId.of("America/New_York");
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12:30:45", 
defaultZone);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45", 
defaultZone);
                assertNotNull(gdt);
                assertEquals(defaultZone, gdt.zdt.getZone());
                assertEquals(2011, gdt.zdt.getYear());
@@ -1407,11 +1377,11 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i91_parse2_withDefaultZoneId_timeOnly() {
+       void i91_of_withDefaultZoneId_timeOnly() {
                // Line 958: defaultZoneId != null branch - use provided 
defaultZoneId for time-only format
                var defaultZone = ZoneId.of("Europe/London");
                var now = ZonedDateTime.now(defaultZone);
-               var gdt = GranularZonedDateTime.parse2("T12:30:45", 
defaultZone);
+               var gdt = GranularZonedDateTime.of("T12:30:45", defaultZone);
                assertNotNull(gdt);
                assertEquals(defaultZone, gdt.zdt.getZone());
                assertEquals(now.getYear(), gdt.zdt.getYear());
@@ -1423,10 +1393,10 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        @Test
-       void i92_parse2_withDefaultZoneId_ignoredWhenZoneInString() {
+       void i92_of_withDefaultZoneId_ignoredWhenZoneInString() {
                // Line 958: defaultZoneId is ignored when zone is found in the 
string
                var defaultZone = ZoneId.of("America/New_York");
-               var gdt = GranularZonedDateTime.parse2("2011-01-15T12:30:45Z", 
defaultZone);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45Z", 
defaultZone);
                assertNotNull(gdt);
                // Should use 'Z' from the string, not the defaultZoneId
                assertEquals(ZoneId.of("Z"), gdt.zdt.getZone());
@@ -1434,121 +1404,121 @@ class GranularZonedDateTime_Test extends TestBase {
        }
 
        
//====================================================================================================
-       // parse2(String) - ISO8601 offset range validation tests (-18:00 ≤ 
offset ≤ +18:00)
+       // of(String) - ISO8601 offset range validation tests (-18:00 ≤ offset 
≤ +18:00)
        
//====================================================================================================
 
        @Test
-       void i60_parse2_offsetBoundary_minus18_00() {
+       void i60_of_offsetBoundary_minus18_00() {
                // Minimum valid offset: -18:00
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45-18:00", null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45-18:00", 
null);
                assertNotNull(gdt);
                assertEquals(ZoneOffset.ofHoursMinutes(-18, 0), 
gdt.zdt.getOffset());
        }
 
        @Test
-       void i61_parse2_offsetBoundary_plus18_00() {
+       void i61_of_offsetBoundary_plus18_00() {
                // Maximum valid offset: +18:00
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45+18:00", null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45+18:00", 
null);
                assertNotNull(gdt);
                assertEquals(ZoneOffset.ofHoursMinutes(18, 0), 
gdt.zdt.getOffset());
        }
 
        @Test
-       void i62_parse2_offsetBoundary_minus18_00_compact() {
+       void i62_of_offsetBoundary_minus18_00_compact() {
                // Minimum valid offset in compact format: -1800
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45-1800", null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45-1800", 
null);
                assertNotNull(gdt);
                assertEquals(ZoneOffset.ofHoursMinutes(-18, 0), 
gdt.zdt.getOffset());
        }
 
        @Test
-       void i63_parse2_offsetBoundary_plus18_00_compact() {
+       void i63_of_offsetBoundary_plus18_00_compact() {
                // Maximum valid offset in compact format: +1800
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45+1800", null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45+1800", 
null);
                assertNotNull(gdt);
                assertEquals(ZoneOffset.ofHoursMinutes(18, 0), 
gdt.zdt.getOffset());
        }
 
        @Test
-       void i64_parse2_offsetBoundary_minus18_00_hoursOnly() {
+       void i64_of_offsetBoundary_minus18_00_hoursOnly() {
                // Minimum valid offset hours only: -18
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45-18", null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45-18", 
null);
                assertNotNull(gdt);
                assertEquals(ZoneOffset.ofHours(-18), gdt.zdt.getOffset());
        }
 
        @Test
-       void i65_parse2_offsetBoundary_plus18_00_hoursOnly() {
+       void i65_of_offsetBoundary_plus18_00_hoursOnly() {
                // Maximum valid offset hours only: +18
-               var gdt = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45+18", null);
+               var gdt = GranularZonedDateTime.of("2011-01-15T12:30:45+18", 
null);
                assertNotNull(gdt);
                assertEquals(ZoneOffset.ofHours(18), gdt.zdt.getOffset());
        }
 
        @Test
-       void i66_parse2_offsetInvalid_belowMinimum() {
+       void i66_of_offsetInvalid_belowMinimum() {
                // Invalid: offset below -18:00
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       
GranularZonedDateTime.parse2("2011-01-15T12:30:45-19:00", null);
+                       GranularZonedDateTime.of("2011-01-15T12:30:45-19:00", 
null);
                });
        }
 
        @Test
-       void i67_parse2_offsetInvalid_aboveMaximum() {
+       void i67_of_offsetInvalid_aboveMaximum() {
                // Invalid: offset above +18:00
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       
GranularZonedDateTime.parse2("2011-01-15T12:30:45+19:00", null);
+                       GranularZonedDateTime.of("2011-01-15T12:30:45+19:00", 
null);
                });
        }
 
        @Test
-       void i68_parse2_offsetInvalid_belowMinimum_compact() {
+       void i68_of_offsetInvalid_belowMinimum_compact() {
                // Invalid: offset below -18:00 in compact format
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       
GranularZonedDateTime.parse2("2011-01-15T12:30:45-1900", null);
+                       GranularZonedDateTime.of("2011-01-15T12:30:45-1900", 
null);
                });
        }
 
        @Test
-       void i69_parse2_offsetInvalid_aboveMaximum_compact() {
+       void i69_of_offsetInvalid_aboveMaximum_compact() {
                // Invalid: offset above +18:00 in compact format
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       
GranularZonedDateTime.parse2("2011-01-15T12:30:45+1900", null);
+                       GranularZonedDateTime.of("2011-01-15T12:30:45+1900", 
null);
                });
        }
 
        @Test
-       void i70_parse2_offsetInvalid_belowMinimum_hoursOnly() {
+       void i70_of_offsetInvalid_belowMinimum_hoursOnly() {
                // Invalid: offset below -18 hours only
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       GranularZonedDateTime.parse2("2011-01-15T12:30:45-19", 
null);
+                       GranularZonedDateTime.of("2011-01-15T12:30:45-19", 
null);
                });
        }
 
        @Test
-       void i71_parse2_offsetInvalid_aboveMaximum_hoursOnly() {
+       void i71_of_offsetInvalid_aboveMaximum_hoursOnly() {
                // Invalid: offset above +18 hours only
                assertThrows(java.time.format.DateTimeParseException.class, () 
-> {
-                       GranularZonedDateTime.parse2("2011-01-15T12:30:45+19", 
null);
+                       GranularZonedDateTime.of("2011-01-15T12:30:45+19", 
null);
                });
        }
 
        @Test
-       void i72_parse2_offsetValid_withinRange() {
+       void i72_of_offsetValid_withinRange() {
                // Valid offsets within range
-               var gdt1 = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45-12:30", null);
+               var gdt1 = 
GranularZonedDateTime.of("2011-01-15T12:30:45-12:30", null);
                assertNotNull(gdt1);
                assertEquals(ZoneOffset.ofHoursMinutes(-12, -30), 
gdt1.zdt.getOffset());
 
-               var gdt2 = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45+12:30", null);
+               var gdt2 = 
GranularZonedDateTime.of("2011-01-15T12:30:45+12:30", null);
                assertNotNull(gdt2);
                assertEquals(ZoneOffset.ofHoursMinutes(12, 30), 
gdt2.zdt.getOffset());
 
-               var gdt3 = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45-17:59", null);
+               var gdt3 = 
GranularZonedDateTime.of("2011-01-15T12:30:45-17:59", null);
                assertNotNull(gdt3);
                assertEquals(ZoneOffset.ofHoursMinutes(-17, -59), 
gdt3.zdt.getOffset());
 
-               var gdt4 = 
GranularZonedDateTime.parse2("2011-01-15T12:30:45+17:59", null);
+               var gdt4 = 
GranularZonedDateTime.of("2011-01-15T12:30:45+17:59", null);
                assertNotNull(gdt4);
                assertEquals(ZoneOffset.ofHoursMinutes(17, 59), 
gdt4.zdt.getOffset());
        }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/httppart/OpenApiPartSerializer_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/httppart/OpenApiPartSerializer_Test.java
index 9ebfbea400..e2ee88b854 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/httppart/OpenApiPartSerializer_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/httppart/OpenApiPartSerializer_Test.java
@@ -176,14 +176,14 @@ class OpenApiPartSerializer_Test extends TestBase {
 
        @Test void c06_stringType_dateFormat() throws Exception {
                var ps = T_DATE;
-               var in = opt("2012-12-21").filter(x1 -> ! isBlank(x1)).map(x -> 
GranularZonedDateTime.parse("2012-12-21").getZonedDateTime()).map(GregorianCalendar::from).orElse(null);
+               var in = opt("2012-12-21").filter(x1 -> ! isBlank(x1)).map(x -> 
GranularZonedDateTime.of("2012-12-21").getZonedDateTime()).map(GregorianCalendar::from).orElse(null);
                assertTrue(serialize(ps, in).contains("2012"));
                assertEquals("null", serialize(ps, null));
        }
 
        @Test void c07_stringType_dateTimeFormat() throws Exception {
                var ps = T_DATETIME;
-               var in = opt("2012-12-21T12:34:56.789").filter(x1 -> ! 
isBlank(x1)).map(x -> 
GranularZonedDateTime.parse("2012-12-21T12:34:56.789").getZonedDateTime()).map(GregorianCalendar::from).orElse(null);
+               var in = opt("2012-12-21T12:34:56.789").filter(x1 -> ! 
isBlank(x1)).map(x -> 
GranularZonedDateTime.of("2012-12-21T12:34:56.789").getZonedDateTime()).map(GregorianCalendar::from).orElse(null);
                assertTrue(serialize(ps, in).contains("2012"));
                assertEquals("null", serialize(ps, null));
        }
@@ -918,13 +918,13 @@ class OpenApiPartSerializer_Test extends TestBase {
 
                assertEquals(
                        
"f01=foo,f02=Zm9v,f04=2012-12-21T12:34:56Z,f05=666F6F,f06=66 6F 
6F,f07=foo,f08=1,f09=2,f10=1.0,f11=1.0,f12=true,f99=1",
-                       serialize(ps, new 
H2("foo",foob,opt("2012-12-21T12:34:56Z").filter(x1 -> ! isBlank(x1)).map(x -> 
GranularZonedDateTime.parse("2012-12-21T12:34:56Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null),foob,foob,"foo",1,2,1.0,1.0,true,1))
+                       serialize(ps, new 
H2("foo",foob,opt("2012-12-21T12:34:56Z").filter(x1 -> ! isBlank(x1)).map(x -> 
GranularZonedDateTime.of("2012-12-21T12:34:56Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null),foob,foob,"foo",1,2,1.0,1.0,true,1))
                );
                assertEquals("", serialize(ps, new 
H2(null,null,null,null,null,null,null,null,null,null,null,null)));
                assertEquals("null", serialize(ps, null));
                assertEquals(
                        
"f01=foo,f02=Zm9v,f04=2012-12-21T12:34:56Z,f05=666F6F,f06=66 6F 
6F,f07=foo,f08=1,f09=2,f10=1.0,f11=1.0,f12=true,f99=1",
-                       serialize(ps, 
JsonMap.of("f01","foo","f02",foob,"f04",opt("2012-12-21T12:34:56Z").filter(x11 
-> ! isBlank(x11)).map(x2 -> 
GranularZonedDateTime.parse("2012-12-21T12:34:56Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null),"f05",foob,"f06",foob,"f07","foo","f08",1,"f09",2,"f10",1.0,"f11",1.0,"f12",true,"f99",1))
+                       serialize(ps, 
JsonMap.of("f01","foo","f02",foob,"f04",opt("2012-12-21T12:34:56Z").filter(x11 
-> ! isBlank(x11)).map(x2 -> 
GranularZonedDateTime.of("2012-12-21T12:34:56Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null),"f05",foob,"f06",foob,"f07","foo","f08",1,"f09",2,"f10",1.0,"f11",1.0,"f12",true,"f99",1))
                );
        }
 
@@ -948,7 +948,7 @@ class OpenApiPartSerializer_Test extends TestBase {
 
                assertEquals(
                        
"(f01=@('a,b',null),f02=@(Zm9v,null),f04=@(2012-12-21T12:34:56Z,null),f05=@(666F6F,null),f06=@('66
 6F 
6F',null),f07=@(a,b,null),f08=@(1,2,null),f09=@(3,4,null),f10=@(1.0,2.0,null),f11=@(3.0,4.0,null),f12=@(true,false,null),f99=@(1,x,null))",
-                       serialize(ps, new H2(a("a,b",null),new 
byte[][]{foob,null},a(opt("2012-12-21T12:34:56Z").filter(x1 -> ! 
isBlank(x1)).map(x -> 
GranularZonedDateTime.parse("2012-12-21T12:34:56Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null),null),new
 byte[][]{foob,null},new 
byte[][]{foob,null},a("a","b",null),a(1,2,null),a(3,4,null),a(1f,2f,null),a(3f,4f,null),a(true,false,null),a(1,"x",null)))
+                       serialize(ps, new H2(a("a,b",null),new 
byte[][]{foob,null},a(opt("2012-12-21T12:34:56Z").filter(x1 -> ! 
isBlank(x1)).map(x -> 
GranularZonedDateTime.of("2012-12-21T12:34:56Z").getZonedDateTime()).map(GregorianCalendar::from).orElse(null),null),new
 byte[][]{foob,null},new 
byte[][]{foob,null},a("a","b",null),a(1,2,null),a(3,4,null),a(1f,2f,null),a(3f,4f,null),a(true,false,null),a(1,"x",null)))
                );
 
        }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/oapi/OpenApi_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/oapi/OpenApi_Test.java
index 21f8d97cf0..888a79b4f2 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/oapi/OpenApi_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/oapi/OpenApi_Test.java
@@ -861,6 +861,6 @@ public class OpenApi_Test extends TestBase {
        
//---------------------------------------------------------------------------------------------
 
        private static Calendar cal(String in) {
-               return opt(in).filter(x1 -> ! isBlank(x1)).map(x -> 
GranularZonedDateTime.parse(in).getZonedDateTime()).map(GregorianCalendar::from).orElse(null);
+               return opt(in).filter(x1 -> ! isBlank(x1)).map(x -> 
GranularZonedDateTime.of(in).getZonedDateTime()).map(GregorianCalendar::from).orElse(null);
        }
 }
\ No newline at end of file
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/objecttools/ObjectSearcher_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/objecttools/ObjectSearcher_Test.java
index c593b530ae..00e92391b2 100755
--- 
a/juneau-utest/src/test/java/org/apache/juneau/objecttools/ObjectSearcher_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/objecttools/ObjectSearcher_Test.java
@@ -387,7 +387,7 @@ public class ObjectSearcher_Test extends TestBase {
                        var bb = new B[dates.length];
                        for (var i = 0; i < dates.length; i++) {
                                bb[i] = new B();
-                               bb[i].f = opt(dates[i]).filter(x1 -> ! 
isBlank(x1)).map(x -> 
GranularZonedDateTime.parse(x).getZonedDateTime()).map(GregorianCalendar::from).orElse(null);
+                               bb[i].f = opt(dates[i]).filter(x1 -> ! 
isBlank(x1)).map(x -> 
GranularZonedDateTime.of(x).getZonedDateTime()).map(GregorianCalendar::from).orElse(null);
                        }
                        return bb;
                }

Reply via email to