Added
Link Here
|
1 |
/******************************************************************************* |
2 |
* Copyright (c) 2006 IBM Corporation and others. |
3 |
* All rights reserved. This program and the accompanying materials |
4 |
* are made available under the terms of the Eclipse Public License v1.0 |
5 |
* which accompanies this distribution, and is available at |
6 |
* http://www.eclipse.org/legal/epl-v10.html |
7 |
* |
8 |
* Contributors: |
9 |
* IBM Corporation - initial API and implementation |
10 |
*******************************************************************************/ |
11 |
package org.eclipse.team.internal.ccvs.ui; |
12 |
|
13 |
import java.text.*; |
14 |
import java.util.Date; |
15 |
|
16 |
import org.eclipse.core.runtime.Assert; |
17 |
|
18 |
import com.ibm.icu.text.DateFormat; |
19 |
import com.ibm.icu.text.MessageFormat; |
20 |
import com.ibm.icu.util.Calendar; |
21 |
|
22 |
/** |
23 |
* Formats dates depending on their age relative to the current time. Only relevant date fields are |
24 |
* displayed (e.g. no day or date indication for dates on the same day, no year indication for dates |
25 |
* in the current year). Recent dates are reported relatively to the current time similarly to the |
26 |
* following scheme for an English locale: |
27 |
* <ul> |
28 |
* <li>The <strong>age in minutes</strong> is reported for dates in the past couple of minutes.</li> |
29 |
* <li><strong>Yesterday</strong> is used instead of the weekday's name for dates on the previous |
30 |
* day.</li> |
31 |
* <li><strong>Last <weekday></strong> is used for days from the previous week but no older |
32 |
* than seven days.</li> |
33 |
* </ul> |
34 |
* <p> |
35 |
* The time segments may either be discrete or sliding: the current year or month, yesterday and |
36 |
* today are discrete time windows that change when the current time moves over a time border (e.g. |
37 |
* midnight). The last minutes are a sliding window that trails the current time. |
38 |
* </p> |
39 |
* <p> |
40 |
* Current restrictions: |
41 |
* </p> |
42 |
* <ul> |
43 |
* <li>only dates in the past are supported.</li> |
44 |
* <li>parsing dates is not supported.</li> |
45 |
* </ul> |
46 |
* |
47 |
* @since 3.3 |
48 |
*/ |
49 |
public final class RelativeDateFormat extends DateFormat { |
50 |
private static final long serialVersionUID= 1L; |
51 |
|
52 |
/** Field describing the last minute. This field is sliding along with the current time. */ |
53 |
private static final int LAST_MINUTE= -1; |
54 |
/** |
55 |
* Field describing the last {@link #fSmallPeriod} minutes. This field is sliding along with the |
56 |
* current time. |
57 |
*/ |
58 |
private static final int LAST_SMALL_PERIOD= -2; |
59 |
/** Field describing yesterday. This field is discrete, starting and ending at midnight. */ |
60 |
private static final int YESTERDAY= -3; |
61 |
/** |
62 |
* Field describing the last week. This field is the intersection of the last seven days and the |
63 |
* last week (discretely starting at the |
64 |
* {@link Calendar#getFirstDayOfWeek() first day of the week}). So any given weekday may fall |
65 |
* in either the current or same week, but never both. |
66 |
* <p> |
67 |
* Example: if the current date is on a Thursday, then the most recent Friday would be within |
68 |
* the last week, but neither of the two most recent Tuesdays. |
69 |
* </p> |
70 |
*/ |
71 |
private static final int LAST_WEEK= -4; |
72 |
|
73 |
private final Format fOldFormat= new MessageFormat(RelativeDateFormatMessages.RelativeDateFormat_oldFormat); |
74 |
private final Format fSameYearFormat= new MessageFormat(RelativeDateFormatMessages.RelativeDateFormat_sameYearFormat); |
75 |
private final Format fSameMonthFormat= new MessageFormat(RelativeDateFormatMessages.RelativeDateFormat_sameMonthFormat); |
76 |
private final Format fLastWeekFormat= new MessageFormat(RelativeDateFormatMessages.RelativeDateFormat_lastWeekFormat); |
77 |
private final Format fSameWeekFormat= new MessageFormat(RelativeDateFormatMessages.RelativeDateFormat_sameWeekFormat); |
78 |
private final Format fYesterdayFormat= new MessageFormat(RelativeDateFormatMessages.RelativeDateFormat_yesterdayFormat); |
79 |
private final Format fSameDayFormat= new MessageFormat(RelativeDateFormatMessages.RelativeDateFormat_sameDayFormat); |
80 |
private final Format fSameHourFormat= new MessageFormat(RelativeDateFormatMessages.RelativeDateFormat_recentMinutesFormat); |
81 |
private final Format fSameMinuteFormat= new MessageFormat(RelativeDateFormatMessages.RelativeDateFormat_sameMinuteFormat); |
82 |
|
83 |
/** Maximum number of minutes to report age instead of timestamp. */ |
84 |
private final int fSmallPeriod; |
85 |
|
86 |
/** |
87 |
* Creates a new relative date format. |
88 |
* |
89 |
* @param relativeMinutes the number of minutes to format as relative age ("n minutes ago"), set |
90 |
* to a negative value to disable relative formats. |
91 |
*/ |
92 |
public RelativeDateFormat(int relativeMinutes) { |
93 |
fSmallPeriod= -relativeMinutes; |
94 |
DateFormat format= DateFormat.getDateTimeInstance(); |
95 |
calendar= format.getCalendar(); |
96 |
numberFormat= format.getNumberFormat(); |
97 |
} |
98 |
|
99 |
/** |
100 |
* Creates a new relative date format. Equivalent to |
101 |
* {@link #RelativeDateFormat(int) RelativeDateFormat(30)}. |
102 |
*/ |
103 |
public RelativeDateFormat() { |
104 |
this(30); |
105 |
} |
106 |
|
107 |
/* (non-Javadoc) |
108 |
* @see com.ibm.icu.text.DateFormat#format(com.ibm.icu.util.Calendar, java.lang.StringBuffer, java.text.FieldPosition) |
109 |
*/ |
110 |
public StringBuffer format(Calendar cal, StringBuffer toAppendTo, FieldPosition fieldPosition) { |
111 |
Calendar now= (Calendar) cal.clone(); |
112 |
now.setTimeInMillis(System.currentTimeMillis()); |
113 |
|
114 |
int field= getDiscriminatingField(cal, now); |
115 |
Date date= cal.getTime(); |
116 |
Format format= getFormat(field, date, now.getTime()); |
117 |
|
118 |
return format.format(new Object[] { date }, toAppendTo, fieldPosition); |
119 |
} |
120 |
|
121 |
private int getDiscriminatingField(Calendar then, Calendar now) { |
122 |
// check for relative sliding windows |
123 |
Calendar temp= (Calendar) now.clone(); |
124 |
temp.add(Calendar.MINUTE, -2); |
125 |
if (temp.before(then)) |
126 |
return LAST_MINUTE; |
127 |
temp.setTime(now.getTime()); |
128 |
temp.add(Calendar.MINUTE, fSmallPeriod); |
129 |
if (temp.before(then)) |
130 |
return LAST_SMALL_PERIOD; |
131 |
|
132 |
// check for relative periods overlapping larger ones (yesterday may be last year, last week |
133 |
// may be last month) |
134 |
Calendar midnight= (Calendar) now.clone(); |
135 |
midnight.set(Calendar.HOUR_OF_DAY, temp.getMinimum(Calendar.HOUR_OF_DAY)); |
136 |
midnight.set(Calendar.MINUTE, temp.getMinimum(Calendar.MINUTE)); |
137 |
midnight.set(Calendar.SECOND, temp.getMinimum(Calendar.SECOND)); |
138 |
midnight.set(Calendar.MILLISECOND, temp.getMinimum(Calendar.MILLISECOND)); |
139 |
|
140 |
// yesterday |
141 |
temp.setTime(then.getTime()); |
142 |
temp.add(Calendar.DATE, 1); |
143 |
if (temp.after(midnight) && temp.get(Calendar.DATE) == now.get(Calendar.DATE)) |
144 |
return YESTERDAY; |
145 |
|
146 |
// last week is from one week before now to discrete week end |
147 |
midnight.add(Calendar.DATE, 1); |
148 |
temp.setTime(then.getTime()); |
149 |
temp.add(Calendar.WEEK_OF_MONTH, 1); |
150 |
if (temp.after(midnight) && temp.get(Calendar.WEEK_OF_YEAR) == now.get(Calendar.WEEK_OF_YEAR)) |
151 |
return LAST_WEEK; |
152 |
|
153 |
// check for discrete periods |
154 |
int[] fields= { Calendar.YEAR, Calendar.MONTH, Calendar.WEEK_OF_MONTH, Calendar.DAY_OF_WEEK, Calendar.HOUR_OF_DAY }; |
155 |
for (int i= 0; i < fields.length; i++) { |
156 |
int field= fields[i]; |
157 |
if (now.get(field) != then.get(field)) |
158 |
return field; |
159 |
} |
160 |
|
161 |
return Calendar.HOUR_OF_DAY; |
162 |
} |
163 |
|
164 |
/** |
165 |
* Returns the proper format given the discriminating <code>field</code> and changes |
166 |
* <code>date</code> to be relative if the format is a relative one. |
167 |
* |
168 |
* @param field the discriminating field |
169 |
* @param date the date that will be formatted, may be modified |
170 |
* @param now the reference date |
171 |
* @return a format that can format <code>date</code> |
172 |
*/ |
173 |
private Format getFormat(int field, Date date, Date now) { |
174 |
switch (field) { |
175 |
case Calendar.YEAR: |
176 |
return fOldFormat; |
177 |
case Calendar.MONTH: |
178 |
return fSameYearFormat; |
179 |
case Calendar.WEEK_OF_MONTH: |
180 |
return fSameMonthFormat; |
181 |
case LAST_WEEK: |
182 |
return fLastWeekFormat; |
183 |
case Calendar.DAY_OF_WEEK: |
184 |
return fSameWeekFormat; |
185 |
case YESTERDAY: |
186 |
return fYesterdayFormat; |
187 |
case Calendar.HOUR_OF_DAY: |
188 |
return fSameDayFormat; |
189 |
case LAST_SMALL_PERIOD: |
190 |
long delta= now.getTime() - date.getTime(); |
191 |
date.setTime(delta); |
192 |
return fSameHourFormat; |
193 |
case LAST_MINUTE: |
194 |
delta= now.getTime() - date.getTime(); |
195 |
date.setTime(delta); |
196 |
return fSameMinuteFormat; |
197 |
default: |
198 |
Assert.isTrue(false); |
199 |
return null; |
200 |
} |
201 |
} |
202 |
|
203 |
/* (non-Javadoc) |
204 |
* @see com.ibm.icu.text.DateFormat#parse(java.lang.String, com.ibm.icu.util.Calendar, java.text.ParsePosition) |
205 |
*/ |
206 |
public void parse(String text, Calendar cal, ParsePosition pos) { |
207 |
throw new UnsupportedOperationException(); |
208 |
} |
209 |
} |