1 /*****************************************************************************/
2 // Copyright 2006-2019 Adobe Systems Incorporated
3 // All Rights Reserved.
4 //
5 // NOTICE:  Adobe permits you to use, modify, and distribute this file in
6 // accordance with the terms of the Adobe license agreement accompanying it.
7 /*****************************************************************************/
8 
9 /** \file
10  * Functions and classes for working with dates and times in DNG files.
11  */
12 
13 /*****************************************************************************/
14 
15 #ifndef __dng_date_time__
16 #define __dng_date_time__
17 
18 /*****************************************************************************/
19 
20 #include "dng_classes.h"
21 #include "dng_string.h"
22 #include "dng_types.h"
23 
24 /*****************************************************************************/
25 
26 /// \brief Class for holding a date/time and converting to and from relevant
27 /// date/time formats
28 
29 class dng_date_time
30 	{
31 
32 	public:
33 
34 		uint32 fYear;
35 		uint32 fMonth;
36 		uint32 fDay;
37 		uint32 fHour;
38 		uint32 fMinute;
39 		uint32 fSecond;
40 
41 	public:
42 
43 		/// Construct an invalid date/time
44 
45 		dng_date_time ();
46 
47 		/// Construct a date/time with specific values.
48 		/// \param year Year to use as actual integer value, such as 2006.
49 		/// \param month Month to use from 1 - 12, where 1 is January.
50 		/// \param day Day of month to use from 1 -31, where 1 is the first.
51 		/// \param hour Hour of day to use from 0 - 23, where 0 is midnight.
52 		/// \param minute Minute of hour to use from 0 - 59.
53 		/// \param second Second of minute to use from 0 - 59.
54 
55 		dng_date_time (uint32 year,
56 					   uint32 month,
57 					   uint32 day,
58 					   uint32 hour,
59 					   uint32 minute,
60 					   uint32 second);
61 
62 		/// Predicate to determine if a date is valid.
63 		/// \retval true if all fields are within range.
64 
65 		bool IsValid () const;
66 
67 		/// Predicate to determine if a date is invalid.
68 		/// \retval true if any field is out of range.
69 
NotValid()70 		bool NotValid () const
71 			{
72 			return !IsValid ();
73 			}
74 
75 		/// Equal operator.
76 
77 		bool operator== (const dng_date_time &dt) const
78 			{
79 			return fYear   == dt.fYear   &&
80 				   fMonth  == dt.fMonth  &&
81 				   fDay    == dt.fDay    &&
82 				   fHour   == dt.fHour   &&
83 				   fMinute == dt.fMinute &&
84 				   fSecond == dt.fSecond;
85 			}
86 
87 		// Not-equal operator.
88 
89 		bool operator!= (const dng_date_time &dt) const
90 			{
91 			return !(*this == dt);
92 			}
93 
94 		/// Set date to an invalid value.
95 
96 		void Clear ();
97 
98 		/// Parse an EXIF format date string.
99 		/// \param s Input date string to parse.
100 		/// \retval true if date was parsed successfully and date is valid.
101 
102 		bool Parse (const char *s);
103 
104 	};
105 
106 /*****************************************************************************/
107 
108 /// \brief Class for holding a time zone.
109 
110 class dng_time_zone
111 	{
112 
113 	private:
114 
115 		enum
116 			{
117 
118 			kMaxOffsetHours = 15,
119 			kMinOffsetHours = -kMaxOffsetHours,
120 
121 			kMaxOffsetMinutes = kMaxOffsetHours * 60,
122 			kMinOffsetMinutes = kMinOffsetHours * 60,
123 
124 			kInvalidOffset = kMinOffsetMinutes - 1
125 
126 			};
127 
128 		// Offset from GMT in minutes.  Positive numbers are
129 		// ahead of GMT, negative number are behind GMT.
130 
131 		int32 fOffsetMinutes;
132 
133 	public:
134 
dng_time_zone()135 		dng_time_zone ()
136 			:	fOffsetMinutes (kInvalidOffset)
137 			{
138 			}
139 
Clear()140 		void Clear ()
141 			{
142 			fOffsetMinutes = kInvalidOffset;
143 			}
144 
SetOffsetHours(int32 offset)145 		void SetOffsetHours (int32 offset)
146 			{
147 			fOffsetMinutes = offset * 60;
148 			}
149 
SetOffsetMinutes(int32 offset)150 		void SetOffsetMinutes (int32 offset)
151 			{
152 			fOffsetMinutes = offset;
153 			}
154 
SetOffsetSeconds(int32 offset)155 		void SetOffsetSeconds (int32 offset)
156 			{
157 			fOffsetMinutes = (offset > 0) ? ((offset + 30) / 60)
158 										  : ((offset - 30) / 60);
159 			}
160 
IsValid()161 		bool IsValid () const
162 			{
163 			return fOffsetMinutes >= kMinOffsetMinutes &&
164 				   fOffsetMinutes <= kMaxOffsetMinutes;
165 			}
166 
NotValid()167 		bool NotValid () const
168 			{
169 			return !IsValid ();
170 			}
171 
OffsetMinutes()172 		int32 OffsetMinutes () const
173 			{
174 			return fOffsetMinutes;
175 			}
176 
IsExactHourOffset()177 		bool IsExactHourOffset () const
178 			{
179 			return IsValid () && ((fOffsetMinutes % 60) == 0);
180 			}
181 
ExactHourOffset()182 		int32 ExactHourOffset () const
183 			{
184 			return fOffsetMinutes / 60;
185 			}
186 
187 		dng_string Encode_ISO_8601 () const;
188 
189 	};
190 
191 /*****************************************************************************/
192 
193 /// \brief Class for holding complete data/time/zone information.
194 
195 class dng_date_time_info
196 	{
197 
198 	private:
199 
200 		// Is only the date valid and not the time?
201 
202 		bool fDateOnly;
203 
204 		// Date and time.
205 
206 		dng_date_time fDateTime;
207 
208 		// Subseconds string (stored in a separate tag in EXIF).
209 
210 		dng_string fSubseconds;
211 
212 		// Time zone, if known.
213 
214 		dng_time_zone fTimeZone;
215 
216 	public:
217 
218 		dng_date_time_info ();
219 
220 		bool IsValid () const;
221 
NotValid()222 		bool NotValid () const
223 			{
224 			return !IsValid ();
225 			}
226 
Clear()227 		void Clear ()
228 			{
229 			*this = dng_date_time_info ();
230 			}
231 
IsDateOnly()232         bool IsDateOnly () const
233             {
234             return fDateOnly;
235             }
236 
DateTime()237 		const dng_date_time & DateTime () const
238 			{
239 			return fDateTime;
240 			}
241 
SetDateTime(const dng_date_time & dt)242 		void SetDateTime (const dng_date_time &dt)
243 			{
244 			fDateOnly = false;
245 			fDateTime = dt;
246 			}
247 
Subseconds()248 		const dng_string & Subseconds () const
249 			{
250 			return fSubseconds;
251 			}
252 
SetSubseconds(const dng_string & s)253 		void SetSubseconds (const dng_string &s)
254 			{
255 			fSubseconds = s;
256 			}
257 
TimeZone()258 		const dng_time_zone & TimeZone () const
259 			{
260 			return fTimeZone;
261 			}
262 
SetZone(const dng_time_zone & zone)263 		void SetZone (const dng_time_zone &zone)
264 			{
265 			fTimeZone = zone;
266 			}
267 
ClearZone()268         void ClearZone ()
269             {
270             fTimeZone.Clear ();
271             }
272 
273         void SetOffsetTime (const dng_string &s);
274 
275         dng_string OffsetTime () const;
276 
277 		void Decode_ISO_8601 (const char *s);
278 
279 		dng_string Encode_ISO_8601 () const;
280 
281 		void Decode_IPTC_Date (const char *s);
282 
283 		dng_string Encode_IPTC_Date () const;
284 
285 		void Decode_IPTC_Time (const char *s);
286 
287 		dng_string Encode_IPTC_Time () const;
288 
289 	private:
290 
291 		void SetDate (uint32 year,
292 					  uint32 month,
293 					  uint32 day);
294 
295 		void SetTime (uint32 hour,
296 					  uint32 minute,
297 					  uint32 second);
298 
299 	};
300 
301 /*****************************************************************************/
302 
303 /// Get the current date/time and timezone.
304 /// \param info Receives current data/time/zone.
305 
306 void CurrentDateTimeAndZone (dng_date_time_info &info);
307 
308 /*****************************************************************************/
309 
310 /// Convert UNIX "seconds since Jan 1, 1970" time to a dng_date_time
311 
312 void DecodeUnixTime (uint32 unixTime, dng_date_time &dt);
313 
314 /*****************************************************************************/
315 
316 /// Return timezone of current location at a given date.
317 /// \param dt Date at which to compute timezone difference. (For example, used
318 /// to determine Daylight Savings, etc.)
319 /// \retval Time zone for date/time dt.
320 
321 dng_time_zone LocalTimeZone (const dng_date_time &dt);
322 
323 /*****************************************************************************/
324 
325 /// Tag to encode date represenation format
326 
327 enum dng_date_time_format
328 	{
329 	dng_date_time_format_unknown            = 0, /// Date format not known
330 	dng_date_time_format_exif               = 1, /// EXIF date string
331 	dng_date_time_format_unix_little_endian = 2, /// 32-bit UNIX time as 4-byte little endian
332 	dng_date_time_format_unix_big_endian    = 3  /// 32-bit UNIX time as 4-byte big endian
333 	};
334 
335 /*****************************************************************************/
336 
337 /// \brief Store file offset from which date was read.
338 ///
339 /// Used internally by Adobe to update date in original file.
340 /// \warning Use at your own risk.
341 
342 class dng_date_time_storage_info
343 	{
344 
345 	private:
346 
347 		uint64 fOffset;
348 
349 		dng_date_time_format fFormat;
350 
351 	public:
352 
353 		/// The default constructor initializes to an invalid state.
354 
355 		dng_date_time_storage_info ();
356 
357 		/// Construct with file offset and date format.
358 
359 		dng_date_time_storage_info (uint64 offset,
360 									dng_date_time_format format);
361 
362 		/// Predicate to determine if an offset is valid.
363 		/// \retval true if offset is valid.
364 
365 		bool IsValid () const;
366 
367 		// The accessors throw if the data is not valid.
368 
369 		/// Getter for offset in file.
370 		/// \exception dng_exception with fErrorCode equal to dng_error_unknown
371 		/// if offset is not valid.
372 
373 		uint64 Offset () const;
374 
375 		/// Get for format date was originally stored in file. Throws a
376 		/// dng_error_unknown exception if offset is invalid.
377 		/// \exception dng_exception with fErrorCode equal to dng_error_unknown
378 		/// if offset is not valid.
379 
380 		dng_date_time_format Format () const;
381 
382 	};
383 
384 /*****************************************************************************/
385 
386 #endif
387 
388 /*****************************************************************************/
389