@@ -39,10 +39,14 @@ public static long parseTimestamp(final @NotNull String timestamp) {
3939 final int day = parseInt (timestamp , offset , offset += 2 );
4040
4141 if (!checkOffset (timestamp , offset , 'T' )) {
42- if (offset ! = length ) {
43- throw new IllegalArgumentException ( "Invalid date separator" );
42+ if (offset = = length ) {
43+ return dateOnlyEpochMillis ( year , month , day );
4444 }
45- return dateOnlyEpochMillis (year , month , day );
45+ final char timezoneIndicator = timestamp .charAt (offset );
46+ if (timezoneIndicator == 'Z' || timezoneIndicator == '+' || timezoneIndicator == '-' ) {
47+ return dateOnlyEpochMillisWithTimezone (timestamp , length , offset , year , month , day );
48+ }
49+ throw new IllegalArgumentException ("Invalid date separator" );
4650 }
4751 validateDate (year , month , day );
4852 offset ++;
@@ -171,6 +175,50 @@ private static long dateOnlyEpochMillis(final int year, final int month, final i
171175 return new GregorianCalendar (year , month - 1 , day ).getTimeInMillis ();
172176 }
173177
178+ private static long dateOnlyEpochMillisWithTimezone (
179+ final @ NotNull String timestamp ,
180+ final int length ,
181+ int offset ,
182+ final int year ,
183+ final int month ,
184+ final int day ) {
185+ final int timezoneOffsetMillis ;
186+ final boolean allowTrailingCharacters ;
187+ final char timezoneIndicator = timestamp .charAt (offset );
188+ if (timezoneIndicator == 'Z' ) {
189+ timezoneOffsetMillis = 0 ;
190+ offset ++;
191+ allowTrailingCharacters = true ;
192+ } else if (timezoneIndicator == '+' || timezoneIndicator == '-' ) {
193+ final int sign = timezoneIndicator == '+' ? 1 : -1 ;
194+ offset ++;
195+ final int timezoneHour = parseInt (timestamp , offset , offset += 2 );
196+ int timezoneMinute = 0 ;
197+ if (checkOffset (timestamp , offset , ':' )) {
198+ offset ++;
199+ }
200+ if (length >= offset + 2 ) {
201+ timezoneMinute = parseInt (timestamp , offset , offset += 2 );
202+ }
203+ validateTimezone (timezoneHour , timezoneMinute );
204+ timezoneOffsetMillis =
205+ sign * (int ) (timezoneHour * MILLIS_PER_HOUR + timezoneMinute * MILLIS_PER_MINUTE );
206+ allowTrailingCharacters = false ;
207+ } else {
208+ throw new IllegalArgumentException ("Invalid time zone indicator" );
209+ }
210+
211+ if (!allowTrailingCharacters && offset != length ) {
212+ throw new IllegalArgumentException ("Invalid trailing characters" );
213+ }
214+
215+ if (isBeforeGregorianCutover (year , month , day )) {
216+ return epochMillisWithCalendar (year , month , day , 0 , 0 , 0 , 0 , timezoneOffsetMillis );
217+ }
218+ validateDate (year , month , day );
219+ return epochMillis (year , month , day , 0 , 0 , 0 , 0 , timezoneOffsetMillis );
220+ }
221+
174222 private static long epochMillisWithCalendar (
175223 final int year ,
176224 final int month ,
0 commit comments