diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheEntry.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheEntry.java
index 8d071d600..578a7227e 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheEntry.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheEntry.java
@@ -132,7 +132,7 @@ public class HttpCacheEntry implements MessageHeaders, Serializable {
* @return the Date value of the header or null if the header is not present
*/
private Date parseDate() {
- return DateUtils.parseDate(this, HttpHeaders.DATE);
+ return DateUtils.toDate(DateUtils.parseStandardDate(this, HttpHeaders.DATE));
}
/**
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java
index 70d7a47ae..bbbd75c38 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java
@@ -51,7 +51,6 @@ import org.apache.hc.client5.http.cache.ResourceIOException;
import org.apache.hc.client5.http.impl.ExecSupport;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
-import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.concurrent.CancellableDependency;
@@ -574,7 +573,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
@Override
public void completed(final HttpCacheEntry existingEntry) {
- if (DateUtils.isAfter(existingEntry, backendResponse, HttpHeaders.DATE)) {
+ if (DateSupport.isAfter(existingEntry, backendResponse, HttpHeaders.DATE)) {
LOG.debug("Backend already contains fresher cache entry");
try {
final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, existingEntry);
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheInvalidatorBase.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheInvalidatorBase.java
index 604ed61d8..feaf95e4c 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheInvalidatorBase.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheInvalidatorBase.java
@@ -30,7 +30,6 @@ import java.net.URI;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
-import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.client5.http.utils.URIUtils;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpHeaders;
@@ -99,7 +98,7 @@ class CacheInvalidatorBase {
}
static boolean responseDateOlderThanEntryDate(final HttpResponse response, final HttpCacheEntry entry) {
- return DateUtils.isBefore(response, entry, HttpHeaders.DATE);
+ return DateSupport.isBefore(response, entry, HttpHeaders.DATE);
}
}
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheUpdateHandler.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheUpdateHandler.java
index 2302148c7..7416766e1 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheUpdateHandler.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheUpdateHandler.java
@@ -36,7 +36,6 @@ import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.Resource;
import org.apache.hc.client5.http.cache.ResourceFactory;
import org.apache.hc.client5.http.cache.ResourceIOException;
-import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpRequest;
@@ -132,7 +131,7 @@ class CacheUpdateHandler {
}
private Header[] mergeHeaders(final HttpCacheEntry entry, final HttpResponse response) {
- if (DateUtils.isAfter(entry, response, HttpHeaders.DATE)) {
+ if (DateSupport.isAfter(entry, response, HttpHeaders.DATE)) {
return entry.getHeaders();
}
final HeaderGroup headerGroup = new HeaderGroup();
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheValidityPolicy.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheValidityPolicy.java
index ef2b82c3f..f29159a85 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheValidityPolicy.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheValidityPolicy.java
@@ -26,6 +26,7 @@
*/
package org.apache.hc.client5.http.impl.cache;
+import java.time.Instant;
import java.util.Date;
import java.util.Iterator;
@@ -64,11 +65,11 @@ class CacheValidityPolicy {
return TimeValue.ZERO_MILLISECONDS;
}
- final Date expiry = DateUtils.parseDate(entry, HeaderConstants.EXPIRES);
+ final Instant expiry = DateUtils.parseStandardDate(entry, HeaderConstants.EXPIRES);
if (expiry == null) {
return TimeValue.ZERO_MILLISECONDS;
}
- final long diff = expiry.getTime() - dateValue.getTime();
+ final long diff = expiry.toEpochMilli() - dateValue.getTime();
return TimeValue.ofSeconds(diff / 1000);
}
@@ -98,10 +99,10 @@ class CacheValidityPolicy {
public TimeValue getHeuristicFreshnessLifetime(final HttpCacheEntry entry,
final float coefficient, final TimeValue defaultLifetime) {
final Date dateValue = entry.getDate();
- final Date lastModifiedValue = DateUtils.parseDate(entry, HeaderConstants.LAST_MODIFIED);
+ final Instant lastModifiedValue = DateUtils.parseStandardDate(entry, HeaderConstants.LAST_MODIFIED);
if (dateValue != null && lastModifiedValue != null) {
- final long diff = dateValue.getTime() - lastModifiedValue.getTime();
+ final long diff = dateValue.getTime() - lastModifiedValue.toEpochMilli();
if (diff < 0) {
return TimeValue.ZERO_MILLISECONDS;
}
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachedHttpResponseGenerator.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachedHttpResponseGenerator.java
index 090d7dc10..fab60b66b 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachedHttpResponseGenerator.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachedHttpResponseGenerator.java
@@ -26,6 +26,7 @@
*/
package org.apache.hc.client5.http.impl.cache;
+import java.time.Instant;
import java.util.Date;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
@@ -105,7 +106,7 @@ class CachedHttpResponseGenerator {
// - Date, unless its omission is required by section 14.8.1
Header dateHeader = entry.getFirstHeader(HttpHeaders.DATE);
if (dateHeader == null) {
- dateHeader = new BasicHeader(HttpHeaders.DATE, DateUtils.formatDate(new Date()));
+ dateHeader = new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(Instant.now()));
}
response.addHeader(dateHeader);
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachedResponseSuitabilityChecker.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachedResponseSuitabilityChecker.java
index 4f3efeade..7e1200a9b 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachedResponseSuitabilityChecker.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachedResponseSuitabilityChecker.java
@@ -26,6 +26,7 @@
*/
package org.apache.hc.client5.http.impl.cache;
+import java.time.Instant;
import java.util.Date;
import java.util.Iterator;
@@ -278,7 +279,8 @@ class CachedResponseSuitabilityChecker {
final boolean hasLastModifiedValidator = hasSupportedLastModifiedValidator(request);
final boolean etagValidatorMatches = (hasEtagValidator) && etagValidatorMatches(request, entry);
- final boolean lastModifiedValidatorMatches = (hasLastModifiedValidator) && lastModifiedValidatorMatches(request, entry, now);
+ final boolean lastModifiedValidatorMatches = (hasLastModifiedValidator) && lastModifiedValidatorMatches(request, entry,
+ DateUtils.toInstant(now));
if ((hasEtagValidator && hasLastModifiedValidator)
&& !(etagValidatorMatches && lastModifiedValidatorMatches)) {
@@ -332,16 +334,16 @@ class CachedResponseSuitabilityChecker {
* @param now right NOW in time
* @return boolean Does the last modified header match
*/
- private boolean lastModifiedValidatorMatches(final HttpRequest request, final HttpCacheEntry entry, final Date now) {
- final Date lastModified = DateUtils.parseDate(entry, HeaderConstants.LAST_MODIFIED);
+ private boolean lastModifiedValidatorMatches(final HttpRequest request, final HttpCacheEntry entry, final Instant now) {
+ final Instant lastModified = DateUtils.parseStandardDate(entry, HeaderConstants.LAST_MODIFIED);
if (lastModified == null) {
return false;
}
for (final Header h : request.getHeaders(HeaderConstants.IF_MODIFIED_SINCE)) {
- final Date ifModifiedSince = DateUtils.parseDate(h.getValue());
+ final Instant ifModifiedSince = DateUtils.parseStandardDate(h.getValue());
if (ifModifiedSince != null) {
- if (ifModifiedSince.after(now) || lastModified.after(ifModifiedSince)) {
+ if (ifModifiedSince.isAfter(now) || lastModified.isAfter(ifModifiedSince)) {
return false;
}
}
@@ -351,8 +353,8 @@ class CachedResponseSuitabilityChecker {
private boolean hasValidDateField(final HttpRequest request, final String headerName) {
for(final Header h : request.getHeaders(headerName)) {
- final Date date = DateUtils.parseDate(h.getValue());
- return date != null;
+ final Instant instant = DateUtils.parseStandardDate(h.getValue());
+ return instant != null;
}
return false;
}
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java
index 3f756fe84..127240185 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java
@@ -47,7 +47,6 @@ import org.apache.hc.client5.http.classic.ExecChainHandler;
import org.apache.hc.client5.http.impl.ExecSupport;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
-import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ContentType;
@@ -413,7 +412,7 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
final HttpCacheEntry cacheEntry;
if (cacheConfig.isFreshnessCheckEnabled()) {
final HttpCacheEntry existingEntry = responseCache.getCacheEntry(target, request);
- if (DateUtils.isAfter(existingEntry, backendResponse, HttpHeaders.DATE)) {
+ if (DateSupport.isAfter(existingEntry, backendResponse, HttpHeaders.DATE)) {
LOG.debug("Backend already contains fresher cache entry");
cacheEntry = existingEntry;
} else {
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExecBase.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExecBase.java
index b7b47ec13..3a7a462c5 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExecBase.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExecBase.java
@@ -40,7 +40,6 @@ import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.cache.HttpCacheContext;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.ResourceIOException;
-import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HeaderElement;
import org.apache.hc.core5.http.HttpHeaders;
@@ -333,7 +332,7 @@ public class CachingExecBase {
// Date header, so we can't tell if they are out of order
// according to the origin clock; thus we can skip the
// unconditional retry recommended in 13.2.6 of RFC 2616.
- return DateUtils.isBefore(backendResponse, cacheEntry, HttpHeaders.DATE);
+ return DateSupport.isBefore(backendResponse, cacheEntry, HttpHeaders.DATE);
}
boolean shouldSendNotModifiedResponse(final HttpRequest request, final HttpCacheEntry responseEntry) {
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/DateSupport.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/DateSupport.java
new file mode 100644
index 000000000..70ac2ef0c
--- /dev/null
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/DateSupport.java
@@ -0,0 +1,116 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+package org.apache.hc.client5.http.impl.cache;
+
+import java.time.Instant;
+
+import org.apache.hc.client5.http.utils.DateUtils;
+import org.apache.hc.core5.annotation.Internal;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.MessageHeaders;
+
+/**
+ * HTTP cache date support utilities.
+ *
+ * @since 5.2
+ */
+@Internal
+public final class DateSupport {
+
+ /**
+ * Tests if the first message is after (newer) than second one
+ * using the given message header for comparison.
+ *
+ * @param message1 the first message
+ * @param message2 the second message
+ * @param headerName header name
+ *
+ * @return {@code true} if both messages contain a header with the given name
+ * and the value of the header from the first message is newer that of
+ * the second message.
+ *
+ * @since 5.0
+ */
+ public static boolean isAfter(
+ final MessageHeaders message1,
+ final MessageHeaders message2,
+ final String headerName) {
+ if (message1 != null && message2 != null) {
+ final Header dateHeader1 = message1.getFirstHeader(headerName);
+ if (dateHeader1 != null) {
+ final Header dateHeader2 = message2.getFirstHeader(headerName);
+ if (dateHeader2 != null) {
+ final Instant date1 = DateUtils.parseStandardDate(dateHeader1.getValue());
+ if (date1 != null) {
+ final Instant date2 = DateUtils.parseStandardDate(dateHeader2.getValue());
+ if (date2 != null) {
+ return date1.isAfter(date2);
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tests if the first message is before (older) than the second one
+ * using the given message header for comparison.
+ *
+ * @param message1 the first message
+ * @param message2 the second message
+ * @param headerName header name
+ *
+ * @return {@code true} if both messages contain a header with the given name
+ * and the value of the header from the first message is older that of
+ * the second message.
+ *
+ * @since 5.0
+ */
+ public static boolean isBefore(
+ final MessageHeaders message1,
+ final MessageHeaders message2,
+ final String headerName) {
+ if (message1 != null && message2 != null) {
+ final Header dateHeader1 = message1.getFirstHeader(headerName);
+ if (dateHeader1 != null) {
+ final Header dateHeader2 = message2.getFirstHeader(headerName);
+ if (dateHeader2 != null) {
+ final Instant date1 = DateUtils.parseStandardDate(dateHeader1.getValue());
+ if (date1 != null) {
+ final Instant date2 = DateUtils.parseStandardDate(dateHeader2.getValue());
+ if (date2 != null) {
+ return date1.isBefore(date2);
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseCachingPolicy.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseCachingPolicy.java
index 940866963..5a01e505c 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseCachingPolicy.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseCachingPolicy.java
@@ -26,9 +26,9 @@
*/
package org.apache.hc.client5.http.impl.cache;
+import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
-import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
@@ -154,7 +154,7 @@ class ResponseCachingPolicy {
return false;
}
- final Date date = DateUtils.parseDate(response, HttpHeaders.DATE);
+ final Instant date = DateUtils.parseStandardDate(response, HttpHeaders.DATE);
if (date == null) {
LOG.debug("Invalid / missing Date header");
return false;
@@ -292,12 +292,12 @@ class ResponseCachingPolicy {
if (expiresHdr == null || dateHdr == null) {
return false;
}
- final Date expires = DateUtils.parseDate(expiresHdr.getValue());
- final Date date = DateUtils.parseDate(dateHdr.getValue());
+ final Instant expires = DateUtils.parseStandardDate(expiresHdr.getValue());
+ final Instant date = DateUtils.parseStandardDate(dateHdr.getValue());
if (expires == null || date == null) {
return false;
}
- return expires.equals(date) || expires.before(date);
+ return expires.equals(date) || expires.isBefore(date);
}
private boolean from1_0Origin(final HttpResponse response) {
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseProtocolCompliance.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseProtocolCompliance.java
index b3f4fc270..31dec99bd 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseProtocolCompliance.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseProtocolCompliance.java
@@ -27,6 +27,7 @@
package org.apache.hc.client5.http.impl.cache;
import java.io.IOException;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -85,7 +86,7 @@ class ResponseProtocolCompliance {
private void warningsWithNonMatchingWarnDatesAreRemoved(
final HttpResponse response) {
- final Date responseDate = DateUtils.parseDate(response, HttpHeaders.DATE);
+ final Instant responseDate = DateUtils.parseStandardDate(response, HttpHeaders.DATE);
if (responseDate == null) {
return;
}
@@ -153,7 +154,7 @@ class ResponseProtocolCompliance {
private void ensure206ContainsDateHeader(final HttpResponse response) {
if (response.getFirstHeader(HttpHeaders.DATE) == null) {
- response.addHeader(HttpHeaders.DATE, DateUtils.formatDate(new Date()));
+ response.addHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(Instant.now()));
}
}
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/WarningValue.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/WarningValue.java
index 905cf86f8..ed7687296 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/WarningValue.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/WarningValue.java
@@ -265,7 +265,7 @@ class WarningValue {
parseError();
}
offs += m.end();
- warnDate = DateUtils.parseDate(src.substring(curr+1,offs-1));
+ warnDate = DateUtils.toDate(DateUtils.parseStandardDate(src.substring(curr+1,offs-1)));
}
/*
@@ -359,7 +359,7 @@ class WarningValue {
public String toString() {
if (warnDate != null) {
return String.format("%d %s %s \"%s\"", warnCode,
- warnAgent, warnText, DateUtils.formatDate(warnDate));
+ warnAgent, warnText, DateUtils.formatStandardDate(DateUtils.toInstant(warnDate)));
} else {
return String.format("%d %s %s", warnCode, warnAgent, warnText);
}
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestDateSupport.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestDateSupport.java
new file mode 100644
index 000000000..de3edbefd
--- /dev/null
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestDateSupport.java
@@ -0,0 +1,100 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+
+package org.apache.hc.client5.http.impl.cache;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.Month;
+import java.time.ZoneId;
+
+import org.apache.hc.client5.http.utils.DateUtils;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.apache.hc.core5.http.message.HeaderGroup;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.MatcherAssert;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link DateSupport}.
+ */
+public class TestDateSupport {
+
+ private static Instant createInstant(final int year, final Month month, final int day) {
+ return LocalDate.of(year, month, day).atStartOfDay(ZoneId.of("GMT")).toInstant();
+ }
+
+ @Test
+ public void testIsBefore() throws Exception {
+ final HeaderGroup message1 = new HeaderGroup();
+ final HeaderGroup message2 = new HeaderGroup();
+ MatcherAssert.assertThat(DateSupport.isBefore(null, null, HttpHeaders.DATE), CoreMatchers.equalTo(false));
+ MatcherAssert.assertThat(DateSupport.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
+
+ message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?"));
+ message2.setHeader(new BasicHeader(HttpHeaders.DATE, "eh?"));
+ MatcherAssert.assertThat(DateSupport.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
+
+ message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?"));
+ message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
+ MatcherAssert.assertThat(DateSupport.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
+
+ message1.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 25))));
+ message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
+ MatcherAssert.assertThat(DateSupport.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(true));
+
+ message1.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 27))));
+ message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
+ MatcherAssert.assertThat(DateSupport.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
+ }
+
+ @Test
+ public void testIsAfter() throws Exception {
+ final HeaderGroup message1 = new HeaderGroup();
+ final HeaderGroup message2 = new HeaderGroup();
+ MatcherAssert.assertThat(DateSupport.isAfter(null, null, HttpHeaders.DATE), CoreMatchers.equalTo(false));
+ MatcherAssert.assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
+
+ message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?"));
+ message2.setHeader(new BasicHeader(HttpHeaders.DATE, "eh?"));
+ MatcherAssert.assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
+
+ message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?"));
+ message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
+ MatcherAssert.assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
+
+ message1.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 27))));
+ message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
+ MatcherAssert.assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(true));
+
+ message1.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 25))));
+ message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
+ MatcherAssert.assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
+ }
+
+}
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestResponseCachingPolicy.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestResponseCachingPolicy.java
index a1f003088..b374d28fe 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestResponseCachingPolicy.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestResponseCachingPolicy.java
@@ -56,6 +56,10 @@ public class TestResponseCachingPolicy {
private Date tenSecondsFromNow;
private Date sixSecondsAgo;
+ static String formatDate(final Date date) {
+ return DateUtils.formatStandardDate(DateUtils.toInstant(date));
+ }
+
@Before
public void setUp() throws Exception {
now = new Date();
@@ -65,7 +69,7 @@ public class TestResponseCachingPolicy {
policy = new ResponseCachingPolicy(0, true, false, false);
request = new BasicHttpRequest("GET","/");
response = new BasicHttpResponse(HttpStatus.SC_OK, "");
- response.setHeader("Date", DateUtils.formatDate(new Date()));
+ response.setHeader("Date", formatDate(new Date()));
response.setHeader("Content-Length", "0");
}
@@ -205,7 +209,7 @@ public class TestResponseCachingPolicy {
public void testNon206WithExplicitExpiresIsCacheable() {
final int status = getRandomStatus();
response.setCode(status);
- response.setHeader("Expires", DateUtils.formatDate(new Date()));
+ response.setHeader("Expires", formatDate(new Date()));
Assert.assertTrue(policy.isResponseCacheable("GET", response));
}
@@ -360,7 +364,7 @@ public class TestResponseCachingPolicy {
Assert.assertTrue(policy.isResponseCacheable("GET", response));
response = new BasicHttpResponse(HttpStatus.SC_OK, "");
- response.setHeader("Date", DateUtils.formatDate(new Date()));
+ response.setHeader("Date", formatDate(new Date()));
response.addHeader("Cache-Control", "no-transform");
response.setHeader("Content-Length", "0");
@@ -375,7 +379,7 @@ public class TestResponseCachingPolicy {
Assert.assertTrue(policy.isResponseCacheable("HEAD", response));
response = new BasicHttpResponse(HttpStatus.SC_OK, "");
- response.setHeader("Date", DateUtils.formatDate(new Date()));
+ response.setHeader("Date", formatDate(new Date()));
response.addHeader("Cache-Control", "no-transform");
response.setHeader("Content-Length", "0");
@@ -480,8 +484,8 @@ public class TestResponseCachingPolicy {
@Test
public void testResponsesWithMultipleDateHeadersAreNotCacheable() {
- response.addHeader("Date", DateUtils.formatDate(now));
- response.addHeader("Date", DateUtils.formatDate(sixSecondsAgo));
+ response.addHeader("Date", formatDate(now));
+ response.addHeader("Date", formatDate(sixSecondsAgo));
Assert.assertFalse(policy.isResponseCacheable("GET", response));
}
@@ -491,8 +495,8 @@ public class TestResponseCachingPolicy {
request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
response.setHeader("Cache-Control", "public");
- response.addHeader("Date", DateUtils.formatDate(now));
- response.addHeader("Date", DateUtils.formatDate(sixSecondsAgo));
+ response.addHeader("Date", formatDate(now));
+ response.addHeader("Date", formatDate(sixSecondsAgo));
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@@ -514,8 +518,8 @@ public class TestResponseCachingPolicy {
@Test
public void testResponsesWithMultipleExpiresHeadersAreNotCacheable() {
- response.addHeader("Expires", DateUtils.formatDate(now));
- response.addHeader("Expires", DateUtils.formatDate(sixSecondsAgo));
+ response.addHeader("Expires", formatDate(now));
+ response.addHeader("Expires", formatDate(sixSecondsAgo));
Assert.assertFalse(policy.isResponseCacheable("GET", response));
}
@@ -525,8 +529,8 @@ public class TestResponseCachingPolicy {
request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
response.setHeader("Cache-Control", "public");
- response.addHeader("Expires", DateUtils.formatDate(now));
- response.addHeader("Expires", DateUtils.formatDate(sixSecondsAgo));
+ response.addHeader("Expires", formatDate(now));
+ response.addHeader("Expires", formatDate(sixSecondsAgo));
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@@ -587,8 +591,8 @@ public class TestResponseCachingPolicy {
@Test
public void testResponsesToGETWithQueryParamsAndExplicitCachingAreCacheable() {
request = new BasicHttpRequest("GET", "/foo?s=bar");
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@@ -596,8 +600,8 @@ public class TestResponseCachingPolicy {
public void testResponsesToHEADWithQueryParamsAndExplicitCachingAreCacheable() {
policy = new ResponseCachingPolicy(0, true, false, false);
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@@ -605,8 +609,8 @@ public class TestResponseCachingPolicy {
public void testResponsesToGETWithQueryParamsAndExplicitCachingAreCacheableEvenWhen1_0QueryCachingDisabled() {
policy = new ResponseCachingPolicy(0, true, true, false);
request = new BasicHttpRequest("GET", "/foo?s=bar");
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@@ -614,8 +618,8 @@ public class TestResponseCachingPolicy {
public void testResponsesToHEADWithQueryParamsAndExplicitCachingAreCacheableEvenWhen1_0QueryCachingDisabled() {
policy = new ResponseCachingPolicy(0, true, true, false);
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@@ -658,8 +662,8 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("GET", "/foo?s=bar");
response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
response.setVersion(HttpVersion.HTTP_1_0);
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@@ -669,8 +673,8 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
response.setVersion(HttpVersion.HTTP_1_0);
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@@ -680,8 +684,8 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("GET", "/foo?s=bar");
response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
response.setVersion(HttpVersion.HTTP_1_0);
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@@ -691,8 +695,8 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
response.setVersion(HttpVersion.HTTP_1_0);
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@@ -713,8 +717,8 @@ public class TestResponseCachingPolicy {
@Test
public void getsWithQueryParametersFrom1_0OriginsViaProxiesAreCacheableWithExpires() {
request = new BasicHttpRequest("GET", "/foo?s=bar");
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "1.0 someproxy");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@@ -723,8 +727,8 @@ public class TestResponseCachingPolicy {
public void headsWithQueryParametersFrom1_0OriginsViaProxiesAreCacheableWithExpires() {
policy = new ResponseCachingPolicy(0, true, false, false);
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "1.0 someproxy");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@@ -733,8 +737,8 @@ public class TestResponseCachingPolicy {
public void getsWithQueryParametersFrom1_0OriginsViaProxiesCanNotBeCacheableEvenWithExpires() {
policy = new ResponseCachingPolicy(0, true, true, true);
request = new BasicHttpRequest("GET", "/foo?s=bar");
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "1.0 someproxy");
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@@ -743,8 +747,8 @@ public class TestResponseCachingPolicy {
public void headsWithQueryParametersFrom1_0OriginsViaProxiesCanNotBeCacheableEvenWithExpires() {
policy = new ResponseCachingPolicy(0, true, true, true);
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "1.0 someproxy");
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@@ -752,8 +756,8 @@ public class TestResponseCachingPolicy {
@Test
public void getsWithQueryParametersFrom1_0OriginsViaExplicitProxiesAreCacheableWithExpires() {
request = new BasicHttpRequest("GET", "/foo?s=bar");
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "HTTP/1.0 someproxy");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@@ -762,8 +766,8 @@ public class TestResponseCachingPolicy {
public void headsWithQueryParametersFrom1_0OriginsViaExplicitProxiesAreCacheableWithExpires() {
policy = new ResponseCachingPolicy(0, true, false, false);
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "HTTP/1.0 someproxy");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@@ -772,8 +776,8 @@ public class TestResponseCachingPolicy {
public void getsWithQueryParametersFrom1_0OriginsViaExplicitProxiesCanNotBeCacheableEvenWithExpires() {
policy = new ResponseCachingPolicy(0, true, true, true);
request = new BasicHttpRequest("GET", "/foo?s=bar");
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "HTTP/1.0 someproxy");
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@@ -782,8 +786,8 @@ public class TestResponseCachingPolicy {
public void headsWithQueryParametersFrom1_0OriginsViaExplicitProxiesCanNotBeCacheableEvenWithExpires() {
policy = new ResponseCachingPolicy(0, true, true, true);
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "HTTP/1.0 someproxy");
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@@ -793,8 +797,8 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("GET", "/foo?s=bar");
response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
response.setVersion(HttpVersion.HTTP_1_0);
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "1.1 someproxy");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@@ -805,24 +809,24 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
response.setVersion(HttpVersion.HTTP_1_0);
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "1.1 someproxy");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@Test
public void notCacheableIfExpiresEqualsDateAndNoCacheControl() {
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(now));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(now));
response.removeHeaders("Cache-Control");
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@Test
public void notCacheableIfExpiresPrecedesDateAndNoCacheControl() {
- response.setHeader("Date", DateUtils.formatDate(now));
- response.setHeader("Expires", DateUtils.formatDate(sixSecondsAgo));
+ response.setHeader("Date", formatDate(now));
+ response.setHeader("Expires", formatDate(sixSecondsAgo));
response.removeHeaders("Cache-Control");
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@@ -830,7 +834,7 @@ public class TestResponseCachingPolicy {
@Test
public void test302WithExplicitCachingHeaders() {
response.setCode(HttpStatus.SC_MOVED_TEMPORARILY);
- response.setHeader("Date", DateUtils.formatDate(now));
+ response.setHeader("Date", formatDate(now));
response.setHeader("Cache-Control","max-age=300");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@@ -839,7 +843,7 @@ public class TestResponseCachingPolicy {
public void test303WithExplicitCachingHeadersUnderDefaultBehavior() {
// RFC 2616 says: 303 should not be cached
response.setCode(HttpStatus.SC_SEE_OTHER);
- response.setHeader("Date", DateUtils.formatDate(now));
+ response.setHeader("Date", formatDate(now));
response.setHeader("Cache-Control","max-age=300");
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@@ -850,7 +854,7 @@ public class TestResponseCachingPolicy {
// response headers
policy = new ResponseCachingPolicy(0, true, false, true);
response.setCode(HttpStatus.SC_SEE_OTHER);
- response.setHeader("Date", DateUtils.formatDate(now));
+ response.setHeader("Date", formatDate(now));
response.setHeader("Cache-Control","max-age=300");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@@ -858,7 +862,7 @@ public class TestResponseCachingPolicy {
@Test
public void test307WithExplicitCachingHeaders() {
response.setCode(HttpStatus.SC_TEMPORARY_REDIRECT);
- response.setHeader("Date", DateUtils.formatDate(now));
+ response.setHeader("Date", formatDate(now));
response.setHeader("Cache-Control","max-age=300");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@@ -866,7 +870,7 @@ public class TestResponseCachingPolicy {
@Test
public void otherStatusCodesAreCacheableWithExplicitCachingHeaders() {
response.setCode(HttpStatus.SC_NOT_FOUND);
- response.setHeader("Date", DateUtils.formatDate(now));
+ response.setHeader("Date", formatDate(now));
response.setHeader("Cache-Control","max-age=300");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestWarningValue.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestWarningValue.java
index d53c055e4..de85b0ee9 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestWarningValue.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestWarningValue.java
@@ -26,7 +26,7 @@
*/
package org.apache.hc.client5.http.impl.cache;
-import java.util.Date;
+import java.time.Instant;
import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.Header;
@@ -203,8 +203,8 @@ public class TestWarningValue {
Assert.assertEquals(110, impl.getWarnCode());
Assert.assertEquals("fred", impl.getWarnAgent());
Assert.assertEquals("\"stale\"", impl.getWarnText());
- final Date target = DateUtils.parseDate("Sun Nov 6 08:49:37 1994");
- Assert.assertEquals(target, impl.getWarnDate());
+ final Instant target = DateUtils.parseStandardDate("Sun Nov 6 08:49:37 1994");
+ Assert.assertEquals(target, DateUtils.toInstant(impl.getWarnDate()));
}
@Test
@@ -213,8 +213,8 @@ public class TestWarningValue {
Assert.assertEquals(110, impl.getWarnCode());
Assert.assertEquals("fred", impl.getWarnAgent());
Assert.assertEquals("\"stale\"", impl.getWarnText());
- final Date target = DateUtils.parseDate("Sunday, 06-Nov-94 08:49:37 GMT");
- Assert.assertEquals(target, impl.getWarnDate());
+ final Instant target = DateUtils.parseStandardDate("Sunday, 06-Nov-94 08:49:37 GMT");
+ Assert.assertEquals(target, DateUtils.toInstant(impl.getWarnDate()));
}
@Test
@@ -223,8 +223,8 @@ public class TestWarningValue {
Assert.assertEquals(110, impl.getWarnCode());
Assert.assertEquals("fred", impl.getWarnAgent());
Assert.assertEquals("\"stale\"", impl.getWarnText());
- final Date target = DateUtils.parseDate("Sun, 06 Nov 1994 08:49:37 GMT");
- Assert.assertEquals(target, impl.getWarnDate());
+ final Instant target = DateUtils.parseStandardDate("Sun, 06 Nov 1994 08:49:37 GMT");
+ Assert.assertEquals(target, DateUtils.toInstant(impl.getWarnDate()));
}
}
diff --git a/httpclient5-fluent/src/main/java/org/apache/hc/client5/http/fluent/Request.java b/httpclient5-fluent/src/main/java/org/apache/hc/client5/http/fluent/Request.java
index 52fa673f5..ea4b09461 100644
--- a/httpclient5-fluent/src/main/java/org/apache/hc/client5/http/fluent/Request.java
+++ b/httpclient5-fluent/src/main/java/org/apache/hc/client5/http/fluent/Request.java
@@ -33,7 +33,6 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
@@ -46,6 +45,7 @@ import org.apache.hc.client5.http.config.Configurable;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ContentType;
@@ -70,8 +70,20 @@ import org.apache.hc.core5.util.Timeout;
*/
public class Request {
+ /**
+ * @deprecated This attribute is no longer supported as a part of the public API.
+ */
+ @Deprecated
public static final String DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
+ /**
+ * @deprecated This attribute is no longer supported as a part of the public API.
+ */
+ @Deprecated
public static final Locale DATE_LOCALE = Locale.US;
+ /**
+ * @deprecated This attribute is no longer supported as a part of the public API.
+ */
+ @Deprecated
public static final TimeZone TIME_ZONE = TimeZone.getTimeZone("GMT");
private final ClassicHttpRequest request;
@@ -80,8 +92,6 @@ public class Request {
private Timeout responseTimeout;
private HttpHost proxy;
- private SimpleDateFormat dateFormatter;
-
public static Request create(final Method method, final URI uri) {
return new Request(new HttpUriRequestBase(method.name(), uri));
}
@@ -246,30 +256,22 @@ public class Request {
return this;
}
- private SimpleDateFormat getDateFormat() {
- if (this.dateFormatter == null) {
- this.dateFormatter = new SimpleDateFormat(DATE_FORMAT, DATE_LOCALE);
- this.dateFormatter.setTimeZone(TIME_ZONE);
- }
- return this.dateFormatter;
- }
-
ClassicHttpRequest getRequest() {
return request;
}
public Request setDate(final Date date) {
- this.request.setHeader(HttpHeader.DATE, getDateFormat().format(date));
+ this.request.setHeader(HttpHeader.DATE, DateUtils.formatStandardDate(DateUtils.toInstant(date)));
return this;
}
public Request setIfModifiedSince(final Date date) {
- this.request.setHeader(HttpHeader.IF_MODIFIED_SINCE, getDateFormat().format(date));
+ this.request.setHeader(HttpHeader.IF_MODIFIED_SINCE, DateUtils.formatStandardDate(DateUtils.toInstant(date)));
return this;
}
public Request setIfUnmodifiedSince(final Date date) {
- this.request.setHeader(HttpHeader.IF_UNMODIFIED_SINCE, getDateFormat().format(date));
+ this.request.setHeader(HttpHeader.IF_UNMODIFIED_SINCE, DateUtils.formatStandardDate(DateUtils.toInstant(date)));
return this;
}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultHttpRequestRetryStrategy.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultHttpRequestRetryStrategy.java
index b1be581ca..a50182bfd 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultHttpRequestRetryStrategy.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultHttpRequestRetryStrategy.java
@@ -32,9 +32,9 @@ import java.io.InterruptedIOException;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.net.UnknownHostException;
+import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Date;
import java.util.HashSet;
import java.util.Set;
@@ -214,10 +214,10 @@ public class DefaultHttpRequestRetryStrategy implements HttpRequestRetryStrategy
try {
retryAfter = TimeValue.ofSeconds(Long.parseLong(value));
} catch (final NumberFormatException ignore) {
- final Date retryAfterDate = DateUtils.parseDate(value);
+ final Instant retryAfterDate = DateUtils.parseStandardDate(value);
if (retryAfterDate != null) {
retryAfter =
- TimeValue.ofMilliseconds(retryAfterDate.getTime() - System.currentTimeMillis());
+ TimeValue.ofMilliseconds(retryAfterDate.toEpochMilli() - System.currentTimeMillis());
}
}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/BasicExpiresHandler.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/BasicExpiresHandler.java
index 25d338574..a3c202805 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/BasicExpiresHandler.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/BasicExpiresHandler.java
@@ -26,7 +26,9 @@
*/
package org.apache.hc.client5.http.impl.cookie;
-import java.util.Date;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler;
import org.apache.hc.client5.http.cookie.Cookie;
@@ -46,11 +48,30 @@ import org.apache.hc.core5.util.Args;
public class BasicExpiresHandler extends AbstractCookieAttributeHandler implements CommonCookieAttributeHandler {
/** Valid date patterns */
- private final String[] datePatterns;
+ private final DateTimeFormatter[] datePatterns;
+ /**
+ * @since 5.2
+ */
+ public BasicExpiresHandler(final DateTimeFormatter... datePatterns) {
+ this.datePatterns = datePatterns;
+ }
+
+ /**
+ * @deprecated Use {@link #BasicExpiresHandler(DateTimeFormatter...)}
+ */
+ @Deprecated
public BasicExpiresHandler(final String[] datePatterns) {
Args.notNull(datePatterns, "Array of date patterns");
- this.datePatterns = datePatterns.clone();
+ this.datePatterns = new DateTimeFormatter[datePatterns.length];
+ for (int i = 0; i < datePatterns.length; i++) {
+ this.datePatterns[i] = new DateTimeFormatterBuilder()
+ .parseLenient()
+ .parseCaseInsensitive()
+ .appendPattern(datePatterns[i])
+ .toFormatter();
+ }
+
}
@Override
@@ -60,12 +81,12 @@ public class BasicExpiresHandler extends AbstractCookieAttributeHandler implemen
if (value == null) {
throw new MalformedCookieException("Missing value for 'expires' attribute");
}
- final Date expiry = DateUtils.parseDate(value, this.datePatterns);
+ final Instant expiry = DateUtils.parseDate(value, this.datePatterns);
if (expiry == null) {
throw new MalformedCookieException("Invalid 'expires' attribute: "
+ value);
}
- cookie.setExpiryDate(expiry);
+ cookie.setExpiryDate(DateUtils.toDate(expiry));
}
@Override
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/LaxExpiresHandler.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/LaxExpiresHandler.java
index 594a6714e..fcca1c3f0 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/LaxExpiresHandler.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/LaxExpiresHandler.java
@@ -26,11 +26,13 @@
*/
package org.apache.hc.client5.http.impl.cookie;
+import java.time.Instant;
+import java.time.Month;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.util.BitSet;
-import java.util.Calendar;
import java.util.Locale;
import java.util.Map;
-import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -39,6 +41,7 @@ import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler;
import org.apache.hc.client5.http.cookie.Cookie;
import org.apache.hc.client5.http.cookie.MalformedCookieException;
import org.apache.hc.client5.http.cookie.SetCookie;
+import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.util.Args;
@@ -54,8 +57,6 @@ import org.apache.hc.core5.util.Tokenizer;
@Contract(threading = ThreadingBehavior.STATELESS)
public class LaxExpiresHandler extends AbstractCookieAttributeHandler implements CommonCookieAttributeHandler {
- static final TimeZone UTC = TimeZone.getTimeZone("UTC");
-
private static final BitSet DELIMS;
static {
final BitSet bitSet = new BitSet();
@@ -74,21 +75,21 @@ public class LaxExpiresHandler extends AbstractCookieAttributeHandler implements
}
DELIMS = bitSet;
}
- private static final Map MONTHS;
+ private static final Map MONTHS;
static {
- final ConcurrentHashMap map = new ConcurrentHashMap<>(12);
- map.put("jan", Calendar.JANUARY);
- map.put("feb", Calendar.FEBRUARY);
- map.put("mar", Calendar.MARCH);
- map.put("apr", Calendar.APRIL);
- map.put("may", Calendar.MAY);
- map.put("jun", Calendar.JUNE);
- map.put("jul", Calendar.JULY);
- map.put("aug", Calendar.AUGUST);
- map.put("sep", Calendar.SEPTEMBER);
- map.put("oct", Calendar.OCTOBER);
- map.put("nov", Calendar.NOVEMBER);
- map.put("dec", Calendar.DECEMBER);
+ final ConcurrentHashMap map = new ConcurrentHashMap<>(12);
+ map.put("jan", Month.JANUARY);
+ map.put("feb", Month.FEBRUARY);
+ map.put("mar", Month.MARCH);
+ map.put("apr", Month.APRIL);
+ map.put("may", Month.MAY);
+ map.put("jun", Month.JUNE);
+ map.put("jul", Month.JULY);
+ map.put("aug", Month.AUGUST);
+ map.put("sep", Month.SEPTEMBER);
+ map.put("oct", Month.OCTOBER);
+ map.put("nov", Month.NOVEMBER);
+ map.put("dec", Month.DECEMBER);
MONTHS = map;
}
@@ -114,7 +115,8 @@ public class LaxExpiresHandler extends AbstractCookieAttributeHandler implements
final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, value.length());
final StringBuilder content = new StringBuilder();
- int second = 0, minute = 0, hour = 0, day = 0, month = 0, year = 0;
+ int second = 0, minute = 0, hour = 0, day = 0, year = 0;
+ Month month = Month.JANUARY;
boolean foundTime = false, foundDayOfMonth = false, foundMonth = false, foundYear = false;
try {
while (!cursor.atEnd()) {
@@ -147,7 +149,7 @@ public class LaxExpiresHandler extends AbstractCookieAttributeHandler implements
final Matcher matcher = MONTH_PATTERN.matcher(content);
if (matcher.matches()) {
foundMonth = true;
- month = MONTHS.get(matcher.group(1).toLowerCase(Locale.ROOT)).intValue();
+ month = MONTHS.get(matcher.group(1).toLowerCase(Locale.ROOT));
continue;
}
}
@@ -176,16 +178,9 @@ public class LaxExpiresHandler extends AbstractCookieAttributeHandler implements
throw new MalformedCookieException("Invalid 'expires' attribute: " + value);
}
- final Calendar c = Calendar.getInstance();
- c.setTimeZone(UTC);
- c.setTimeInMillis(0L);
- c.set(Calendar.SECOND, second);
- c.set(Calendar.MINUTE, minute);
- c.set(Calendar.HOUR_OF_DAY, hour);
- c.set(Calendar.DAY_OF_MONTH, day);
- c.set(Calendar.MONTH, month);
- c.set(Calendar.YEAR, year);
- cookie.setExpiryDate(c.getTime());
+ final Instant expiryDate = ZonedDateTime.of(year, month.getValue(), day, hour, minute, second, 0,
+ ZoneId.of("UTC")).toInstant();
+ cookie.setExpiryDate(DateUtils.toDate(expiryDate));
}
private void skipDelims(final CharSequence buf, final Tokenizer.Cursor cursor) {
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265CookieSpecFactory.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265CookieSpecFactory.java
index e213aaa8f..93575c25a 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265CookieSpecFactory.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265CookieSpecFactory.java
@@ -33,6 +33,7 @@ import org.apache.hc.client5.http.cookie.CookieSpec;
import org.apache.hc.client5.http.cookie.CookieSpecFactory;
import org.apache.hc.client5.http.cookie.MalformedCookieException;
import org.apache.hc.client5.http.psl.PublicSuffixMatcher;
+import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.http.protocol.HttpContext;
@@ -88,7 +89,7 @@ public class RFC6265CookieSpecFactory implements CookieSpecFactory {
new BasicMaxAgeHandler(),
new BasicSecureHandler(),
new BasicHttpOnlyHandler(),
- new BasicExpiresHandler(RFC6265StrictSpec.DATE_PATTERNS));
+ new BasicExpiresHandler(DateUtils.STANDARD_PATTERNS));
break;
case IE_MEDIUM_SECURITY:
this.cookieSpec = new RFC6265LaxSpec(
@@ -105,7 +106,7 @@ public class RFC6265CookieSpecFactory implements CookieSpecFactory {
new BasicMaxAgeHandler(),
new BasicSecureHandler(),
new BasicHttpOnlyHandler(),
- new BasicExpiresHandler(RFC6265StrictSpec.DATE_PATTERNS));
+ new BasicExpiresHandler(DateUtils.STANDARD_PATTERNS));
break;
default:
this.cookieSpec = new RFC6265LaxSpec(
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265StrictSpec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265StrictSpec.java
index 262fb955c..b155ba993 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265StrictSpec.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265StrictSpec.java
@@ -42,19 +42,13 @@ import org.apache.hc.core5.annotation.ThreadingBehavior;
@Contract(threading = ThreadingBehavior.SAFE)
public class RFC6265StrictSpec extends RFC6265CookieSpecBase {
- final static String[] DATE_PATTERNS = {
- DateUtils.PATTERN_RFC1123,
- DateUtils.PATTERN_RFC1036,
- DateUtils.PATTERN_ASCTIME
- };
-
public RFC6265StrictSpec() {
super(new BasicPathHandler(),
new BasicDomainHandler(),
new BasicMaxAgeHandler(),
new BasicSecureHandler(),
new BasicHttpOnlyHandler(),
- new BasicExpiresHandler(DATE_PATTERNS));
+ new BasicExpiresHandler(DateUtils.STANDARD_PATTERNS));
}
RFC6265StrictSpec(final CommonCookieAttributeHandler... handlers) {
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/utils/DateUtils.java b/httpclient5/src/main/java/org/apache/hc/client5/http/utils/DateUtils.java
index d59c6d3c1..e5d747e9a 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/utils/DateUtils.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/utils/DateUtils.java
@@ -27,14 +27,14 @@
package org.apache.hc.client5.http.utils;
-import java.lang.ref.SoftReference;
-import java.text.ParsePosition;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.DateTimeParseException;
import java.util.Date;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
import java.util.TimeZone;
import org.apache.hc.core5.http.Header;
@@ -55,35 +55,192 @@ public final class DateUtils {
*/
public static final String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz";
+ /**
+ * Date formatter used to parse HTTP date headers in RFC 1123 format.
+ *
+ * @since 5.2
+ */
+ public static final DateTimeFormatter FORMATTER_RFC1123 = new DateTimeFormatterBuilder()
+ .parseLenient()
+ .parseCaseInsensitive()
+ .appendPattern(PATTERN_RFC1123)
+ .toFormatter();
+
/**
* Date format pattern used to parse HTTP date headers in RFC 1036 format.
*/
public static final String PATTERN_RFC1036 = "EEE, dd-MMM-yy HH:mm:ss zzz";
+ /**
+ * Date formatter used to parse HTTP date headers in RFC 1036 format.
+ *
+ * @since 5.2
+ */
+ public static final DateTimeFormatter FORMATTER_RFC1036 = new DateTimeFormatterBuilder()
+ .parseLenient()
+ .parseCaseInsensitive()
+ .appendPattern(PATTERN_RFC1036)
+ .toFormatter();
+
/**
* Date format pattern used to parse HTTP date headers in ANSI C
* {@code asctime()} format.
*/
public static final String PATTERN_ASCTIME = "EEE MMM d HH:mm:ss yyyy";
- private static final String[] DEFAULT_PATTERNS = new String[] {
- PATTERN_RFC1123,
- PATTERN_RFC1036,
- PATTERN_ASCTIME
+ /**
+ * Date formatter used to parse HTTP date headers in in ANSI C {@code asctime()} format.
+ *
+ * @since 5.2
+ */
+ public static final DateTimeFormatter FORMATTER_ASCTIME = new DateTimeFormatterBuilder()
+ .parseLenient()
+ .parseCaseInsensitive()
+ .appendPattern(PATTERN_ASCTIME)
+ .toFormatter();
+
+ /**
+ * Standard date formatters: {@link #FORMATTER_RFC1123}, {@link #FORMATTER_RFC1036}, {@link #FORMATTER_ASCTIME}.
+ *
+ * @since 5.2
+ */
+ public static final DateTimeFormatter[] STANDARD_PATTERNS = new DateTimeFormatter[] {
+ FORMATTER_RFC1123,
+ FORMATTER_RFC1036,
+ FORMATTER_ASCTIME
};
- private static final Date DEFAULT_TWO_DIGIT_YEAR_START;
+ static final ZoneId GMT_ID = ZoneId.of("GMT");
- public static final TimeZone GMT = TimeZone.getTimeZone("GMT");
-
- static {
- final Calendar calendar = Calendar.getInstance();
- calendar.setTimeZone(GMT);
- calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
- calendar.set(Calendar.MILLISECOND, 0);
- DEFAULT_TWO_DIGIT_YEAR_START = calendar.getTime();
+ /**
+ * @since 5.2
+ */
+ public static Date toDate(final Instant instant) {
+ return instant != null ? new Date(instant.toEpochMilli()) : null;
}
+ /**
+ * @since 5.2
+ */
+ public static Instant toInstant(final Date date) {
+ return date != null ? Instant.ofEpochMilli(date.getTime()) : null;
+ }
+
+ /**
+ * @since 5.2
+ */
+ public static LocalDateTime toUTC(final Instant instant) {
+ return instant != null ? instant.atZone(ZoneOffset.UTC).toLocalDateTime() : null;
+ }
+
+ /**
+ * @since 5.2
+ */
+ public static LocalDateTime toUTC(final Date date) {
+ return toUTC(toInstant(date));
+ }
+
+ /**
+ * Parses the date value using the given date/time formats.
+ *
+ * @param dateValue the instant value to parse
+ * @param dateFormatters the date/time formats to use
+ *
+ * @return the parsed instant or null if input could not be parsed
+ *
+ * @since 5.2
+ */
+ public static Instant parseDate(final String dateValue, final DateTimeFormatter... dateFormatters) {
+ Args.notNull(dateValue, "Date value");
+ String v = dateValue;
+ // trim single quotes around date if present
+ // see issue #5279
+ if (v.length() > 1 && v.startsWith("'") && v.endsWith("'")) {
+ v = v.substring (1, v.length() - 1);
+ }
+
+ for (final DateTimeFormatter dateFormatter : dateFormatters) {
+ try {
+ return Instant.from(dateFormatter.parse(v));
+ } catch (final DateTimeParseException ignore) {
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Parses the instant value using the standard date/time formats ({@link #PATTERN_RFC1123},
+ * {@link #PATTERN_RFC1036}, {@link #PATTERN_ASCTIME}).
+ *
+ * @param dateValue the instant value to parse
+ * @param dateFormatters the date/time formats to use
+ *
+ * @return the parsed instant or null if input could not be parsed
+ *
+ * @since 5.2
+ */
+ public static Instant parseStandardDate(final String dateValue) {
+ return parseDate(dateValue, STANDARD_PATTERNS);
+ }
+
+ /**
+ * Parses an instant value from a header with the given name.
+ *
+ * @param headers message headers
+ * @param headerName header name
+ *
+ * @return the parsed instant or null if input could not be parsed
+ *
+ * @since 5.2
+ */
+ public static Instant parseStandardDate(final MessageHeaders headers, final String headerName) {
+ if (headers == null) {
+ return null;
+ }
+ final Header header = headers.getFirstHeader(headerName);
+ if (header == null) {
+ return null;
+ }
+ return parseStandardDate(header.getValue());
+ }
+
+ /**
+ * Formats the given instant according to the RFC 1123 pattern.
+ *
+ * @param instant Instant to format.
+ * @return An RFC 1123 formatted instant string.
+ *
+ * @see #PATTERN_RFC1123
+ *
+ * @since 5.2
+ */
+ public static String formatStandardDate(final Instant instant) {
+ return formatDate(instant, FORMATTER_RFC1123);
+ }
+
+ /**
+ * Formats the given date according to the specified pattern.
+ *
+ * @param instant Instant to format.
+ * @param dateTimeFormatter The pattern to use for formatting the instant.
+ * @return A formatted instant string.
+ *
+ * @throws IllegalArgumentException If the given date pattern is invalid.
+ *
+ * @since 5.2
+ */
+ public static String formatDate(final Instant instant, final DateTimeFormatter dateTimeFormatter) {
+ Args.notNull(instant, "Instant");
+ Args.notNull(dateTimeFormatter, "DateTimeFormatter");
+ return dateTimeFormatter.format(instant.atZone(GMT_ID));
+ }
+
+ /**
+ * @deprecated This attribute is no longer supported as a part of the public API.
+ */
+ @Deprecated
+ public static final TimeZone GMT = TimeZone.getTimeZone("GMT");
+
/**
* Parses a date value. The formats used for parsing the date value are retrieved from
* the default http params.
@@ -91,7 +248,10 @@ public final class DateUtils {
* @param dateValue the date value to parse
*
* @return the parsed date or null if input could not be parsed
+ *
+ * @deprecated Use {@link #parseStandardDate(String)}
*/
+ @Deprecated
public static Date parseDate(final String dateValue) {
return parseDate(dateValue, null, null);
}
@@ -105,16 +265,12 @@ public final class DateUtils {
* @return the parsed date or null if input could not be parsed
*
* @since 5.0
+ *
+ * @deprecated Use {@link #parseStandardDate(MessageHeaders, String)}
*/
+ @Deprecated
public static Date parseDate(final MessageHeaders headers, final String headerName) {
- if (headers == null) {
- return null;
- }
- final Header header = headers.getFirstHeader(headerName);
- if (header == null) {
- return null;
- }
- return parseDate(header.getValue(), null, null);
+ return toDate(parseStandardDate(headers, headerName));
}
/**
@@ -130,7 +286,10 @@ public final class DateUtils {
* the second message.
*
* @since 5.0
+ *
+ * @deprecated This method is no longer supported as a part of the public API.
*/
+ @Deprecated
public static boolean isAfter(
final MessageHeaders message1,
final MessageHeaders message2,
@@ -166,7 +325,10 @@ public final class DateUtils {
* the second message.
*
* @since 5.0
+ *
+ * @deprecated This method is no longer supported as a part of the public API.
*/
+ @Deprecated
public static boolean isBefore(
final MessageHeaders message1,
final MessageHeaders message2,
@@ -190,53 +352,53 @@ public final class DateUtils {
}
/**
- * Parses the date value using the given date formats.
+ * Parses the date value using the given date/time formats.
*
* @param dateValue the date value to parse
- * @param dateFormats the date formats to use
+ * @param dateFormats the date/time formats to use
*
* @return the parsed date or null if input could not be parsed
+ *
+ * @deprecated Use {@link #parseDate(String, DateTimeFormatter...)}
*/
+ @Deprecated
public static Date parseDate(final String dateValue, final String[] dateFormats) {
return parseDate(dateValue, dateFormats, null);
}
/**
- * Parses the date value using the given date formats.
+ * Parses the date value using the given date/time formats.
*
* @param dateValue the date value to parse
- * @param dateFormats the date formats to use
+ * @param dateFormats the date/time formats to use
* @param startDate During parsing, two digit years will be placed in the range
* {@code startDate} to {@code startDate + 100 years}. This value may
* be {@code null}. When {@code null} is given as a parameter, year
* {@code 2000} will be used.
*
* @return the parsed date or null if input could not be parsed
+ *
+ * @deprecated Use {@link #parseDate(String, DateTimeFormatter...)}
*/
+ @Deprecated
public static Date parseDate(
final String dateValue,
final String[] dateFormats,
final Date startDate) {
- Args.notNull(dateValue, "Date value");
- final String[] localDateFormats = dateFormats != null ? dateFormats : DEFAULT_PATTERNS;
- final Date localStartDate = startDate != null ? startDate : DEFAULT_TWO_DIGIT_YEAR_START;
- String v = dateValue;
- // trim single quotes around date if present
- // see issue #5279
- if (v.length() > 1 && v.startsWith("'") && v.endsWith("'")) {
- v = v.substring (1, v.length() - 1);
- }
-
- for (final String dateFormat : localDateFormats) {
- final SimpleDateFormat dateParser = DateFormatHolder.formatFor(dateFormat);
- dateParser.set2DigitYearStart(localStartDate);
- final ParsePosition pos = new ParsePosition(0);
- final Date result = dateParser.parse(v, pos);
- if (pos.getIndex() != 0) {
- return result;
+ final DateTimeFormatter[] dateTimeFormatters;
+ if (dateFormats != null) {
+ dateTimeFormatters = new DateTimeFormatter[dateFormats.length];
+ for (int i = 0; i < dateFormats.length; i++) {
+ dateTimeFormatters[i] = new DateTimeFormatterBuilder()
+ .parseLenient()
+ .parseCaseInsensitive()
+ .appendPattern(dateFormats[i])
+ .toFormatter();
}
+ } else {
+ dateTimeFormatters = STANDARD_PATTERNS;
}
- return null;
+ return toDate(parseDate(dateValue, dateTimeFormatters));
}
/**
@@ -246,15 +408,16 @@ public final class DateUtils {
* @return An RFC 1123 formatted date string.
*
* @see #PATTERN_RFC1123
+ *
+ * @deprecated Use {@link #formatStandardDate(Instant)}
*/
+ @Deprecated
public static String formatDate(final Date date) {
- return formatDate(date, PATTERN_RFC1123);
+ return formatStandardDate(toInstant(date));
}
/**
- * Formats the given date according to the specified pattern. The pattern
- * must conform to that used by the {@link SimpleDateFormat simple date
- * format} class.
+ * Formats the given date according to the specified pattern.
*
* @param date The date to format.
* @param pattern The pattern to use for formatting the date.
@@ -262,72 +425,28 @@ public final class DateUtils {
*
* @throws IllegalArgumentException If the given date pattern is invalid.
*
- * @see SimpleDateFormat
+ * @deprecated Use {@link #formatDate(Instant, DateTimeFormatter)}
*/
+ @Deprecated
public static String formatDate(final Date date, final String pattern) {
Args.notNull(date, "Date");
Args.notNull(pattern, "Pattern");
- final SimpleDateFormat formatter = DateFormatHolder.formatFor(pattern);
- return formatter.format(date);
+ return DateTimeFormatter.ofPattern(pattern).format(toInstant(date).atZone(GMT_ID));
}
/**
* Clears thread-local variable containing {@link java.text.DateFormat} cache.
*
* @since 4.3
+ *
+ * @deprecated Noop method. Do not use.
*/
+ @Deprecated
public static void clearThreadLocal() {
- DateFormatHolder.clearThreadLocal();
}
/** This class should not be instantiated. */
private DateUtils() {
}
- /**
- * A factory for {@link SimpleDateFormat}s. The instances are stored in a
- * threadlocal way because SimpleDateFormat is not threadsafe as noted in
- * {@link SimpleDateFormat its javadoc}.
- *
- */
- final static class DateFormatHolder {
-
- private static final ThreadLocal>> THREADLOCAL_FORMATS = new ThreadLocal<>();
-
- /**
- * creates a {@link SimpleDateFormat} for the requested format string.
- *
- * @param pattern
- * a non-{@code null} format String according to
- * {@link SimpleDateFormat}. The format is not checked against
- * {@code null} since all paths go through
- * {@link DateUtils}.
- * @return the requested format. This simple dateformat should not be used
- * to {@link SimpleDateFormat#applyPattern(String) apply} to a
- * different pattern.
- */
- public static SimpleDateFormat formatFor(final String pattern) {
- final SoftReference