Java/Email/Formatter
Convert date information contained in Date into RFC2822 and UTC ("Zulu") strings
/*
* 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.ru.
*
* 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
* <code>Date</code> objects into RFC2822 and UTC ("Zulu") strings, and to
* build <code>Date</code> 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 <code>Date</code> 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
* <code>Date</code> 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 <code>Date</code> object into a string in "Zulu" format
*
* @param d
* A <code>Date</code> object to be converted into a string in
* "Zulu" format
* @return A string representing the date contained in the passed
* <code>Date</code> 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 <code>Date</code>
* object. <p>
* 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<stringDate.length();++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 <code>Date</code> object from a string representing a date in
* "Zulu" format (yyyyMMddTHHmmssZ)
*
* @param utc
* date in "Zulu" format (yyyyMMddTHHmmssZ)
* @return A <code>Date</code> 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 <MonthName> 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<l; 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<monthNames.length) {
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;
}
}
}
Convert lines into the canonical MIME format, that is, terminate lines with CRLF
/*
* @(#)CRLFOutputStream.java 1.3 01/05/23
*
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND
* ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES OR LIABILITIES
* SUFFERED BY LICENSEE AS A RESULT OF OR RELATING TO USE, MODIFICATION
* OR DISTRIBUTION OF THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
* SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR
* FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that Software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of any
* nuclear facility.
*/
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* Convert lines into the canonical MIME format, that is, terminate lines with
* CRLF.
* <p>
*
* This stream can be used with the Part.writeTo and Message.writeTo methods to
* generate the canonical MIME format of the data for the purpose of (e.g.)
* sending it via SMTP or computing a digital signature.
*/
public class CRLFOutputStream extends FilterOutputStream {
protected int lastb = -1;
protected static byte[] newline;
static {
newline = new byte[2];
newline[0] = (byte) "\r";
newline[1] = (byte) "\n";
}
public CRLFOutputStream(OutputStream os) {
super(os);
}
public void write(int b) throws IOException {
if (b == "\r") {
out.write(newline);
} else if (b == "\n") {
if (lastb != "\r")
out.write(newline);
} else {
out.write(b);
}
lastb = b;
}
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
public void write(byte b[], int off, int len) throws IOException {
int start = off;
len += off;
for (int i = start; i < len; i++) {
if (b[i] == "\r") {
out.write(b, start, i - start);
out.write(newline);
start = i + 1;
} else if (b[i] == "\n") {
if (lastb != "\r") {
out.write(b, start, i - start);
out.write(newline);
}
start = i + 1;
}
lastb = b[i];
}
if ((len - start) > 0)
out.write(b, start, len - start);
}
}
Convert the various newline conventions to the local platform"s newline convention
/*
* @(#)NewlineOutputStream.java 1.3 01/05/23
*
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND
* ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES OR LIABILITIES
* SUFFERED BY LICENSEE AS A RESULT OF OR RELATING TO USE, MODIFICATION
* OR DISTRIBUTION OF THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
* SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR
* FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that Software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of any
* nuclear facility.
*/
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* Convert the various newline conventions to the local platform"s newline
* convention.
* <p>
*
* This stream can be used with the Message.writeTo method to generate a message
* that uses the local plaform"s line terminator for the purpose of (e.g.)
* saving the message to a local file.
*/
public class NewlineOutputStream extends FilterOutputStream {
private int lastb = -1;
private static byte[] newline;
public NewlineOutputStream(OutputStream os) {
super(os);
if (newline == null) {
String s = System.getProperty("line.separator");
if (s == null || s.length() <= 0)
s = "\n";
newline = new byte[s.length()];
s.getBytes(0, s.length(), newline, 0);
}
}
public void write(int b) throws IOException {
if (b == "\r") {
out.write(newline);
} else if (b == "\n") {
if (lastb != "\r")
out.write(newline);
} else {
out.write(b);
}
lastb = b;
}
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
public void write(byte b[], int off, int len) throws IOException {
for (int i = 0; i < len; i++) {
write(b[off + i]);
}
}
}