1// =================================================================================================
2// ADOBE SYSTEMS INCORPORATED
3// Copyright 2006 Adobe Systems Incorporated
4// All Rights Reserved
5//
6// NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the terms
7// of the Adobe license agreement accompanying it.
8// =================================================================================================
9
10package com.adobe.xmp.impl;
11
12import java.util.Calendar;
13import java.util.Date;
14import java.util.GregorianCalendar;
15import java.util.Locale;
16import java.util.TimeZone;
17
18import com.adobe.xmp.XMPDateTime;
19import com.adobe.xmp.XMPException;
20
21
22/**
23 * The implementation of <code>XMPDateTime</code>. Internally a <code>calendar</code> is used
24 * plus an additional nano seconds field, because <code>Calendar</code> supports only milli
25 * seconds. The <code>nanoSeconds</code> convers only the resolution beyond a milli second.
26 *
27 * @since 16.02.2006
28 */
29public class XMPDateTimeImpl implements XMPDateTime
30{
31	/** */
32	private int year = 0;
33	/** */
34	private int month = 0;
35	/** */
36	private int day = 0;
37	/** */
38	private int hour = 0;
39	/** */
40	private int minute = 0;
41	/** */
42	private int second = 0;
43	/** Use the unversal time as default */
44	private TimeZone timeZone = TimeZone.getTimeZone("UTC");
45	/**
46	 * The nano seconds take micro and nano seconds, while the milli seconds are in the calendar.
47	 */
48	private int nanoSeconds;
49
50
51	/**
52	 * Creates an <code>XMPDateTime</code>-instance with the current time in the default time
53	 * zone.
54	 */
55	public XMPDateTimeImpl()
56	{
57		// EMPTY
58	}
59
60
61	/**
62	 * Creates an <code>XMPDateTime</code>-instance from a calendar.
63	 *
64	 * @param calendar a <code>Calendar</code>
65	 */
66	public XMPDateTimeImpl(Calendar calendar)
67	{
68		// extract the date and timezone from the calendar provided
69        Date date = calendar.getTime();
70        TimeZone zone = calendar.getTimeZone();
71
72        // put that date into a calendar the pretty much represents ISO8601
73        // I use US because it is close to the "locale" for the ISO8601 spec
74        GregorianCalendar intCalendar =
75        	(GregorianCalendar) Calendar.getInstance(Locale.US);
76        intCalendar.setGregorianChange(new Date(Long.MIN_VALUE));
77        intCalendar.setTimeZone(zone);
78        intCalendar.setTime(date);
79
80		this.year = intCalendar.get(Calendar.YEAR);
81		this.month = intCalendar.get(Calendar.MONTH) + 1; // cal is from 0..12
82		this.day = intCalendar.get(Calendar.DAY_OF_MONTH);
83		this.hour = intCalendar.get(Calendar.HOUR_OF_DAY);
84		this.minute = intCalendar.get(Calendar.MINUTE);
85		this.second = intCalendar.get(Calendar.SECOND);
86		this.nanoSeconds = intCalendar.get(Calendar.MILLISECOND) * 1000000;
87		this.timeZone = intCalendar.getTimeZone();
88	}
89
90
91	/**
92	 * Creates an <code>XMPDateTime</code>-instance from
93	 * a <code>Date</code> and a <code>TimeZone</code>.
94	 *
95	 * @param date a date describing an absolute point in time
96	 * @param timeZone a TimeZone how to interpret the date
97	 */
98	public XMPDateTimeImpl(Date date, TimeZone timeZone)
99	{
100		GregorianCalendar calendar = new GregorianCalendar(timeZone);
101		calendar.setTime(date);
102		this.year = calendar.get(Calendar.YEAR);
103		this.month = calendar.get(Calendar.MONTH) + 1; // cal is from 0..12
104		this.day = calendar.get(Calendar.DAY_OF_MONTH);
105		this.hour = calendar.get(Calendar.HOUR_OF_DAY);
106		this.minute = calendar.get(Calendar.MINUTE);
107		this.second = calendar.get(Calendar.SECOND);
108		this.nanoSeconds = calendar.get(Calendar.MILLISECOND) * 1000000;
109		this.timeZone = timeZone;
110	}
111
112
113	/**
114	 * Creates an <code>XMPDateTime</code>-instance from an ISO 8601 string.
115	 *
116	 * @param strValue an ISO 8601 string
117	 * @throws XMPException If the string is a non-conform ISO 8601 string, an exception is thrown
118	 */
119	public XMPDateTimeImpl(String strValue) throws XMPException
120	{
121		ISO8601Converter.parse(strValue, this);
122	}
123
124
125	/**
126	 * @see XMPDateTime#getYear()
127	 */
128	public int getYear()
129	{
130		return year;
131	}
132
133
134	/**
135	 * @see XMPDateTime#setYear(int)
136	 */
137	public void setYear(int year)
138	{
139		this.year = Math.min(Math.abs(year), 9999);
140	}
141
142
143	/**
144	 * @see XMPDateTime#getMonth()
145	 */
146	public int getMonth()
147	{
148		return month;
149	}
150
151
152	/**
153	 * @see XMPDateTime#setMonth(int)
154	 */
155	public void setMonth(int month)
156	{
157		if (month < 1)
158		{
159			this.month = 1;
160		}
161		else if (month > 12)
162		{
163			this.month = 12;
164		}
165		else
166		{
167			this.month = month;
168		}
169	}
170
171
172	/**
173	 * @see XMPDateTime#getDay()
174	 */
175	public int getDay()
176	{
177		return day;
178	}
179
180
181	/**
182	 * @see XMPDateTime#setDay(int)
183	 */
184	public void setDay(int day)
185	{
186		if (day < 1)
187		{
188			this.day = 1;
189		}
190		else if (day > 31)
191		{
192			this.day = 31;
193		}
194		else
195		{
196			this.day = day;
197		}
198	}
199
200
201	/**
202	 * @see XMPDateTime#getHour()
203	 */
204	public int getHour()
205	{
206		return hour;
207	}
208
209
210	/**
211	 * @see XMPDateTime#setHour(int)
212	 */
213	public void setHour(int hour)
214	{
215		this.hour = Math.min(Math.abs(hour), 23);
216	}
217
218
219	/**
220	 * @see XMPDateTime#getMinute()
221	 */
222	public int getMinute()
223	{
224		return minute;
225	}
226
227
228	/**
229	 * @see XMPDateTime#setMinute(int)
230	 */
231	public void setMinute(int minute)
232	{
233		this.minute = Math.min(Math.abs(minute), 59);
234	}
235
236
237	/**
238	 * @see XMPDateTime#getSecond()
239	 */
240	public int getSecond()
241	{
242		return second;
243	}
244
245
246	/**
247	 * @see XMPDateTime#setSecond(int)
248	 */
249	public void setSecond(int second)
250	{
251		this.second = Math.min(Math.abs(second), 59);
252	}
253
254
255	/**
256	 * @see XMPDateTime#getNanoSecond()
257	 */
258	public int getNanoSecond()
259	{
260		return nanoSeconds;
261	}
262
263
264	/**
265	 * @see XMPDateTime#setNanoSecond(int)
266	 */
267	public void setNanoSecond(int nanoSecond)
268	{
269		this.nanoSeconds = nanoSecond;
270	}
271
272
273	/**
274	 * @see Comparable#compareTo(Object)
275	 */
276	public int compareTo(Object dt)
277	{
278		long d = getCalendar().getTimeInMillis()
279				- ((XMPDateTime) dt).getCalendar().getTimeInMillis();
280		if (d != 0)
281		{
282			return (int) (d % 2);
283		}
284		else
285		{
286			// if millis are equal, compare nanoseconds
287			d = nanoSeconds - ((XMPDateTime) dt).getNanoSecond();
288			return (int) (d % 2);
289		}
290	}
291
292
293	/**
294	 * @see XMPDateTime#getTimeZone()
295	 */
296	public TimeZone getTimeZone()
297	{
298		return timeZone;
299	}
300
301
302	/**
303	 * @see XMPDateTime#setTimeZone(TimeZone)
304	 */
305	public void setTimeZone(TimeZone timeZone)
306	{
307		this.timeZone = timeZone;
308	}
309
310
311	/**
312	 * @see XMPDateTime#getCalendar()
313	 */
314	public Calendar getCalendar()
315	{
316		GregorianCalendar calendar = (GregorianCalendar) Calendar.getInstance(Locale.US);
317		calendar.setGregorianChange(new Date(Long.MIN_VALUE));
318		calendar.setTimeZone(timeZone);
319		calendar.set(Calendar.YEAR, year);
320		calendar.set(Calendar.MONTH, month - 1);
321		calendar.set(Calendar.DAY_OF_MONTH, day);
322		calendar.set(Calendar.HOUR_OF_DAY, hour);
323		calendar.set(Calendar.MINUTE, minute);
324		calendar.set(Calendar.SECOND, second);
325		calendar.set(Calendar.MILLISECOND, nanoSeconds / 1000000);
326		return calendar;
327	}
328
329
330	/**
331	 * @see XMPDateTime#getISO8601String()
332	 */
333	public String getISO8601String()
334	{
335		return ISO8601Converter.render(this);
336	}
337
338
339	/**
340	 * @return Returns the ISO string representation.
341	 */
342	public String toString()
343	{
344		return getISO8601String();
345	}
346}