/* * Copyright 2008 ZXing 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 com.google.zxing.client.result; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import java.util.regex.Pattern; /** * @author Sean Owen */ public final class CalendarParsedResult extends ParsedResult { private static final Pattern DATE_TIME = Pattern.compile("[0-9]{8}(T[0-9]{6}Z?)?"); private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH); static { // For dates without a time, for purposes of interacting with Android, the resulting timestamp // needs to be midnight of that day in GMT. See: // http://code.google.com/p/android/issues/detail?id=8330 DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT")); } private static final DateFormat DATE_TIME_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.ENGLISH); private final String summary; private final Date start; private final boolean startAllDay; private final Date end; private final boolean endAllDay; private final String location; private final String organizer; private final String[] attendees; private final String description; private final double latitude; private final double longitude; public CalendarParsedResult(String summary, String startString, String endString, String location, String organizer, String[] attendees, String description, double latitude, double longitude) { super(ParsedResultType.CALENDAR); this.summary = summary; try { this.start = parseDate(startString); this.end = endString == null ? null : parseDate(endString); } catch (ParseException pe) { throw new IllegalArgumentException(pe.toString()); } this.startAllDay = startString.length() == 8; this.endAllDay = endString != null && endString.length() == 8; this.location = location; this.organizer = organizer; this.attendees = attendees; this.description = description; this.latitude = latitude; this.longitude = longitude; } public String getSummary() { return summary; } /** * @return start time */ public Date getStart() { return start; } /** * @return true if start time was specified as a whole day */ public boolean isStartAllDay() { return startAllDay; } /** * May return null if the event has no duration. * @see #getStart() */ public Date getEnd() { return end; } /** * @return true if end time was specified as a whole day */ public boolean isEndAllDay() { return endAllDay; } public String getLocation() { return location; } public String getOrganizer() { return organizer; } public String[] getAttendees() { return attendees; } public String getDescription() { return description; } public double getLatitude() { return latitude; } public double getLongitude() { return longitude; } @Override public String getDisplayResult() { StringBuilder result = new StringBuilder(100); maybeAppend(summary, result); maybeAppend(format(startAllDay, start), result); maybeAppend(format(endAllDay, end), result); maybeAppend(location, result); maybeAppend(organizer, result); maybeAppend(attendees, result); maybeAppend(description, result); return result.toString(); } /** * Parses a string as a date. RFC 2445 allows the start and end fields to be of type DATE (e.g. 20081021) * or DATE-TIME (e.g. 20081021T123000 for local time, or 20081021T123000Z for UTC). * * @param when The string to parse * @throws ParseException if not able to parse as a date */ private static Date parseDate(String when) throws ParseException { if (!DATE_TIME.matcher(when).matches()) { throw new ParseException(when, 0); } if (when.length() == 8) { // Show only year/month/day return DATE_FORMAT.parse(when); } else { // The when string can be local time, or UTC if it ends with a Z Date date; if (when.length() == 16 && when.charAt(15) == 'Z') { date = DATE_TIME_FORMAT.parse(when.substring(0, 15)); Calendar calendar = new GregorianCalendar(); long milliseconds = date.getTime(); // Account for time zone difference milliseconds += calendar.get(Calendar.ZONE_OFFSET); // Might need to correct for daylight savings time, but use target time since // now might be in DST but not then, or vice versa calendar.setTime(new Date(milliseconds)); milliseconds += calendar.get(Calendar.DST_OFFSET); date = new Date(milliseconds); } else { date = DATE_TIME_FORMAT.parse(when); } return date; } } private static String format(boolean allDay, Date date) { if (date == null) { return null; } DateFormat format = allDay ? DateFormat.getDateInstance(DateFormat.MEDIUM) : DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); return format.format(date); } }