mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-22 22:32:06 +01: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");
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ package org.jivesoftware.smackx.delay.packet;
|
|||
|
||||
import java.util.Date;
|
||||
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.util.XmppDateTime;
|
||||
|
||||
/**
|
||||
* A decorator for the {@link DelayInformation} class to transparently support
|
||||
|
@ -92,7 +92,7 @@ public class DelayInfo extends DelayInformation
|
|||
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append(
|
||||
"\"");
|
||||
buf.append(" stamp=\"");
|
||||
buf.append(StringUtils.formatXEP0082Date(getStamp()));
|
||||
buf.append(XmppDateTime.formatXEP0082Date(getStamp()));
|
||||
buf.append("\"");
|
||||
if (getFrom() != null && getFrom().length() > 0) {
|
||||
buf.append(" from=\"").append(getFrom()).append("\"");
|
||||
|
|
|
@ -21,7 +21,7 @@ import java.util.Date;
|
|||
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.jivesoftware.smack.provider.PacketExtensionProvider;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.util.XmppDateTime;
|
||||
import org.jivesoftware.smackx.delay.packet.DelayInformation;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
|
@ -38,7 +38,7 @@ public class DelayInformationProvider implements PacketExtensionProvider {
|
|||
Date stamp = null;
|
||||
|
||||
try {
|
||||
stamp = StringUtils.parseDate(stampString);
|
||||
stamp = XmppDateTime.parseDate(stampString);
|
||||
}
|
||||
catch (ParseException parseExc) {
|
||||
/*
|
||||
|
|
|
@ -645,6 +645,19 @@ public class ServiceDiscoveryManager {
|
|||
connection.createPacketCollectorAndSend(discoverItems).nextResultOrThrow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the remote jid for it's features and returns true if the given feature is found.
|
||||
*
|
||||
* @param jid
|
||||
* @param feature
|
||||
* @return
|
||||
* @throws XMPPException
|
||||
*/
|
||||
public boolean supportsFeature(String jid, String feature) throws XMPPException {
|
||||
DiscoverInfo result = discoverInfo(jid);
|
||||
return result.containsFeature(feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity Capabilities
|
||||
*/
|
||||
|
|
|
@ -23,7 +23,7 @@ import java.util.Date;
|
|||
import java.util.Iterator;
|
||||
import java.util.UnknownFormatConversionException;
|
||||
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.util.XmppDateTime;
|
||||
import org.jivesoftware.smackx.xdata.Form;
|
||||
import org.jivesoftware.smackx.xdata.FormField;
|
||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||
|
@ -128,7 +128,7 @@ public class SubscribeForm extends Form
|
|||
String dateTime = getFieldValue(SubscribeOptionFields.expire);
|
||||
try
|
||||
{
|
||||
return StringUtils.parseDate(dateTime);
|
||||
return XmppDateTime.parseDate(dateTime);
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
|
@ -146,7 +146,7 @@ public class SubscribeForm extends Form
|
|||
public void setExpiry(Date expire)
|
||||
{
|
||||
addField(SubscribeOptionFields.expire, FormField.TYPE_TEXT_SINGLE);
|
||||
setAnswer(SubscribeOptionFields.expire.getFieldName(), StringUtils.formatXEP0082Date(expire));
|
||||
setAnswer(SubscribeOptionFields.expire.getFieldName(), XmppDateTime.formatXEP0082Date(expire));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Date;
|
|||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.util.XmppDateTime;
|
||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||
|
||||
/**
|
||||
|
@ -345,7 +346,7 @@ public class StreamInitiation extends IQ {
|
|||
}
|
||||
|
||||
if (getDate() != null) {
|
||||
buffer.append("date=\"").append(StringUtils.formatXEP0082Date(date)).append("\" ");
|
||||
buffer.append("date=\"").append(XmppDateTime.formatXEP0082Date(date)).append("\" ");
|
||||
}
|
||||
|
||||
if (getHash() != null) {
|
||||
|
|
|
@ -23,7 +23,7 @@ import java.util.logging.Logger;
|
|||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.provider.IQProvider;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.util.XmppDateTime;
|
||||
import org.jivesoftware.smackx.si.packet.StreamInitiation;
|
||||
import org.jivesoftware.smackx.si.packet.StreamInitiation.File;
|
||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||
|
@ -98,7 +98,7 @@ public class StreamInitiationProvider implements IQProvider {
|
|||
Date fileDate = new Date();
|
||||
if (date != null) {
|
||||
try {
|
||||
fileDate = StringUtils.parseDate(date);
|
||||
fileDate = XmppDateTime.parseDate(date);
|
||||
} catch (ParseException e) {
|
||||
// couldn't parse date, use current date-time
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
*
|
||||
* 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.smackx.time;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import org.jivesoftware.smack.Connection;
|
||||
import org.jivesoftware.smack.ConnectionCreationListener;
|
||||
import org.jivesoftware.smack.Manager;
|
||||
import org.jivesoftware.smack.PacketListener;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.filter.AndFilter;
|
||||
import org.jivesoftware.smack.filter.IQTypeFilter;
|
||||
import org.jivesoftware.smack.filter.PacketFilter;
|
||||
import org.jivesoftware.smack.filter.PacketTypeFilter;
|
||||
import org.jivesoftware.smack.packet.IQ.Type;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||
import org.jivesoftware.smackx.time.packet.Time;
|
||||
|
||||
public class EntityTimeManager extends Manager {
|
||||
|
||||
private static final Map<Connection, EntityTimeManager> INSTANCES = new WeakHashMap<Connection, EntityTimeManager>();
|
||||
|
||||
private static final PacketFilter TIME_PACKET_FILTER = new AndFilter(new PacketTypeFilter(
|
||||
Time.class), new IQTypeFilter(Type.GET));
|
||||
|
||||
private static boolean autoEnable = true;
|
||||
|
||||
static {
|
||||
Connection.addConnectionCreationListener(new ConnectionCreationListener() {
|
||||
public void connectionCreated(Connection connection) {
|
||||
getInstanceFor(connection);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void setAutoEnable(boolean autoEnable) {
|
||||
EntityTimeManager.autoEnable = autoEnable;
|
||||
}
|
||||
|
||||
public synchronized static EntityTimeManager getInstanceFor(Connection connection) {
|
||||
EntityTimeManager entityTimeManager = INSTANCES.get(connection);
|
||||
if (entityTimeManager == null) {
|
||||
entityTimeManager = new EntityTimeManager(connection);
|
||||
}
|
||||
return entityTimeManager;
|
||||
}
|
||||
|
||||
private boolean enabled = false;
|
||||
|
||||
private EntityTimeManager(Connection connection) {
|
||||
super(connection);
|
||||
INSTANCES.put(connection, this);
|
||||
if (autoEnable)
|
||||
enable();
|
||||
|
||||
connection.addPacketListener(new PacketListener() {
|
||||
@Override
|
||||
public void processPacket(Packet packet) {
|
||||
if (!enabled)
|
||||
return;
|
||||
connection().sendPacket(Time.createResponse(packet));
|
||||
}
|
||||
}, TIME_PACKET_FILTER);
|
||||
}
|
||||
|
||||
public synchronized void enable() {
|
||||
if (enabled)
|
||||
return;
|
||||
ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection());
|
||||
sdm.addFeature(Time.NAMESPACE);
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
public synchronized void disable() {
|
||||
if (!enabled)
|
||||
return;
|
||||
ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection());
|
||||
sdm.removeFeature(Time.NAMESPACE);
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
public boolean isTimeSupported(String jid) throws XMPPException {
|
||||
return ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(jid, Time.NAMESPACE);
|
||||
}
|
||||
|
||||
public Time getTime(String jid) throws XMPPException {
|
||||
if (!isTimeSupported(jid))
|
||||
return null;
|
||||
|
||||
Time request = new Time();
|
||||
Time response = (Time) connection().createPacketCollectorAndSend(request).nextResultOrThrow();
|
||||
return response;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2003-2007 Jive Software.
|
||||
* Copyright 2003-2007 Jive Software, 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.
|
||||
|
@ -14,16 +14,14 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.time.packet;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smack.util.XmppDateTime;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
@ -31,49 +29,21 @@ import java.util.logging.Logger;
|
|||
* A Time IQ packet, which is used by XMPP clients to exchange their respective local
|
||||
* times. Clients that wish to fully support the entitity time protocol should register
|
||||
* a PacketListener for incoming time requests that then respond with the local time.
|
||||
* This class can be used to request the time from other clients, such as in the
|
||||
* following code snippet:
|
||||
*
|
||||
* <pre>
|
||||
* // Request the time from a remote user.
|
||||
* Time timeRequest = new Time();
|
||||
* timeRequest.setType(IQ.Type.GET);
|
||||
* timeRequest.setTo(someUser@example.com/resource);
|
||||
*
|
||||
* // Create a packet collector to listen for a response.
|
||||
* PacketCollector collector = con.createPacketCollector(
|
||||
* new PacketIDFilter(timeRequest.getPacketID()));
|
||||
*
|
||||
* con.sendPacket(timeRequest);
|
||||
*
|
||||
* // Wait up to 5 seconds for a result.
|
||||
* IQ result = (IQ)collector.nextResult(5000);
|
||||
* if (result != null && result.getType() == IQ.Type.RESULT) {
|
||||
* Time timeResult = (Time)result;
|
||||
* // Do something with result...
|
||||
* }</pre><p>
|
||||
*
|
||||
* Warning: this is an non-standard protocol documented by
|
||||
* <a href="http://www.xmpp.org/extensions/xep-0090.html">XEP-0090</a>. Because this is a
|
||||
* non-standard protocol, it is subject to change.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
* @see http://www.xmpp.org/extensions/xep-0202.html
|
||||
* @author Florian Schmaus
|
||||
*/
|
||||
public class Time extends IQ {
|
||||
public static final String NAMESPACE = "urn:xmpp:time";
|
||||
public static final String ELEMENT = "time";
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(Time.class.getName());
|
||||
|
||||
private static SimpleDateFormat utcFormat = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
|
||||
private static DateFormat displayFormat = DateFormat.getDateTimeInstance();
|
||||
|
||||
private String utc = null;
|
||||
private String tz = null;
|
||||
private String display = null;
|
||||
private String utc;
|
||||
private String tzo;
|
||||
|
||||
/**
|
||||
* Creates a new Time instance with empty values for all fields.
|
||||
*/
|
||||
public Time() {
|
||||
|
||||
setType(Type.GET);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,12 +53,9 @@ public class Time extends IQ {
|
|||
* @param cal the time value.
|
||||
*/
|
||||
public Time(Calendar cal) {
|
||||
TimeZone timeZone = cal.getTimeZone();
|
||||
tz = cal.getTimeZone().getID();
|
||||
display = displayFormat.format(cal.getTime());
|
||||
tzo = XmppDateTime.asString(cal.getTimeZone());
|
||||
// Convert local time to the UTC time.
|
||||
utc = utcFormat.format(new Date(
|
||||
cal.getTimeInMillis() - timeZone.getOffset(cal.getTimeInMillis())));
|
||||
utc = XmppDateTime.formatXEP0082Date(cal.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -102,11 +69,7 @@ public class Time extends IQ {
|
|||
}
|
||||
Date date = null;
|
||||
try {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
// Convert the UTC time to local time.
|
||||
cal.setTime(new Date(utcFormat.parse(utc).getTime() +
|
||||
cal.getTimeZone().getOffset(cal.getTimeInMillis())));
|
||||
date = cal.getTime();
|
||||
date = XmppDateTime.parseDate(utc);
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOGGER.log(Level.SEVERE, "Error getting local time", e);
|
||||
|
@ -120,13 +83,10 @@ public class Time extends IQ {
|
|||
* @param time the current local time.
|
||||
*/
|
||||
public void setTime(Date time) {
|
||||
// Convert local time to UTC time.
|
||||
utc = utcFormat.format(new Date(
|
||||
time.getTime() - TimeZone.getDefault().getOffset(time.getTime())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time as a UTC formatted String using the format CCYYMMDDThh:mm:ss.
|
||||
* Returns the time as a UTC formatted String using the format CCYY-MM-DDThh:mm:ssZ.
|
||||
*
|
||||
* @return the time as a UTC formatted String.
|
||||
*/
|
||||
|
@ -135,13 +95,12 @@ public class Time extends IQ {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the time using UTC formatted String in the format CCYYMMDDThh:mm:ss.
|
||||
* Sets the time using UTC formatted String in the format CCYY-MM-DDThh:mm:ssZ.
|
||||
*
|
||||
* @param utc the time using a formatted String.
|
||||
*/
|
||||
public void setUtc(String utc) {
|
||||
this.utc = utc;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -149,50 +108,34 @@ public class Time extends IQ {
|
|||
*
|
||||
* @return the time zone.
|
||||
*/
|
||||
public String getTz() {
|
||||
return tz;
|
||||
public String getTzo() {
|
||||
return tzo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time zone.
|
||||
* Sets the time zone offset.
|
||||
*
|
||||
* @param tz the time zone.
|
||||
* @param tzo the time zone offset.
|
||||
*/
|
||||
public void setTz(String tz) {
|
||||
this.tz = tz;
|
||||
public void setTzo(String tzo) {
|
||||
this.tzo = tzo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the local (non-utc) time in human-friendly format.
|
||||
*
|
||||
* @return the local time in human-friendly format.
|
||||
*/
|
||||
public String getDisplay() {
|
||||
return display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the local time in human-friendly format.
|
||||
*
|
||||
* @param display the local time in human-friendly format.
|
||||
*/
|
||||
public void setDisplay(String display) {
|
||||
this.display = display;
|
||||
public static Time createResponse(Packet request) {
|
||||
Time time = new Time(Calendar.getInstance());
|
||||
time.setType(Type.RESULT);
|
||||
time.setTo(request.getFrom());
|
||||
return time;
|
||||
}
|
||||
|
||||
public String getChildElementXML() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("<query xmlns=\"jabber:iq:time\">");
|
||||
buf.append("<" + ELEMENT + " xmlns='" + NAMESPACE + "'>");
|
||||
if (utc != null) {
|
||||
buf.append("<utc>").append(utc).append("</utc>");
|
||||
buf.append("<tzo>").append(tzo).append("</tzo>");
|
||||
}
|
||||
if (tz != null) {
|
||||
buf.append("<tz>").append(tz).append("</tz>");
|
||||
}
|
||||
if (display != null) {
|
||||
buf.append("<display>").append(display).append("</display>");
|
||||
}
|
||||
buf.append("</query>");
|
||||
buf.append("</" + ELEMENT + ">");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- Providers file for default Smack extensions -->
|
||||
<smackProviders>
|
||||
|
||||
|
||||
<!-- Private Data Storage -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:private</namespace>
|
||||
<className>org.jivesoftware.smackx.iqprivate.PrivateDataManager$PrivateDataIQProvider</className>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:private</namespace>
|
||||
<className>org.jivesoftware.smackx.iqprivate.PrivateDataManager$PrivateDataIQProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
|
||||
<!-- Time -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:time</namespace>
|
||||
<className>org.jivesoftware.smackx.time.packet.Time</className>
|
||||
<elementName>time</elementName>
|
||||
<namespace>urn:xmpp:time</namespace>
|
||||
<className>org.jivesoftware.smackx.time.packet.Time</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- Message Events -->
|
||||
|
@ -209,7 +209,7 @@
|
|||
<namespace>http://jabber.org/protocol/ibb</namespace>
|
||||
<className>org.jivesoftware.smackx.bytestreams.ibb.provider.CloseIQProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>data</elementName>
|
||||
<namespace>http://jabber.org/protocol/ibb</namespace>
|
||||
|
@ -258,7 +258,7 @@
|
|||
<namespace>http://jabber.org/protocol/commands</namespace>
|
||||
<className>org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$SessionExpiredError</className>
|
||||
</extensionProvider>
|
||||
|
||||
|
||||
<!-- SHIM -->
|
||||
<extensionProvider>
|
||||
<elementName>headers</elementName>
|
||||
|
@ -345,14 +345,14 @@
|
|||
<namespace>http://jabber.org/protocol/pubsub#owner</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.FormNodeProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
|
||||
<!-- XEP-0060 pubsub#event -->
|
||||
<extensionProvider>
|
||||
<elementName>event</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.EventProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>configuration</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
|
||||
|
@ -364,31 +364,31 @@
|
|||
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>options</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.FormNodeProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>items</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.ItemsProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>item</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.ItemProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>retract</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.RetractEventProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>purge</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
|
||||
|
@ -401,7 +401,7 @@
|
|||
<namespace>http://jabber.org/protocol/nick</namespace>
|
||||
<className>org.jivesoftware.smackx.nick.packet.Nick$Provider</className>
|
||||
</extensionProvider>
|
||||
|
||||
|
||||
<!-- Attention -->
|
||||
<extensionProvider>
|
||||
<elementName>attention</elementName>
|
||||
|
|
|
@ -10,5 +10,6 @@
|
|||
<className>org.jivesoftware.smackx.commands.AdHocCommandManager</className>
|
||||
<className>org.jivesoftware.smackx.ping.PingManager</className>
|
||||
<className>org.jivesoftware.smackx.privacy.PrivacyListManager</className>
|
||||
<className>org.jivesoftware.smackx.time.EntityTimeManager</className>
|
||||
</startupClasses>
|
||||
</smack>
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
*
|
||||
* 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.smackx;
|
||||
|
||||
public class InitExtensions {
|
||||
|
||||
static {
|
||||
(new ExtensionsProviderInitializer()).initialize();
|
||||
(new ExtensionsStartupClasses()).initialize();
|
||||
}
|
||||
|
||||
}
|
|
@ -27,7 +27,7 @@ import java.util.GregorianCalendar;
|
|||
import java.util.Properties;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.util.XmppDateTime;
|
||||
import org.jivesoftware.smackx.delay.packet.DelayInfo;
|
||||
import org.jivesoftware.smackx.delay.packet.DelayInformation;
|
||||
import org.jivesoftware.smackx.delay.provider.DelayInfoProvider;
|
||||
|
@ -218,7 +218,7 @@ public class DelayInformationTest {
|
|||
.asString(outputProperties);
|
||||
|
||||
delayInfo = (DelayInfo) p.parseExtension(getParser(control, "delay"));
|
||||
Date controlDate = StringUtils.parseXEP0082Date("2008-06-08T09:16:20.0Z");
|
||||
Date controlDate = XmppDateTime.parseXEP0082Date("2008-06-08T09:16:20.0Z");
|
||||
|
||||
assertEquals(controlDate, delayInfo.getStamp());
|
||||
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
*
|
||||
* 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.smackx.time.packet;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.jivesoftware.smack.DummyConnection;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.test.util.TestUtils;
|
||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||
import org.jivesoftware.smackx.InitExtensions;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TimeTest extends InitExtensions {
|
||||
|
||||
@Test
|
||||
public void parseCurrentTimeTest() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
Time time = new Time(calendar);
|
||||
|
||||
Date date = time.getTime();
|
||||
Date calendarDate = calendar.getTime();
|
||||
|
||||
assertEquals(calendarDate, date);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void negativeTimezoneTest() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTimeZone(TimeZone.getTimeZone("GMT-830"));
|
||||
Time time = new Time(calendar);
|
||||
|
||||
assertEquals("-8:30", time.getTzo());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positiveTimezoneTest() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTimeZone(TimeZone.getTimeZone("GMT+830"));
|
||||
Time time = new Time(calendar);
|
||||
|
||||
assertEquals("+8:30", time.getTzo());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseTimeWithIntrospectionTest() throws Exception {
|
||||
DummyConnection connection = new DummyConnection();
|
||||
|
||||
// @formatter:off
|
||||
final String request =
|
||||
"<iq type='get'"
|
||||
+ "from='romeo@montague.net/orchard'"
|
||||
+ "to='juliet@capulet.com/balcony'"
|
||||
+ "id='time_1'>"
|
||||
+ "<time xmlns='urn:xmpp:time'/>"
|
||||
+ "</iq>";
|
||||
// @formatter:on
|
||||
IQ iqRequest = PacketParserUtils.parseIQ(TestUtils.getIQParser(request), connection);
|
||||
assertTrue(iqRequest instanceof Time);
|
||||
|
||||
// @formatter:off
|
||||
final String response =
|
||||
"<iq type='result'"
|
||||
+ "from='juliet@capulet.com/balcony'"
|
||||
+ "to='romeo@montague.net/orchard'"
|
||||
+ "id='time_1'>"
|
||||
+ "<time xmlns='urn:xmpp:time'>"
|
||||
+ "<tzo>-06:00</tzo>"
|
||||
+ "<utc>2006-12-19T17:58:35Z</utc>"
|
||||
+ "</time>"
|
||||
+ "</iq>";
|
||||
// @formatter:on
|
||||
IQ iqResponse = PacketParserUtils.parseIQ(TestUtils.getIQParser(response), connection);
|
||||
assertTrue(iqResponse instanceof Time);
|
||||
Time time = (Time) iqResponse;
|
||||
assertEquals("-06:00", time.getTzo());
|
||||
assertEquals("2006-12-19T17:58:35Z", time.getUtc());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue