/*
* Funambol is a mobile platform developed by Funambol, Inc.
* Copyright (C) 2003 - 2007 Funambol, Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation with the addition of the following permission
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
* WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, see http://www.gnu.org/licenses or write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA.
*
* You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
* 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Powered by Funambol" logo. If the display of the logo is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Powered by Funambol".
*/
import java.util.Calendar;
import java.util.Date;
import java.util.Hashtable;
/**
* A utility class providing methods to convert date information contained in
* Date
objects into RFC2822 and UTC ('Zulu') strings, and to
* build Date
objects starting from string representations of
* dates in RFC2822 and UTC format
*/
public class MailDateFormatter {
/** Format date as: MM/DD */
public static final int FORMAT_MONTH_DAY = 0;
/** Format date as: MM/DD/YYYY */
public static final int FORMAT_MONTH_DAY_YEAR = 1;
/** Format date as: hh:mm */
public static final int FORMAT_HOURS_MINUTES = 2;
/** Format date as: hh:mm:ss */
public static final int FORMAT_HOURS_MINUTES_SECONDS = 3;
/** Format date as: DD/MM */
public static final int FORMAT_DAY_MONTH = 4;
/** Format date as: DD/MM/YYYY */
public static final int FORMAT_DAY_MONTH_YEAR = 5;
/** Device offset, as string */
private static String deviceOffset = "+0000";
/** Device offset, in millis */
private static long millisDeviceOffset = 0;
/** Names of the months */
private static String[] monthNames = new String[] {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
/**
* Transforms data contained in a Date
object (expressed in
* UTC) in a string formatted as per RFC2822 in local time (par. 3.3)
*
* @return A string representing the date contained in the passed
* Date
object formatted as per RFC 2822 and in local
* time
*/
public static String dateToRfc2822(Date date) {
Calendar deviceTime = Calendar.getInstance();
deviceTime.setTime(date);
String dayweek = "";
int dayOfWeek = deviceTime.get(deviceTime.DAY_OF_WEEK);
switch (dayOfWeek) {
case 1 : dayweek = "Sun"; break;
case 2 : dayweek = "Mon"; break;
case 3 : dayweek = "Tue"; break;
case 4 : dayweek = "Wed"; break;
case 5 : dayweek = "Thu"; break;
case 6 : dayweek = "Fri"; break;
case 7 : dayweek = "Sat"; break;
}
int dayOfMonth = deviceTime.get(deviceTime.DAY_OF_MONTH);
String monthInYear = getMonthName(deviceTime.get(deviceTime.MONTH));
int year = deviceTime.get(deviceTime.YEAR);
int hourOfDay = deviceTime.get(deviceTime.HOUR_OF_DAY);
int minutes = deviceTime.get(deviceTime.MINUTE);
int seconds = deviceTime.get(deviceTime.SECOND);
String rfc = dayweek + ", " + // Tue
dayOfMonth + " " + // 7
monthInYear + " " + // Nov
year + " " + // 2006
hourOfDay + ":" + minutes + ":" + seconds + " " + // 14:13:26
deviceOffset; //+0200
return rfc;
}
/**
* Converts a Date
object into a string in 'Zulu' format
*
* @param d
* A Date
object to be converted into a string in
* 'Zulu' format
* @return A string representing the date contained in the passed
* Date
object in 'Zulu' format (e.g.
* yyyyMMDDThhmmssZ)
*/
public static String dateToUTC(Date d) {
StringBuffer date = new StringBuffer();
Calendar cal = Calendar.getInstance();
cal.setTime(d);
date.append(cal.get(Calendar.YEAR));
date.append(printTwoDigits(cal.get(Calendar.MONTH) + 1))
.append(printTwoDigits(cal.get(Calendar.DATE)))
.append("T");
date.append(printTwoDigits(cal.get(Calendar.HOUR_OF_DAY)))
.append(printTwoDigits(cal.get(Calendar.MINUTE)))
.append(printTwoDigits(cal.get(Calendar.SECOND)))
.append("Z");
return date.toString();
}
/**
* A method that returns a string rapresenting a date.
*
* @param date the date
*
* @param format the format as one of
* FORMAT_MONTH_DAY,
* FORMAT_MONTH_DAY_YEAR,
* FORMAT_HOURS_MINUTES,
* FORMAT_HOURS_MINUTES_SECONDS
* FORMAT_DAY_MONTH
* FORMAT_DAY_MONTH_YEAR
* constants
*
* @param separator the separator to be used
*/
public static String getFormattedStringFromDate(
Date date, int format, String separator) {
Calendar cal=Calendar.getInstance();
cal.setTime(date);
StringBuffer ret = new StringBuffer();
switch (format) {
case FORMAT_HOURS_MINUTES:
//if pm and hour == 0 we want to write 12, not 0
if (cal.get(Calendar.AM_PM)==Calendar.PM
&& cal.get(Calendar.HOUR) == 0) {
ret.append("12");
} else {
ret.append(cal.get(Calendar.HOUR));
}
ret.append(separator)
.append(printTwoDigits(cal.get(Calendar.MINUTE)))
.append(getAMPM(cal));
break;
case FORMAT_HOURS_MINUTES_SECONDS:
//if pm and hour == 0 we want to write 12, not 0
if (cal.get(Calendar.AM_PM)==Calendar.PM
&& cal.get(Calendar.HOUR) == 0) {
ret.append("12");
} else {
ret.append(cal.get(Calendar.HOUR));
}
ret.append(separator)
.append(printTwoDigits(cal.get(Calendar.MINUTE)))
.append(separator)
.append(cal.get(Calendar.SECOND))
.append(getAMPM(cal));
break;
case FORMAT_MONTH_DAY:
ret.append(cal.get(Calendar.MONTH)+1)
.append(separator)
.append(cal.get(Calendar.DAY_OF_MONTH));
break;
case FORMAT_DAY_MONTH:
ret.append(cal.get(Calendar.DAY_OF_MONTH))
.append(separator)
.append(cal.get(Calendar.MONTH)+1);
break;
case FORMAT_MONTH_DAY_YEAR:
ret.append(cal.get(Calendar.MONTH)+1)
.append(separator)
.append(cal.get(Calendar.DAY_OF_MONTH))
.append(separator)
.append(cal.get(Calendar.YEAR));
break;
case FORMAT_DAY_MONTH_YEAR:
ret.append(cal.get(Calendar.DAY_OF_MONTH))
.append(separator)
.append(cal.get(Calendar.MONTH)+1)
.append(separator)
.append(cal.get(Calendar.YEAR));
break;
default:
// Log.error("getFormattedStringFromDate: invalid format ("+
// format+")");
}
return ret.toString();
}
/**
* Returns a localized string representation of Date.
*/
public static String formatLocalTime(Date d) {
int dateFormat = FORMAT_MONTH_DAY_YEAR;
int timeFormat = FORMAT_HOURS_MINUTES;
if(!System.getProperty("microedition.locale").equals("en")) {
dateFormat = FORMAT_DAY_MONTH_YEAR;
}
return getFormattedStringFromDate(d,FORMAT_MONTH_DAY_YEAR,"/")
+" "+getFormattedStringFromDate(d,FORMAT_HOURS_MINUTES,":");
}
/**
* Parses the string in RFC 2822 format and return a Date
* object.
* Parse strings like:
* Thu, 03 May 2007 14:45:38 GMT
* Thu, 03 May 2007 14:45:38 GMT+0200
* Thu, 1 Feb 2007 03:57:01 -0800
* Fri, 04 May 2007 13:40:17 PDT
*
* @param d the date representation to parse
* @return a date, if valid, or null on error
*
*/
public static Date parseRfc2822Date(String stringDate) {
if (stringDate == null) {
return null;
}
long hourOffset=0;
long minOffset=0;
Calendar cal = Calendar.getInstance();
try {
// We use the ' ' as separator and we expect only one space. We
// clean the string to remove extra spaces
StringBuffer cleanedDate = new StringBuffer();
char previous = 'a';
for(int i=0;i char ch = stringDate.charAt(i);
if (ch != ' ' || previous != ' ') {
cleanedDate.append(ch);
}
previous = ch;
}
stringDate = cleanedDate.toString();
// Log.debug("Cleaned date: " + stringDate);
// Just skip the weekday if present
int start = stringDate.indexOf(',');
//put start after ", "
start = (start == -1) ? 0 : start + 2;
stringDate = stringDate.substring(start).trim();
start = 0;
// Get day of month
int end = stringDate.indexOf(' ', start);
//4 Nov 2008 10:30:05 -0400
int day =1;
try {
day = Integer.parseInt(stringDate.substring(start, end));
} catch (NumberFormatException ex) {
day = Integer.parseInt(stringDate.substring(start+3, end));
}
cal.set(Calendar.DAY_OF_MONTH,day);
// Get month
start = end + 1;
end = stringDate.indexOf(' ', start);
cal.set(Calendar.MONTH, getMonthNumber(stringDate.substring(start, end)));
// Get year
start = end + 1;
end = stringDate.indexOf(' ', start);
cal.set(Calendar.YEAR,
Integer.parseInt(stringDate.substring(start, end)));
// Get hour
start = end + 1;
end = stringDate.indexOf(':', start);
cal.set(Calendar.HOUR_OF_DAY,
Integer.parseInt(stringDate.substring(start, end).trim()));
// Get min
start = end + 1;
end = stringDate.indexOf(':', start);
cal.set(Calendar.MINUTE,
Integer.parseInt(stringDate.substring(start, end)));
// Get sec
start = end + 1;
end = stringDate.indexOf(' ', start);
cal.set(Calendar.SECOND,
Integer.parseInt(stringDate.substring(start, end)));
// Get OFFSET
start = end +1;
end = stringDate.indexOf('\r', start);
// Process Timezone, checking first for the actual RFC2822 format,
// and then for nthe obsolete syntax.
char sign = '+';
String hourDiff = "0";
String minDiff = "0";
String offset = stringDate.substring(start).trim();
if (offset.startsWith("+") || offset.startsWith("-")) {
if(offset.length() >= 5 ){
sign = offset.charAt(0);
hourDiff = offset.substring(1,3);
minDiff = offset.substring(3,5);
}
else if(offset.length() == 3){
sign = offset.charAt(0);
hourDiff = offset.substring(1);
minDiff = "00";
}
// Convert offset to int
hourOffset = Long.parseLong(hourDiff);
minOffset = Long.parseLong(minDiff);
if(sign == '-') {
hourOffset = -hourOffset;
}
}
else if(offset.equals("EDT")){
hourOffset = -4;
}
else if(offset.equals("EST") || offset.equals("CDT")){
hourOffset = -5;
}
else if(offset.equals("CST") || offset.equals("MDT")){
hourOffset = -6;
}
else if(offset.equals("PDT") || offset.equals("MST")){
hourOffset = -7;
}
else if(offset.equals("PST")){
hourOffset = -8;
}
else if(offset.equals("GMT") || offset.equals("UT")){
hourOffset = 0;
}
else if (offset.substring(0,3).equals("GMT") && offset.length() > 3){
sign = offset.charAt(3);
hourDiff = offset.substring(4,6);
minDiff = offset.substring(6,8);
}
long millisOffset = (hourOffset * 3600000) + (minOffset * 60000);
Date gmtDate = cal.getTime();
long millisDate = gmtDate.getTime();
millisDate -= millisOffset;
gmtDate.setTime(millisDate);
return gmtDate;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* Convert the given date (GMT) into the local date.
* NOTE: changes the original date too!
* Should we change it to a void toLocalDate(Date) that changes the
* input date only?
*/
public static Date getDeviceLocalDate (Date gmtDate){
if (null != gmtDate){
/*long dateInMillis = gmtDate.getTime();
Date deviceDate = new Date();
deviceDate.setTime(dateInMillis+millisDeviceOffset);
return deviceDate;
**/
gmtDate.setTime(gmtDate.getTime()+millisDeviceOffset);
return gmtDate;
}
else {
return null;
}
}
/**
* Gets a Date
object from a string representing a date in
* 'Zulu' format (yyyyMMddTHHmmssZ)
*
* @param utc
* date in 'Zulu' format (yyyyMMddTHHmmssZ)
* @return A Date
object obtained starting from a time in
* milliseconds from the Epoch
*/
public static Date parseUTCDate(String utc) {
int day = 0;
int month = 0;
int year = 0;
int hour = 0;
int minute = 0;
int second = 0;
Calendar calendar = null;
day = Integer.parseInt(utc.substring(6, 8));
month = Integer.parseInt(utc.substring(4, 6));
year = Integer.parseInt(utc.substring(0, 4));
hour = Integer.parseInt(utc.substring(9, 11));
minute = Integer.parseInt(utc.substring(11, 13));
second = Integer.parseInt(utc.substring(13, 15));
calendar = Calendar.getInstance();
calendar.set(Calendar.DAY_OF_MONTH, day);
calendar.set(Calendar.MONTH, month - 1);
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, second);
Date date = calendar.getTime();
long dateInMillis = date.getTime();
date.setTime(dateInMillis+millisDeviceOffset);
return date;
}
public static void setTimeZone(String timeZone){
if (timeZone == null || timeZone.length() < 5) {
// Log.error("setTimeZone: invalid timezone " + timeZone);
}
try {
deviceOffset = timeZone;
String hstmz = deviceOffset.substring(1, 3);
String mstmz = deviceOffset.substring(3, 5);
long hhtmz = Long.parseLong(hstmz);
long mmtmz = Long.parseLong(mstmz);
millisDeviceOffset = (hhtmz * 3600000) + (mmtmz * 60000);
if(deviceOffset.charAt(0)=='-') {
millisDeviceOffset *= -1;
}
} catch(Exception e) {
e.printStackTrace();
}
}
/**
* returns a date with string representation of the month
* @param date input date in the format MM/DD/YYYY HH:MMp/a
* @return a representation of the date in the format DD, YYYY HH:MM
*/
public static String getReplyDateString(String date) {
StringBuffer ret = new StringBuffer();
//Replace the month number with the month name
String monthName = getMonthName(
Integer.parseInt(date.substring(0, date.indexOf('/')))-1
);
String day = date.substring(date.indexOf('/')+1, date.lastIndexOf('/'));
String yearAndTime = date.substring(date.lastIndexOf('/')+1);
ret.append(monthName).append(" ").append(day).append(", ").append(yearAndTime);
//Replace the slash char between DD and YYYY with ", "
return ret.toString();
}
//------------------------------------------------------------- Private methods
/**
* Get the number of the month, given the name.
*/
private static int getMonthNumber(String name) {
for(int i=0, l=monthNames.length; i if(monthNames[i].equals(name)) {
return i;
}
}
return -1;
}
/**
* Get the name of the month, given the number.
*/
private static String getMonthName(int number) {
if(number>=0 && number return monthNames[number];
}
else return null;
}
private static String getAMPM(Calendar cal) {
return (cal.get(Calendar.AM_PM)==Calendar.AM)?"a":"p";
}
/**
* Returns a string representation of number with at least 2 digits
*/
private static String printTwoDigits(int number) {
if (number>9) {
return String.valueOf(number);
} else {
return "0"+number;
}
}
}