Skip to content

Commit

Permalink
keep timezone when parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
azinman committed May 10, 2012
1 parent 159aece commit 68ba395
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 10 deletions.
Expand Up @@ -686,7 +686,7 @@ public ListenableFuture<List<MessageStatus>> fetchUidsHeaders(Folder folder, Lis
argsBuilder.append(',');
}
// argsBuilder.append(')');
argsBuilder.append(" (RFC822.SIZE FLAGS INTERNALDATE BODYSTRUCTURE ENVELOPE UID");
argsBuilder.append(" (RFC822.SIZE FLAGS INTERNALDATE ENVELOPE UID");
if (config.useGmailExtensions()) {
argsBuilder.append(" X-GM-MSGID X-GM-THRID X-GM-LABELS");
}
Expand Down
Expand Up @@ -24,6 +24,7 @@ public class Message implements HasBodyParts {
// A header can have multiple, different values.
private Multimap<String, String> headers = newListMultimap();
private List<BodyPart> bodyParts = new ArrayList<BodyPart>();
private String rootMimeType = null;

public void setImapUid(int imapUid) {
this.imapUid = imapUid;
Expand Down Expand Up @@ -67,7 +68,14 @@ public List<BodyPart> getBodyParts() {
}

@Override public void setMimeType(String mimeType) {
throw new RuntimeException("Should not call mimeType from here");
if (!mimeType.startsWith("multipart")) {
throw new RuntimeException("Invalid root mimeType: " + mimeType);
}
this.rootMimeType = mimeType;
}

public String getRootMimeType() {
return rootMimeType;
}

public static class BodyPart implements HasBodyParts {
Expand Down
Expand Up @@ -11,7 +11,7 @@
public class MessageStatus {
private int imapUid;
private String messageUid;
private Date receivedDate;
private GregorianCalendar receivedDate;
private String subject;
private String inReplyTo;

Expand Down Expand Up @@ -100,7 +100,7 @@ public String getMessageUid() {
return messageUid;
}

public Date getReceivedDate() {
public GregorianCalendar getReceivedDate() {
return receivedDate;
}

Expand All @@ -124,7 +124,7 @@ public void setMessageUid(String messageUid) {
this.messageUid = messageUid;
}

public void setReceivedDate(Date receivedDate) {
public void setReceivedDate(GregorianCalendar receivedDate) {
this.receivedDate = receivedDate;
}

Expand Down Expand Up @@ -178,7 +178,7 @@ public Long getGmailMsgId() {
return "MessageStatus{" +
"imapUid=" + imapUid +
", messageUid='" + messageUid + '\'' +
", receivedDate=" + (receivedDate == null ? "null" : ISO_C_DATE_SYDNEY.format(receivedDate)) +
", receivedDate=" + (receivedDate == null ? "null" : receivedDate) +
", subject='" + subject + '\'' +
", inReplyTo='" + inReplyTo + '\'' +
", from=" + from +
Expand Down
@@ -1,5 +1,7 @@
package com.google.sitebricks.mail.imap;

import com.google.sitebricks.util.*;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
Expand Down Expand Up @@ -304,10 +306,9 @@ private static boolean parseEnvelope(Queue<String> tokens, MessageStatus status)
String receivedDate = tokens.peek();
if (Parsing.isValid(receivedDate)) {
receivedDate = Parsing.normalizeDateToken(Parsing.match(tokens, String.class));
try {
status.setReceivedDate(new javax.mail.internet.MailDateFormat().parse(receivedDate));
} catch (ParseException e) {
log.error("Malformed received date format {}. Unable to parse.", receivedDate, e);
status.setReceivedDate(com.google.sitebricks.util.MailDateFormat.parse(receivedDate));
if (status.getReceivedDate() == null) {
log.warning("Malformed received date format {}. Unable to parse.", receivedDate);
}
} else if (receivedDate != null) {
Parsing.eat(tokens, "NIL");
Expand Down
@@ -0,0 +1,169 @@
package com.google.sitebricks.util;

import java.io.PrintStream;
import java.text.DecimalFormat;
import java.text.FieldPosition;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;

/**
* A modification of the GNU implementation to keep the timezone component in
* parsing mail date formats.
*
*/
public class MailDateFormat {
private static final String[] MONTHS = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

/**
* Parses the given date in the format specified by
* draft-ietf-drums-msg-fmt-08 in the current TimeZone.
* @param text the formatted date to be parsed
*/
public static GregorianCalendar parse(String text)
{
int start = 0, end = -1;
int len = text.length();
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
calendar.clear();
ParsePosition pos = new ParsePosition(start);
try
{
// Advance to date
if (Character.isLetter(text.charAt(start)))
{
start = skipNonWhitespace(text, start, len);
}
start = skipWhitespace(text, start, len);
pos.setIndex(start);
end = skipNonWhitespace(text, start + 1, len);
int date = Integer.parseInt(text.substring(start, end));
// Advance to month
start = skipWhitespace(text, end + 1, len);
pos.setIndex(start);
end = skipNonWhitespace(text, start + 1, len);
String monthText = text.substring(start, end);
int month = -1;
for (int i = 0; i < 12; i++)
{
if (MONTHS[i].equals(monthText))
{
month = i;
break;
}
}
if (month == -1)
{
pos.setErrorIndex(end);
return null;
}
// Advance to year
start = skipWhitespace(text, end + 1, len);
pos.setIndex(start);
end = skipNonWhitespace(text, start + 1, len);
int year = Integer.parseInt(text.substring(start, end));
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.DAY_OF_MONTH, date);
// Advance to hour
start = skipWhitespace(text, end + 1, len);
pos.setIndex(start);
end = skipToColon(text, start + 1, len);
int hour = Integer.parseInt(text.substring(start, end));
calendar.set(Calendar.HOUR, hour);
// Advance to minute
start = end + 1;
pos.setIndex(start);
end = skipToColon(text, start + 1, len);
int minute = Integer.parseInt(text.substring(start, end));
calendar.set(Calendar.MINUTE, minute);
// Advance to second
start = end + 1;
pos.setIndex(start);
end = skipNonWhitespace(text, start + 1, len);
int second = Integer.parseInt(text.substring(start, end));
calendar.set(Calendar.SECOND, second);

if (end != len)
{
start = skipWhitespace(text, end + 1, len);
if (start != len)
{
// Trailing characters, therefore timezone
end = skipNonWhitespace(text, start + 1, len);
char pm = text.charAt(start);
if (Character.isLetter(pm))
{
TimeZone tz =
TimeZone.getTimeZone(text.substring(start, end));
calendar.set(Calendar.ZONE_OFFSET, tz.getRawOffset());
}
else
{
int zoneOffset = 0;
zoneOffset +=
600 * Character.digit(text.charAt(++start), 10);
zoneOffset +=
60 * Character.digit(text.charAt(++start), 10);
zoneOffset +=
10 * Character.digit(text.charAt(++start), 10);
zoneOffset +=
Character.digit(text.charAt(++start), 10);
zoneOffset *= 60000; // minutes -> ms
if ('-' == pm)
{
zoneOffset = -zoneOffset;
}
calendar.set(Calendar.ZONE_OFFSET, zoneOffset);
}
}
}
pos.setIndex(end);

return calendar;
}
catch (NumberFormatException e)
{
pos.setErrorIndex(Math.max(start, end));
}
catch (StringIndexOutOfBoundsException e)
{
pos.setErrorIndex(Math.max(start, end));
}
return null;
}

private static int skipWhitespace(final String text, int pos, final int len)
{
while (pos < len && Character.isWhitespace(text.charAt(pos)))
{
pos++;
}
return pos;
}

private static int skipNonWhitespace(final String text, int pos, final int len)
{
while (pos < len && !Character.isWhitespace(text.charAt(pos)))
{
pos++;
}
return pos;
}

private static int skipToColon(final String text, int pos, final int len)
{
while (pos < len && text.charAt(pos) != ':')
{
pos++;
}
return pos;
}
}

0 comments on commit 68ba395

Please sign in to comment.