1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package de.bea.domingo.proxy;
24
25 import java.io.Serializable;
26 import java.util.Calendar;
27 import java.util.Date;
28 import java.util.TimeZone;
29
30 import lotus.domino.DateRange;
31 import lotus.domino.DateTime;
32 import lotus.domino.NotesException;
33 import lotus.domino.Session;
34 import de.bea.domingo.DNotesMonitor;
35 import de.bea.domingo.exception.DominoException;
36 import de.bea.domingo.i18n.ResourceManager;
37 import de.bea.domingo.i18n.Resources;
38 import de.bea.domingo.util.GregorianDate;
39 import de.bea.domingo.util.GregorianDateTime;
40 import de.bea.domingo.util.GregorianTime;
41
42 /***
43 * Proxy to the Notes international settings.
44 *
45 * @author <a href=mailto:kriede@users.sourceforge.net>Kurt Riede</a>
46 */
47 public final class InternationalProxy implements Serializable {
48
49 private static final int TZ_MINUTES_OFFSET = 100;
50
51 /*** serial version ID for serialization. */
52 private static final long serialVersionUID = -2957715893148795275L;
53
54 /*** Minutes per hour. */
55 private static final int MINUTES_PER_HOUR = 60;
56
57 /*** Seconds per minute. */
58 private static final int SECONDS_PER_MINUTE = 60;
59
60 /*** Milli seconds per hour. */
61 private static final int MILLIS_PER_SECOND = 1000;
62
63 /*** Milli seconds per hour. */
64 private static final int MILLIS_PER_MINUTE = MILLIS_PER_SECOND * SECONDS_PER_MINUTE;
65
66 /*** Milli seconds per hour. */
67 private static final int MILLIS_PER_HOUR = MILLIS_PER_MINUTE * MINUTES_PER_HOUR;
68
69 /*** Internationalized resources. */
70 private static final Resources RESOURCES = ResourceManager.getPackageResources(NotesProxyFactory.class);
71
72 /*** Default separator for date components. */
73 public static final char DATE_SEPARATOR_DEFAULT = '/';
74
75 /*** Default separator for time components. */
76 public static final char TIME_SEPARATOR_DEFAULT = ':';
77
78 /***
79 * Date format ordered as day/month/year. The slashes will be replaced with
80 * the concrete date separator.
81 */
82 public static final String DATE_FORMAT_DMY = "dd/MM/yy";
83
84 /***
85 * Date format ordered as month/day/year. The slashes will be replaced with
86 * the concrete date separator.
87 */
88 public static final String DATE_FORMAT_MDY = "MM/dd/yy";
89
90 /***
91 * Date format ordered as year/month/day. The slashes will be replaced with
92 * the concrete date separator.
93 */
94 public static final String DATE_FORMAT_YMD = "yy/MM/dd";
95
96 /***
97 * Default date format. The slashes will be replaced with the concrete date
98 * separator.
99 */
100 public static final String DATE_FORMAT_DEFAULT = DATE_FORMAT_DMY;
101
102 /***
103 * Time format in 24 hour display. The colons will be replaced with the
104 * concrete date separator.
105 */
106 public static final String TIME_FORMAT_24 = "HH:mm:ss";
107
108 /***
109 * Default time format. The colons will be replaced with the concrete date
110 * separator.
111 */
112 public static final String TIME_FORMAT_DEFAULT = "hh:mm:ss a";
113
114 /*** Format string for time zones in format of RFC 822 , e.g. <tt>"-0800"</tt>. */
115 public static final String TIMEZONE_FORMAT = "Z";
116
117 /*** Base Monitor instance. */
118 private DNotesMonitor monitor = null;
119
120 /*** Associated session (local call or IIOP). */
121 private Session session;
122
123 /***
124 * Constructor.
125 *
126 * @param theSession the Notes Session
127 * @param theMonitor the monitor
128 */
129 public InternationalProxy(final Session theSession, final DNotesMonitor theMonitor) {
130 super();
131 this.session = theSession;
132 this.monitor = theMonitor;
133 }
134
135 /***
136 * Get the current monitor.
137 *
138 * @return current monitor
139 * @see de.bea.domingo.DNotesFactory#getMonitor()
140 */
141 protected DNotesMonitor getMonitor() {
142 return monitor;
143 }
144
145 /***
146 * Creates a Notes <code>DateTime</code> instance from a
147 * <code>java.util.Calendar</code>.
148 *
149 * @param calendar the calendar to convert
150 * @return DateTime a Notes DateTime object
151 */
152 protected DateTime createDateTime(final Calendar calendar) {
153 final Calendar correct = (Calendar) calendar.clone();
154 try {
155 TimeZone gmtZone = TimeZone.getTimeZone("GMT");
156 correct.setTimeZone(gmtZone);
157 TimeZone timeZone = calendar.getTimeZone();
158 correct.add(Calendar.SECOND, -timeZone.getOffset(correct.getTimeInMillis()) / MILLIS_PER_SECOND);
159 final DateTime dateTime = session.createDateTime(correct);
160 int zoneValue = getNotesTimeZoneValue(timeZone, calendar.getTimeInMillis());
161 dateTime.convertToZone(zoneValue, timeZone.inDaylightTime(calendar.getTime()));
162 if (!isDateSet(correct)) {
163 dateTime.setAnyDate();
164 } else if (!isTimeSet(correct)) {
165 dateTime.setAnyTime();
166 }
167 return dateTime;
168 } catch (NotesException e) {
169 error("Cannot convert calendar to DateTime", e);
170 }
171 return null;
172 }
173
174 /***
175 * Converts a Notes DateTime object into a Calendar. <p>Milli seconds are
176 * cleared in all cases.</p>
177 *
178 * @param dateTime a Notes DateTime object
179 * @return a Calendar
180 */
181 protected Calendar createCalendar(final DateTime dateTime) {
182 final Date date = createDate(dateTime);
183 if (date == null) {
184 return null;
185 }
186 TimeZone zone = getTimeZone(dateTime);
187 Calendar calendar = Calendar.getInstance(zone);
188 calendar.setTime(date);
189 if (!isTimeSet(dateTime)) {
190 calendar.add(Calendar.MILLISECOND, zone.getOffset(calendar.getTimeInMillis()));
191 return new GregorianDate(calendar.getTime());
192 } else if (!isDateSet(dateTime)) {
193 calendar.add(Calendar.MILLISECOND, zone.getOffset(calendar.getTimeInMillis()));
194 return new GregorianTime(calendar);
195 } else {
196 calendar.add(Calendar.MILLISECOND, -getOffset(dateTime));
197 calendar.add(Calendar.MILLISECOND, -zone.getOffset(calendar.getTimeInMillis()));
198 return new GregorianDateTime(calendar);
199 }
200 }
201
202 /***
203 * Returns the Java time zone as expected when converting a given Notes
204 * date/time instance to a Java date..
205 *
206 * <p>In case of local calls, the converted Java date is in the local default time zone.
207 * In case of remote calls (DIIOP), the converted Java date is always in GMT.
208 *
209 * @param dateTime a Notes date/tim einstance
210 * @return The time zone of the date/time instance when converted to Java
211 */
212 private TimeZone getTimeZone(final DateTime dateTime) {
213 TimeZone zone = TimeZone.getDefault();
214 if (dateTime.getClass().getName().equals("lotus.domino.cso.DateTime")) {
215 zone = TimeZone.getTimeZone("GMT");
216 }
217 return zone;
218 }
219
220 /***
221 * Creates a Notes <code>DateRange</code> instance from two
222 * <code>java.util.Calendar</code>s.
223 *
224 * @param calendar1 the start calendar to convert
225 * @param calendar2 the end calendar to convert
226 * @return DateRange a Notes DateTime object
227 */
228 protected DateRange createDateRange(final Calendar calendar1, final Calendar calendar2) {
229 final DateTime time1 = createDateTime(calendar1);
230 final DateTime time2 = createDateTime(calendar2);
231 try {
232 return session.createDateRange(time1, time2);
233 } catch (NotesException e) {
234 error("Cannot convert DDateRange to DateRange", e);
235 }
236 return null;
237 }
238
239 /***
240 * Computes the Notes time zone value for a given time zone.
241 *
242 * <p>For time zones that are not a full hour increment from GMT, the
243 * return value is an integer in the format <code>mmhh</code> where
244 * <code>mm</code> is the minutes component of the time relative to GMT and
245 * <code>hh</code> is the hours component of the time relative to GMT.
246 *
247 * <p>The offset might have changed in history. For this case, a reference
248 * date is given to find the correct offset at the given date.</p>
249 *
250 * @param zone a Java time zone
251 * @param date a reference date for computing the zone
252 * @return the Notes time zone value
253 * @see #getOffset(DateTime)
254 */
255 static int getNotesTimeZoneValue(final TimeZone zone, final long date) {
256 int offsetMinutes = zone.getRawOffset() / MILLIS_PER_MINUTE;
257 int offsetHours = offsetMinutes / MINUTES_PER_HOUR;
258 if (MINUTES_PER_HOUR * offsetHours == offsetMinutes) {
259 return -offsetHours;
260 } else {
261 return -(offsetMinutes % MINUTES_PER_HOUR) * TZ_MINUTES_OFFSET - offsetHours;
262 }
263 }
264
265 /***
266 * Returns the raw offset of the time zone in milli seconds for a given Notes time zone value.
267 *
268 * @param dateTime a Notes date/time value
269 * @return war offset in milli seconds as used in Java time zones
270 * @see #getNotesTimeZoneValue(TimeZone, long)
271 */
272 int getOffset(final DateTime dateTime) {
273 int zoneValue;
274 try {
275 zoneValue = dateTime.getTimeZone();
276 int offset = getRawOffset(zoneValue);
277 if (dateTime.isDST()) {
278 offset -= MILLIS_PER_HOUR;
279 }
280 return offset;
281 } catch (NotesException e) {
282 getMonitor().warn("Cannot get time zone from Notes date/time", e);
283 return 0;
284 }
285 }
286
287 /***
288 * Returns the offset in milli seconds for a given Notes time zone value.
289 *
290 * @param zoneValue Notes time zone value
291 * @return offset in milli seconds as used in Java time zones
292 * @see #getOffset(DateTime)
293 */
294 protected static int getRawOffset(final int zoneValue) {
295 if (zoneValue < TZ_MINUTES_OFFSET && zoneValue > -TZ_MINUTES_OFFSET) {
296 return zoneValue * MILLIS_PER_HOUR;
297 } else {
298 return (zoneValue % TZ_MINUTES_OFFSET) * MILLIS_PER_HOUR + (zoneValue / TZ_MINUTES_OFFSET) * MILLIS_PER_MINUTE;
299 }
300 }
301
302 /***
303 * Checks if a DateTime object defines a date.
304 *
305 * @param dateTime a Notes DateTime object
306 * @return <code>true</code> if a date is defined, else <code>false</code>
307 */
308 protected boolean isDateSet(final DateTime dateTime) {
309 try {
310 return !"".equals(dateTime.getDateOnly());
311 } catch (NotesException e) {
312 error("Cannot check if date is set.", e);
313 return false;
314 }
315 }
316
317 /***
318 * Checks if a DateTime object defines a time.
319 *
320 * @param dateTime a Notes DateTime object
321 * @return <code>true</code> if a time is defined, else <code>false</code>
322 */
323 protected boolean isTimeSet(final DateTime dateTime) {
324 try {
325 return !"".equals(dateTime.getTimeOnly());
326 } catch (NotesException e) {
327 error("Cannot check if time is set.", e);
328 return false;
329 }
330 }
331
332 /***
333 * Checks if a Calendar object defines a time.
334 *
335 * @param calendar a Calendar object
336 * @return <code>true</code> if a time is defined, else <code>false</code>
337 */
338 protected boolean isTimeSet(final Calendar calendar) {
339 return calendar.isSet(Calendar.HOUR_OF_DAY) || calendar.isSet(Calendar.MINUTE) || calendar.isSet(Calendar.SECOND)
340 || calendar.isSet(Calendar.MILLISECOND);
341 }
342
343 /***
344 * Checks if a Calendar object defines a date.
345 *
346 * @param calendar a Calendar object
347 * @return <code>true</code> if a date is defined, else <code>false</code>
348 */
349 protected boolean isDateSet(final Calendar calendar) {
350 return calendar.isSet(Calendar.YEAR) || calendar.isSet(Calendar.MONTH) || calendar.isSet(Calendar.DATE);
351 }
352
353 /***
354 * Converts a Notes DateTime into a <code>java.util.Calendar</code>. For
355 * invalid dates (e.g. no date- and no time-component available) a null
356 * value is returned.
357 *
358 * @param dateTime a Notes DateTime object
359 * @return a <code>java.util.Calendar</code> object or <code>null</code>
360 * if dateTime is invalid
361 */
362 private Date createDate(final DateTime dateTime) {
363 if (dateTime == null) {
364 return null;
365 }
366 try {
367 return dateTime.toJavaDate();
368 } catch (NotesException e) {
369 return null;
370 }
371 }
372
373 /***
374 * Sends an error to the current monitor.
375 *
376 * @param message the message
377 * @param e the NotesException, can be null
378 */
379 protected void error(final String message, final NotesException e) {
380 getMonitor().error(this.getClass().getName() + ":" + message, new DominoException(e));
381 }
382
383 /***
384 * Sends a warning to the current monitor.
385 *
386 * @param message the message
387 * @param e the NotesException, can be null
388 */
389 protected void info(final String message, final NotesException e) {
390 getMonitor().info(this.getClass().getName() + ":" + message, new DominoException(e));
391 }
392
393 /***
394 * Sends an info to the current monitor.
395 *
396 * @param message the message
397 * @param e the NotesException, can be null
398 */
399 protected void warn(final String message, final NotesException e) {
400 getMonitor().warn(this.getClass().getName() + ":" + message, new DominoException(e));
401 }
402 }