mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2025-09-12 03:39:42 +02:00
Rework XMPP Date/Time related code
- Fix "packet.Time is not thread-safe" (SMACK-543) - Update packet.Time to XEP-0202 Add SDM.supportsFeature(), since this is a pattern that repeats over and over again in Smack. Also add abstract Manager class, that takes care of the weak reference to Connection, as this is also a repeating pattern in Smack.
This commit is contained in:
parent
768700b301
commit
585e20e93e
21 changed files with 904 additions and 678 deletions
32
core/src/main/java/org/jivesoftware/smack/Manager.java
Normal file
32
core/src/main/java/org/jivesoftware/smack/Manager.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Florian Schmaus
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public abstract class Manager {
|
||||
|
||||
final WeakReference<Connection> weakConnection;
|
||||
|
||||
public Manager(Connection connection) {
|
||||
weakConnection = new WeakReference<Connection>(connection);
|
||||
}
|
||||
|
||||
protected final Connection connection() {
|
||||
return weakConnection.get();
|
||||
}
|
||||
}
|
|
@ -196,7 +196,7 @@ public final class ProviderManager {
|
|||
IQ.class.isAssignableFrom((Class<?>)provider))))
|
||||
{
|
||||
throw new IllegalArgumentException("Provider must be an IQProvider " +
|
||||
"or a Class instance.");
|
||||
"or a Class instance sublcassing IQ.");
|
||||
}
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
iqProviders.put(key, provider);
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2013 Robin Collier.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
package org.jivesoftware.smack.util;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
/**
|
||||
* Defines the various date and time profiles used in XMPP along with their associated formats.
|
||||
*
|
||||
* @author Robin Collier
|
||||
*
|
||||
*/
|
||||
public enum DateFormatType {
|
||||
// @formatter:off
|
||||
XEP_0082_DATE_PROFILE("yyyy-MM-dd"),
|
||||
XEP_0082_DATETIME_PROFILE("yyyy-MM-dd'T'HH:mm:ssZ"),
|
||||
XEP_0082_DATETIME_MILLIS_PROFILE("yyyy-MM-dd'T'HH:mm:ss.SSSZ"),
|
||||
XEP_0082_TIME_PROFILE("hh:mm:ss"),
|
||||
XEP_0082_TIME_ZONE_PROFILE("hh:mm:ssZ"),
|
||||
XEP_0082_TIME_MILLIS_PROFILE("hh:mm:ss.SSS"),
|
||||
XEP_0082_TIME_MILLIS_ZONE_PROFILE("hh:mm:ss.SSSZ"),
|
||||
XEP_0091_DATETIME("yyyyMMdd'T'HH:mm:ss");
|
||||
// @formatter:on
|
||||
|
||||
private String formatString;
|
||||
|
||||
private DateFormatType(String dateFormat) {
|
||||
formatString = dateFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the format string as defined in either XEP-0082 or XEP-0091.
|
||||
*
|
||||
* @return The defined string format for the date.
|
||||
*/
|
||||
public String getFormatString() {
|
||||
return formatString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link SimpleDateFormat} object with the format defined by {@link #getFormatString()}.
|
||||
*
|
||||
* @return A new date formatter.
|
||||
*/
|
||||
public SimpleDateFormat createFormatter() {
|
||||
return new SimpleDateFormat(getFormatString());
|
||||
}
|
||||
}
|
|
@ -341,9 +341,9 @@ public class PacketParserUtils {
|
|||
// Decide what to do when an IQ packet was not understood
|
||||
if (iqPacket == null) {
|
||||
if (IQ.Type.GET == type || IQ.Type.SET == type ) {
|
||||
// If the IQ stanza is of type "get" or "set" containing a child element
|
||||
// qualified by a namespace it does not understand, then answer an IQ of
|
||||
// type "error" with code 501 ("feature-not-implemented")
|
||||
// If the IQ stanza is of type "get" or "set" containing a child element qualified
|
||||
// by a namespace with no registered Smack provider, then answer an IQ of type
|
||||
// "error" with code 501 ("feature-not-implemented")
|
||||
iqPacket = new IQ() {
|
||||
@Override
|
||||
public String getChildElementXML() {
|
||||
|
@ -848,7 +848,7 @@ public class PacketParserUtils {
|
|||
}
|
||||
}
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a String into an object of the specified type. If the object
|
||||
|
|
|
@ -20,94 +20,15 @@ package org.jivesoftware.smack.util;
|
|||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.TimeZone;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A collection of utility methods for String objects.
|
||||
*/
|
||||
public class StringUtils {
|
||||
private static final Logger LOGGER = Logger.getLogger(StringUtils.class.getName());
|
||||
|
||||
/**
|
||||
* Date format as defined in XEP-0082 - XMPP Date and Time Profiles. The time zone is set to
|
||||
* UTC.
|
||||
* <p>
|
||||
* Date formats are not synchronized. Since multiple threads access the format concurrently, it
|
||||
* must be synchronized externally or you can use the convenience methods
|
||||
* {@link #parseXEP0082Date(String)} and {@link #formatXEP0082Date(Date)}.
|
||||
* @deprecated This public version will be removed in favor of using the methods defined within this class.
|
||||
*/
|
||||
public static final DateFormat XEP_0082_UTC_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
|
||||
|
||||
/*
|
||||
* private version to use internally so we don't have to be concerned with thread safety.
|
||||
*/
|
||||
private static final DateFormat dateFormatter = DateFormatType.XEP_0082_DATE_PROFILE.createFormatter();
|
||||
private static final Pattern datePattern = Pattern.compile("^\\d+-\\d+-\\d+$");
|
||||
|
||||
private static final DateFormat timeFormatter = DateFormatType.XEP_0082_TIME_MILLIS_ZONE_PROFILE.createFormatter();
|
||||
private static final Pattern timePattern = Pattern.compile("^(\\d+:){2}\\d+.\\d+(Z|([+-](\\d+:\\d+)))$");
|
||||
private static final DateFormat timeNoZoneFormatter = DateFormatType.XEP_0082_TIME_MILLIS_PROFILE.createFormatter();
|
||||
private static final Pattern timeNoZonePattern = Pattern.compile("^(\\d+:){2}\\d+.\\d+$");
|
||||
|
||||
private static final DateFormat timeNoMillisFormatter = DateFormatType.XEP_0082_TIME_ZONE_PROFILE.createFormatter();
|
||||
private static final Pattern timeNoMillisPattern = Pattern.compile("^(\\d+:){2}\\d+(Z|([+-](\\d+:\\d+)))$");
|
||||
private static final DateFormat timeNoMillisNoZoneFormatter = DateFormatType.XEP_0082_TIME_PROFILE.createFormatter();
|
||||
private static final Pattern timeNoMillisNoZonePattern = Pattern.compile("^(\\d+:){2}\\d+$");
|
||||
|
||||
private static final DateFormat dateTimeFormatter = DateFormatType.XEP_0082_DATETIME_MILLIS_PROFILE.createFormatter();
|
||||
private static final Pattern dateTimePattern = Pattern.compile("^\\d+(-\\d+){2}+T(\\d+:){2}\\d+.\\d+(Z|([+-](\\d+:\\d+)))?$");
|
||||
private static final DateFormat dateTimeNoMillisFormatter = DateFormatType.XEP_0082_DATETIME_PROFILE.createFormatter();
|
||||
private static final Pattern dateTimeNoMillisPattern = Pattern.compile("^\\d+(-\\d+){2}+T(\\d+:){2}\\d+(Z|([+-](\\d+:\\d+)))?$");
|
||||
|
||||
private static final DateFormat xep0091Formatter = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
|
||||
private static final DateFormat xep0091Date6DigitFormatter = new SimpleDateFormat("yyyyMd'T'HH:mm:ss");
|
||||
private static final DateFormat xep0091Date7Digit1MonthFormatter = new SimpleDateFormat("yyyyMdd'T'HH:mm:ss");
|
||||
private static final DateFormat xep0091Date7Digit2MonthFormatter = new SimpleDateFormat("yyyyMMd'T'HH:mm:ss");
|
||||
private static final Pattern xep0091Pattern = Pattern.compile("^\\d+T\\d+:\\d+:\\d+$");
|
||||
|
||||
private static final List<PatternCouplings> couplings = new ArrayList<PatternCouplings>();
|
||||
|
||||
static {
|
||||
TimeZone utc = TimeZone.getTimeZone("UTC");
|
||||
XEP_0082_UTC_FORMAT.setTimeZone(utc);
|
||||
dateFormatter.setTimeZone(utc);
|
||||
timeFormatter.setTimeZone(utc);
|
||||
timeNoZoneFormatter.setTimeZone(utc);
|
||||
timeNoMillisFormatter.setTimeZone(utc);
|
||||
timeNoMillisNoZoneFormatter.setTimeZone(utc);
|
||||
dateTimeFormatter.setTimeZone(utc);
|
||||
dateTimeNoMillisFormatter.setTimeZone(utc);
|
||||
|
||||
xep0091Formatter.setTimeZone(utc);
|
||||
xep0091Date6DigitFormatter.setTimeZone(utc);
|
||||
xep0091Date7Digit1MonthFormatter.setTimeZone(utc);
|
||||
xep0091Date7Digit1MonthFormatter.setLenient(false);
|
||||
xep0091Date7Digit2MonthFormatter.setTimeZone(utc);
|
||||
xep0091Date7Digit2MonthFormatter.setLenient(false);
|
||||
|
||||
couplings.add(new PatternCouplings(datePattern, dateFormatter));
|
||||
couplings.add(new PatternCouplings(dateTimePattern, dateTimeFormatter, true));
|
||||
couplings.add(new PatternCouplings(dateTimeNoMillisPattern, dateTimeNoMillisFormatter, true));
|
||||
couplings.add(new PatternCouplings(timePattern, timeFormatter, true));
|
||||
couplings.add(new PatternCouplings(timeNoZonePattern, timeNoZoneFormatter));
|
||||
couplings.add(new PatternCouplings(timeNoMillisPattern, timeNoMillisFormatter, true));
|
||||
couplings.add(new PatternCouplings(timeNoMillisNoZonePattern, timeNoMillisNoZoneFormatter));
|
||||
}
|
||||
|
||||
private static final char[] QUOTE_ENCODE = """.toCharArray();
|
||||
private static final char[] APOS_ENCODE = "'".toCharArray();
|
||||
|
@ -115,161 +36,6 @@ public class StringUtils {
|
|||
private static final char[] LT_ENCODE = "<".toCharArray();
|
||||
private static final char[] GT_ENCODE = ">".toCharArray();
|
||||
|
||||
/**
|
||||
* Parses the given date string in the <a href="http://xmpp.org/extensions/xep-0082.html">XEP-0082 - XMPP Date and Time Profiles</a>.
|
||||
*
|
||||
* @param dateString the date string to parse
|
||||
* @return the parsed Date
|
||||
* @throws ParseException if the specified string cannot be parsed
|
||||
* @deprecated Use {@link #parseDate(String)} instead.
|
||||
*
|
||||
*/
|
||||
public static Date parseXEP0082Date(String dateString) throws ParseException {
|
||||
return parseDate(dateString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given date string in either of the three profiles of <a href="http://xmpp.org/extensions/xep-0082.html">XEP-0082 - XMPP Date and Time Profiles</a>
|
||||
* or <a href="http://xmpp.org/extensions/xep-0091.html">XEP-0091 - Legacy Delayed Delivery</a> format.
|
||||
* <p>
|
||||
* This method uses internal date formatters and is thus threadsafe.
|
||||
* @param dateString the date string to parse
|
||||
* @return the parsed Date
|
||||
* @throws ParseException if the specified string cannot be parsed
|
||||
*/
|
||||
public static Date parseDate(String dateString) throws ParseException {
|
||||
Matcher matcher = xep0091Pattern.matcher(dateString);
|
||||
|
||||
/*
|
||||
* if date is in XEP-0091 format handle ambiguous dates missing the
|
||||
* leading zero in month and day
|
||||
*/
|
||||
if (matcher.matches()) {
|
||||
int length = dateString.split("T")[0].length();
|
||||
|
||||
if (length < 8) {
|
||||
Date date = handleDateWithMissingLeadingZeros(dateString, length);
|
||||
|
||||
if (date != null)
|
||||
return date;
|
||||
}
|
||||
else {
|
||||
synchronized (xep0091Formatter) {
|
||||
return xep0091Formatter.parse(dateString);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (PatternCouplings coupling : couplings) {
|
||||
matcher = coupling.pattern.matcher(dateString);
|
||||
|
||||
if (matcher.matches())
|
||||
{
|
||||
if (coupling.needToConvertTimeZone) {
|
||||
dateString = coupling.convertTime(dateString);
|
||||
}
|
||||
|
||||
synchronized (coupling.formatter) {
|
||||
return coupling.formatter.parse(dateString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We assume it is the XEP-0082 DateTime profile with no milliseconds at this point. If it isn't, is is just not parseable, then we attempt
|
||||
* to parse it regardless and let it throw the ParseException.
|
||||
*/
|
||||
synchronized (dateTimeNoMillisFormatter) {
|
||||
return dateTimeNoMillisFormatter.parse(dateString);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given date string in different ways and returns the date that
|
||||
* lies in the past and/or is nearest to the current date-time.
|
||||
*
|
||||
* @param stampString date in string representation
|
||||
* @param dateLength
|
||||
* @param noFuture
|
||||
* @return the parsed date
|
||||
* @throws ParseException The date string was of an unknown format
|
||||
*/
|
||||
private static Date handleDateWithMissingLeadingZeros(String stampString, int dateLength) throws ParseException {
|
||||
if (dateLength == 6) {
|
||||
synchronized (xep0091Date6DigitFormatter) {
|
||||
return xep0091Date6DigitFormatter.parse(stampString);
|
||||
}
|
||||
}
|
||||
Calendar now = Calendar.getInstance();
|
||||
|
||||
Calendar oneDigitMonth = parseXEP91Date(stampString, xep0091Date7Digit1MonthFormatter);
|
||||
Calendar twoDigitMonth = parseXEP91Date(stampString, xep0091Date7Digit2MonthFormatter);
|
||||
|
||||
List<Calendar> dates = filterDatesBefore(now, oneDigitMonth, twoDigitMonth);
|
||||
|
||||
if (!dates.isEmpty()) {
|
||||
return determineNearestDate(now, dates).getTime();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Calendar parseXEP91Date(String stampString, DateFormat dateFormat) {
|
||||
try {
|
||||
synchronized (dateFormat) {
|
||||
dateFormat.parse(stampString);
|
||||
return dateFormat.getCalendar();
|
||||
}
|
||||
}
|
||||
catch (ParseException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Calendar> filterDatesBefore(Calendar now, Calendar... dates) {
|
||||
List<Calendar> result = new ArrayList<Calendar>();
|
||||
|
||||
for (Calendar calendar : dates) {
|
||||
if (calendar != null && calendar.before(now)) {
|
||||
result.add(calendar);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Calendar determineNearestDate(final Calendar now, List<Calendar> dates) {
|
||||
|
||||
Collections.sort(dates, new Comparator<Calendar>() {
|
||||
|
||||
public int compare(Calendar o1, Calendar o2) {
|
||||
Long diff1 = new Long(now.getTimeInMillis() - o1.getTimeInMillis());
|
||||
Long diff2 = new Long(now.getTimeInMillis() - o2.getTimeInMillis());
|
||||
return diff1.compareTo(diff2);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return dates.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a Date into a XEP-0082 - XMPP Date and Time Profiles string.
|
||||
*
|
||||
* @param date the time value to be formatted into a time string
|
||||
* @return the formatted time string in XEP-0082 format
|
||||
*/
|
||||
public static String formatXEP0082Date(Date date) {
|
||||
synchronized (dateTimeFormatter) {
|
||||
return dateTimeFormatter.format(date);
|
||||
}
|
||||
}
|
||||
|
||||
public static String formatDate(Date toFormat, DateFormatType type)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name portion of a XMPP address. For example, for the
|
||||
* address "matt@jivesoftware.com/Smack", "matt" would be returned. If no
|
||||
|
@ -804,38 +570,4 @@ public class StringUtils {
|
|||
return new String(randBuffer);
|
||||
}
|
||||
|
||||
private StringUtils() {
|
||||
// Not instantiable.
|
||||
}
|
||||
|
||||
private static class PatternCouplings {
|
||||
Pattern pattern;
|
||||
DateFormat formatter;
|
||||
boolean needToConvertTimeZone = false;
|
||||
|
||||
public PatternCouplings(Pattern datePattern, DateFormat dateFormat) {
|
||||
pattern = datePattern;
|
||||
formatter = dateFormat;
|
||||
}
|
||||
|
||||
public PatternCouplings(Pattern datePattern, DateFormat dateFormat, boolean shouldConvertToRFC822) {
|
||||
pattern = datePattern;
|
||||
formatter = dateFormat;
|
||||
needToConvertTimeZone = shouldConvertToRFC822;
|
||||
}
|
||||
|
||||
public String convertTime(String dateString) {
|
||||
if (dateString.charAt(dateString.length() - 1) == 'Z') {
|
||||
return dateString.replace("Z", "+0000");
|
||||
}
|
||||
else {
|
||||
// If the time zone wasn't specified with 'Z', then it's in
|
||||
// ISO8601 format (i.e. '(+|-)HH:mm')
|
||||
// RFC822 needs a similar format just without the colon (i.e.
|
||||
// '(+|-)HHmm)'), so remove it
|
||||
return dateString.replaceAll("([\\+\\-]\\d\\d):(\\d\\d)","$1$2");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
325
core/src/main/java/org/jivesoftware/smack/util/XmppDateTime.java
Normal file
325
core/src/main/java/org/jivesoftware/smack/util/XmppDateTime.java
Normal file
|
@ -0,0 +1,325 @@
|
|||
/**
|
||||
*
|
||||
* Copyright the original author or authors
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
package org.jivesoftware.smack.util;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class XmppDateTime {
|
||||
|
||||
private static final DateFormatType dateFormatter = DateFormatType.XEP_0082_DATE_PROFILE;
|
||||
private static final Pattern datePattern = Pattern.compile("^\\d+-\\d+-\\d+$");
|
||||
|
||||
private static final DateFormatType timeFormatter = DateFormatType.XEP_0082_TIME_MILLIS_ZONE_PROFILE;
|
||||
private static final Pattern timePattern = Pattern.compile("^(\\d+:){2}\\d+.\\d+(Z|([+-](\\d+:\\d+)))$");
|
||||
private static final DateFormatType timeNoZoneFormatter = DateFormatType.XEP_0082_TIME_MILLIS_PROFILE;
|
||||
private static final Pattern timeNoZonePattern = Pattern.compile("^(\\d+:){2}\\d+.\\d+$");
|
||||
|
||||
private static final DateFormatType timeNoMillisFormatter = DateFormatType.XEP_0082_TIME_ZONE_PROFILE;
|
||||
private static final Pattern timeNoMillisPattern = Pattern.compile("^(\\d+:){2}\\d+(Z|([+-](\\d+:\\d+)))$");
|
||||
private static final DateFormatType timeNoMillisNoZoneFormatter = DateFormatType.XEP_0082_TIME_PROFILE;
|
||||
private static final Pattern timeNoMillisNoZonePattern = Pattern.compile("^(\\d+:){2}\\d+$");
|
||||
|
||||
private static final DateFormatType dateTimeFormatter = DateFormatType.XEP_0082_DATETIME_MILLIS_PROFILE;
|
||||
private static final Pattern dateTimePattern = Pattern.compile("^\\d+(-\\d+){2}+T(\\d+:){2}\\d+.\\d+(Z|([+-](\\d+:\\d+)))?$");
|
||||
private static final DateFormatType dateTimeNoMillisFormatter = DateFormatType.XEP_0082_DATETIME_PROFILE;
|
||||
private static final Pattern dateTimeNoMillisPattern = Pattern.compile("^\\d+(-\\d+){2}+T(\\d+:){2}\\d+(Z|([+-](\\d+:\\d+)))?$");
|
||||
|
||||
private static final DateFormat xep0091Formatter = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
|
||||
private static final DateFormat xep0091Date6DigitFormatter = new SimpleDateFormat(
|
||||
"yyyyMd'T'HH:mm:ss");
|
||||
private static final DateFormat xep0091Date7Digit1MonthFormatter = new SimpleDateFormat(
|
||||
"yyyyMdd'T'HH:mm:ss");
|
||||
private static final DateFormat xep0091Date7Digit2MonthFormatter = new SimpleDateFormat(
|
||||
"yyyyMMd'T'HH:mm:ss");
|
||||
private static final Pattern xep0091Pattern = Pattern.compile("^\\d+T\\d+:\\d+:\\d+$");
|
||||
|
||||
public static enum DateFormatType {
|
||||
// @formatter:off
|
||||
XEP_0082_DATE_PROFILE("yyyy-MM-dd"),
|
||||
XEP_0082_DATETIME_PROFILE("yyyy-MM-dd'T'HH:mm:ssZ"),
|
||||
XEP_0082_DATETIME_MILLIS_PROFILE("yyyy-MM-dd'T'HH:mm:ss.SSSZ"),
|
||||
XEP_0082_TIME_PROFILE("hh:mm:ss"),
|
||||
XEP_0082_TIME_ZONE_PROFILE("hh:mm:ssZ"),
|
||||
XEP_0082_TIME_MILLIS_PROFILE("hh:mm:ss.SSS"),
|
||||
XEP_0082_TIME_MILLIS_ZONE_PROFILE("hh:mm:ss.SSSZ"),
|
||||
XEP_0091_DATETIME("yyyyMMdd'T'HH:mm:ss");
|
||||
// @formatter:on
|
||||
|
||||
private final String FORMAT_STRING;
|
||||
private final DateFormat FORMATTER;
|
||||
private final boolean CONVERT_TIMEZONE;
|
||||
|
||||
private DateFormatType(String dateFormat) {
|
||||
FORMAT_STRING = dateFormat;
|
||||
FORMATTER = new SimpleDateFormat(FORMAT_STRING);
|
||||
FORMATTER.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
CONVERT_TIMEZONE = dateFormat.charAt(dateFormat.length() - 1) == 'Z';
|
||||
}
|
||||
|
||||
public String format(Date date) {
|
||||
String res;
|
||||
synchronized(FORMATTER) {
|
||||
res = FORMATTER.format(date);
|
||||
}
|
||||
if (CONVERT_TIMEZONE) {
|
||||
res = convertRfc822TimezoneToXep82(res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public Date parse(String dateString) throws ParseException {
|
||||
if (CONVERT_TIMEZONE) {
|
||||
dateString = convertXep82TimezoneToRfc822(dateString);
|
||||
}
|
||||
synchronized(FORMATTER) {
|
||||
return FORMATTER.parse(dateString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final List<PatternCouplings> couplings = new ArrayList<PatternCouplings>();
|
||||
|
||||
static {
|
||||
TimeZone utc = TimeZone.getTimeZone("UTC");
|
||||
|
||||
xep0091Formatter.setTimeZone(utc);
|
||||
xep0091Date6DigitFormatter.setTimeZone(utc);
|
||||
xep0091Date7Digit1MonthFormatter.setTimeZone(utc);
|
||||
xep0091Date7Digit1MonthFormatter.setLenient(false);
|
||||
xep0091Date7Digit2MonthFormatter.setTimeZone(utc);
|
||||
xep0091Date7Digit2MonthFormatter.setLenient(false);
|
||||
|
||||
couplings.add(new PatternCouplings(datePattern, dateFormatter));
|
||||
couplings.add(new PatternCouplings(dateTimePattern, dateTimeFormatter));
|
||||
couplings.add(new PatternCouplings(dateTimeNoMillisPattern, dateTimeNoMillisFormatter));
|
||||
couplings.add(new PatternCouplings(timePattern, timeFormatter));
|
||||
couplings.add(new PatternCouplings(timeNoZonePattern, timeNoZoneFormatter));
|
||||
couplings.add(new PatternCouplings(timeNoMillisPattern, timeNoMillisFormatter));
|
||||
couplings.add(new PatternCouplings(timeNoMillisNoZonePattern, timeNoMillisNoZoneFormatter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given date string in the <a
|
||||
* href="http://xmpp.org/extensions/xep-0082.html">XEP-0082 - XMPP Date and Time Profiles</a>.
|
||||
*
|
||||
* @param dateString the date string to parse
|
||||
* @return the parsed Date
|
||||
* @throws ParseException if the specified string cannot be parsed
|
||||
* @deprecated Use {@link #parseDate(String)} instead.
|
||||
*/
|
||||
public static Date parseXEP0082Date(String dateString) throws ParseException {
|
||||
return parseDate(dateString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given date string in either of the three profiles of <a
|
||||
* href="http://xmpp.org/extensions/xep-0082.html">XEP-0082 - XMPP Date and Time Profiles</a> or
|
||||
* <a href="http://xmpp.org/extensions/xep-0091.html">XEP-0091 - Legacy Delayed Delivery</a>
|
||||
* format.
|
||||
* <p>
|
||||
* This method uses internal date formatters and is thus threadsafe.
|
||||
*
|
||||
* @param dateString the date string to parse
|
||||
* @return the parsed Date
|
||||
* @throws ParseException if the specified string cannot be parsed
|
||||
*/
|
||||
public static Date parseDate(String dateString) throws ParseException {
|
||||
Matcher matcher = xep0091Pattern.matcher(dateString);
|
||||
|
||||
/*
|
||||
* if date is in XEP-0091 format handle ambiguous dates missing the leading zero in month
|
||||
* and day
|
||||
*/
|
||||
if (matcher.matches()) {
|
||||
int length = dateString.split("T")[0].length();
|
||||
|
||||
if (length < 8) {
|
||||
Date date = handleDateWithMissingLeadingZeros(dateString, length);
|
||||
|
||||
if (date != null)
|
||||
return date;
|
||||
}
|
||||
else {
|
||||
synchronized (xep0091Formatter) {
|
||||
return xep0091Formatter.parse(dateString);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (PatternCouplings coupling : couplings) {
|
||||
matcher = coupling.pattern.matcher(dateString);
|
||||
|
||||
if (matcher.matches()) {
|
||||
return coupling.formatter.parse(dateString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We assume it is the XEP-0082 DateTime profile with no milliseconds at this point. If it
|
||||
* isn't, is is just not parseable, then we attempt to parse it regardless and let it throw
|
||||
* the ParseException.
|
||||
*/
|
||||
synchronized (dateTimeNoMillisFormatter) {
|
||||
return dateTimeNoMillisFormatter.parse(dateString);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a Date into a XEP-0082 - XMPP Date and Time Profiles string.
|
||||
*
|
||||
* @param date the time value to be formatted into a time string
|
||||
* @return the formatted time string in XEP-0082 format
|
||||
*/
|
||||
public static String formatXEP0082Date(Date date) {
|
||||
synchronized (dateTimeFormatter) {
|
||||
return dateTimeFormatter.format(date);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a XEP-0082 date String's time zone definition into a RFC822 time zone definition.
|
||||
* The major difference is that XEP-0082 uses a smicolon between hours and minutes and RFC822
|
||||
* does not.
|
||||
*
|
||||
* @param dateString
|
||||
* @return
|
||||
*/
|
||||
public static String convertXep82TimezoneToRfc822(String dateString) {
|
||||
if (dateString.charAt(dateString.length() - 1) == 'Z') {
|
||||
return dateString.replace("Z", "+0000");
|
||||
}
|
||||
else {
|
||||
// If the time zone wasn't specified with 'Z', then it's in
|
||||
// ISO8601 format (i.e. '(+|-)HH:mm')
|
||||
// RFC822 needs a similar format just without the colon (i.e.
|
||||
// '(+|-)HHmm)'), so remove it
|
||||
return dateString.replaceAll("([\\+\\-]\\d\\d):(\\d\\d)", "$1$2");
|
||||
}
|
||||
}
|
||||
|
||||
public static String convertRfc822TimezoneToXep82(String dateString) {
|
||||
int length = dateString.length();
|
||||
String res = dateString.substring(0, length -2);
|
||||
res += ':';
|
||||
res += dateString.substring(length - 2, length);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a time zone to the String format as specified in XEP-0082
|
||||
*
|
||||
* @param timeZone
|
||||
* @return
|
||||
*/
|
||||
public static String asString(TimeZone timeZone) {
|
||||
int rawOffset = timeZone.getRawOffset();
|
||||
int hours = rawOffset / (1000*60*60);
|
||||
int minutes = Math.abs((rawOffset / (1000*60)) - (hours * 60));
|
||||
return String.format("%+d:%02d", hours, minutes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given date string in different ways and returns the date that lies in the past
|
||||
* and/or is nearest to the current date-time.
|
||||
*
|
||||
* @param stampString date in string representation
|
||||
* @param dateLength
|
||||
* @param noFuture
|
||||
* @return the parsed date
|
||||
* @throws ParseException The date string was of an unknown format
|
||||
*/
|
||||
private static Date handleDateWithMissingLeadingZeros(String stampString, int dateLength)
|
||||
throws ParseException {
|
||||
if (dateLength == 6) {
|
||||
synchronized (xep0091Date6DigitFormatter) {
|
||||
return xep0091Date6DigitFormatter.parse(stampString);
|
||||
}
|
||||
}
|
||||
Calendar now = Calendar.getInstance();
|
||||
|
||||
Calendar oneDigitMonth = parseXEP91Date(stampString, xep0091Date7Digit1MonthFormatter);
|
||||
Calendar twoDigitMonth = parseXEP91Date(stampString, xep0091Date7Digit2MonthFormatter);
|
||||
|
||||
List<Calendar> dates = filterDatesBefore(now, oneDigitMonth, twoDigitMonth);
|
||||
|
||||
if (!dates.isEmpty()) {
|
||||
return determineNearestDate(now, dates).getTime();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Calendar parseXEP91Date(String stampString, DateFormat dateFormat) {
|
||||
try {
|
||||
synchronized (dateFormat) {
|
||||
dateFormat.parse(stampString);
|
||||
return dateFormat.getCalendar();
|
||||
}
|
||||
}
|
||||
catch (ParseException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Calendar> filterDatesBefore(Calendar now, Calendar... dates) {
|
||||
List<Calendar> result = new ArrayList<Calendar>();
|
||||
|
||||
for (Calendar calendar : dates) {
|
||||
if (calendar != null && calendar.before(now)) {
|
||||
result.add(calendar);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Calendar determineNearestDate(final Calendar now, List<Calendar> dates) {
|
||||
|
||||
Collections.sort(dates, new Comparator<Calendar>() {
|
||||
|
||||
public int compare(Calendar o1, Calendar o2) {
|
||||
Long diff1 = new Long(now.getTimeInMillis() - o1.getTimeInMillis());
|
||||
Long diff2 = new Long(now.getTimeInMillis() - o2.getTimeInMillis());
|
||||
return diff1.compareTo(diff2);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return dates.get(0);
|
||||
}
|
||||
|
||||
private static class PatternCouplings {
|
||||
final Pattern pattern;
|
||||
final DateFormatType formatter;
|
||||
|
||||
public PatternCouplings(Pattern datePattern, DateFormatType dateFormat) {
|
||||
pattern = datePattern;
|
||||
formatter = dateFormat;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,11 +22,6 @@ import static org.junit.Assert.assertNull;
|
|||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
|
@ -216,225 +211,4 @@ public class StringUtilsTest {
|
|||
assertEquals(error, result, StringUtils.parseServer("user@yahoo.myjabber.net/registred"));
|
||||
assertEquals(error, result, StringUtils.parseServer("user@yahoo.myjabber.net"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082Date() throws Exception
|
||||
{
|
||||
Date date = StringUtils.parseDate("1971-07-21");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(1971, cal.get(Calendar.YEAR));
|
||||
assertEquals(6, cal.get(Calendar.MONTH));
|
||||
assertEquals(21, cal.get(Calendar.DAY_OF_MONTH));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082Time() throws Exception
|
||||
{
|
||||
Date date = StringUtils.parseDate("02:56:15");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082TimeUTC() throws Exception
|
||||
{
|
||||
Date date = StringUtils.parseDate("02:56:15Z");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082TimeWithZone() throws Exception
|
||||
{
|
||||
Date date = StringUtils.parseDate("04:40:15+02:30");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(10, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082TimeWithMillis() throws Exception
|
||||
{
|
||||
Date date = StringUtils.parseDate("02:56:15.123");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
assertEquals(123, cal.get(Calendar.MILLISECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082TimeWithMillisUTC() throws Exception
|
||||
{
|
||||
Date date = StringUtils.parseDate("02:56:15.123Z");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
assertEquals(123, cal.get(Calendar.MILLISECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082TimeWithMillisZone() throws Exception
|
||||
{
|
||||
Date date = StringUtils.parseDate("02:56:15.123+01:00");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(1, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
assertEquals(123, cal.get(Calendar.MILLISECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082DateTimeUTC() throws Exception
|
||||
{
|
||||
Date date = StringUtils.parseDate("1971-07-21T02:56:15Z");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(1971, cal.get(Calendar.YEAR));
|
||||
assertEquals(6, cal.get(Calendar.MONTH));
|
||||
assertEquals(21, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(2, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082DateTimeZone() throws Exception
|
||||
{
|
||||
Date date = StringUtils.parseDate("1971-07-21T02:56:15-01:00");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(1971, cal.get(Calendar.YEAR));
|
||||
assertEquals(6, cal.get(Calendar.MONTH));
|
||||
assertEquals(21, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(3, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082DateTimeWithMillisUTC() throws Exception
|
||||
{
|
||||
Date date = StringUtils.parseDate("1971-07-21T02:56:15.123Z");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(1971, cal.get(Calendar.YEAR));
|
||||
assertEquals(6, cal.get(Calendar.MONTH));
|
||||
assertEquals(21, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(2, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
assertEquals(123, cal.get(Calendar.MILLISECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082DateTimeWithMillisZone() throws Exception
|
||||
{
|
||||
Date date = StringUtils.parseDate("1971-07-21T02:56:15.123-01:00");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(1971, cal.get(Calendar.YEAR));
|
||||
assertEquals(6, cal.get(Calendar.MONTH));
|
||||
assertEquals(21, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(3, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
assertEquals(123, cal.get(Calendar.MILLISECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0091() throws Exception
|
||||
{
|
||||
Date date = StringUtils.parseDate("20020910T23:08:25");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2002, cal.get(Calendar.YEAR));
|
||||
assertEquals(8, cal.get(Calendar.MONTH));
|
||||
assertEquals(10, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(23, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(8, cal.get(Calendar.MINUTE));
|
||||
assertEquals(25, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0091NoLeading0() throws Exception
|
||||
{
|
||||
Date date = StringUtils.parseDate("200291T23:08:25");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2002, cal.get(Calendar.YEAR));
|
||||
assertEquals(8, cal.get(Calendar.MONTH));
|
||||
assertEquals(1, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(23, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(8, cal.get(Calendar.MINUTE));
|
||||
assertEquals(25, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0091AmbiguousMonthDay() throws Exception
|
||||
{
|
||||
Date date = StringUtils.parseDate("2002101T23:08:25");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2002, cal.get(Calendar.YEAR));
|
||||
assertEquals(9, cal.get(Calendar.MONTH));
|
||||
assertEquals(1, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(23, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(8, cal.get(Calendar.MINUTE));
|
||||
assertEquals(25, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0091SingleDigitMonth() throws Exception
|
||||
{
|
||||
Date date = StringUtils.parseDate("2002130T23:08:25");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2002, cal.get(Calendar.YEAR));
|
||||
assertEquals(0, cal.get(Calendar.MONTH));
|
||||
assertEquals(30, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(23, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(8, cal.get(Calendar.MINUTE));
|
||||
assertEquals(25, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test (expected=ParseException.class)
|
||||
public void parseNoMonthDay() throws Exception
|
||||
{
|
||||
StringUtils.parseDate("2002T23:08:25");
|
||||
}
|
||||
|
||||
@Test (expected=ParseException.class)
|
||||
public void parseNoYear() throws Exception
|
||||
{
|
||||
StringUtils.parseDate("130T23:08:25");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2003-2007 Jive Software.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
package org.jivesoftware.smack.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class XmppDateTimeTest {
|
||||
@Test
|
||||
public void parseXep0082Date() throws Exception {
|
||||
Date date = XmppDateTime.parseDate("1971-07-21");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(1971, cal.get(Calendar.YEAR));
|
||||
assertEquals(6, cal.get(Calendar.MONTH));
|
||||
assertEquals(21, cal.get(Calendar.DAY_OF_MONTH));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082Time() throws Exception {
|
||||
Date date = XmppDateTime.parseDate("02:56:15");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082TimeUTC() throws Exception {
|
||||
Date date = XmppDateTime.parseDate("02:56:15Z");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082TimeWithZone() throws Exception {
|
||||
Date date = XmppDateTime.parseDate("04:40:15+02:30");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(10, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082TimeWithMillis() throws Exception {
|
||||
Date date = XmppDateTime.parseDate("02:56:15.123");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
assertEquals(123, cal.get(Calendar.MILLISECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082TimeWithMillisUTC() throws Exception {
|
||||
Date date = XmppDateTime.parseDate("02:56:15.123Z");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
assertEquals(123, cal.get(Calendar.MILLISECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082TimeWithMillisZone() throws Exception {
|
||||
Date date = XmppDateTime.parseDate("02:56:15.123+01:00");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(1, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
assertEquals(123, cal.get(Calendar.MILLISECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082DateTimeUTC() throws Exception {
|
||||
Date date = XmppDateTime.parseDate("1971-07-21T02:56:15Z");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(1971, cal.get(Calendar.YEAR));
|
||||
assertEquals(6, cal.get(Calendar.MONTH));
|
||||
assertEquals(21, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(2, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082DateTimeZone() throws Exception {
|
||||
Date date = XmppDateTime.parseDate("1971-07-21T02:56:15-01:00");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(1971, cal.get(Calendar.YEAR));
|
||||
assertEquals(6, cal.get(Calendar.MONTH));
|
||||
assertEquals(21, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(3, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082DateTimeWithMillisUTC() throws Exception {
|
||||
Date date = XmppDateTime.parseDate("1971-07-21T02:56:15.123Z");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(1971, cal.get(Calendar.YEAR));
|
||||
assertEquals(6, cal.get(Calendar.MONTH));
|
||||
assertEquals(21, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(2, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
assertEquals(123, cal.get(Calendar.MILLISECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0082DateTimeWithMillisZone() throws Exception {
|
||||
Date date = XmppDateTime.parseDate("1971-07-21T02:56:15.123-01:00");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(1971, cal.get(Calendar.YEAR));
|
||||
assertEquals(6, cal.get(Calendar.MONTH));
|
||||
assertEquals(21, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(3, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(56, cal.get(Calendar.MINUTE));
|
||||
assertEquals(15, cal.get(Calendar.SECOND));
|
||||
assertEquals(123, cal.get(Calendar.MILLISECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0091() throws Exception {
|
||||
Date date = XmppDateTime.parseDate("20020910T23:08:25");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2002, cal.get(Calendar.YEAR));
|
||||
assertEquals(8, cal.get(Calendar.MONTH));
|
||||
assertEquals(10, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(23, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(8, cal.get(Calendar.MINUTE));
|
||||
assertEquals(25, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0091NoLeading0() throws Exception {
|
||||
Date date = XmppDateTime.parseDate("200291T23:08:25");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2002, cal.get(Calendar.YEAR));
|
||||
assertEquals(8, cal.get(Calendar.MONTH));
|
||||
assertEquals(1, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(23, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(8, cal.get(Calendar.MINUTE));
|
||||
assertEquals(25, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0091AmbiguousMonthDay() throws Exception {
|
||||
Date date = XmppDateTime.parseDate("2002101T23:08:25");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2002, cal.get(Calendar.YEAR));
|
||||
assertEquals(9, cal.get(Calendar.MONTH));
|
||||
assertEquals(1, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(23, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(8, cal.get(Calendar.MINUTE));
|
||||
assertEquals(25, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseXep0091SingleDigitMonth() throws Exception {
|
||||
Date date = XmppDateTime.parseDate("2002130T23:08:25");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
assertEquals(2002, cal.get(Calendar.YEAR));
|
||||
assertEquals(0, cal.get(Calendar.MONTH));
|
||||
assertEquals(30, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(23, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(8, cal.get(Calendar.MINUTE));
|
||||
assertEquals(25, cal.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void parseNoMonthDay() throws Exception {
|
||||
XmppDateTime.parseDate("2002T23:08:25");
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void parseNoYear() throws Exception {
|
||||
XmppDateTime.parseDate("130T23:08:25");
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue