Home | History | Annotate | Download | only in source
      1 /*****************************************************************************/
      2 // Copyright 2006-2008 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 /* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_date_time.cpp#2 $ */
     10 /* $DateTime: 2012/06/01 07:28:57 $ */
     11 /* $Change: 832715 $ */
     12 /* $Author: tknoll $ */
     13 
     14 /*****************************************************************************/
     15 
     16 #include "dng_date_time.h"
     17 
     18 #include "dng_exceptions.h"
     19 #include "dng_mutex.h"
     20 #include "dng_stream.h"
     21 #include "dng_string.h"
     22 #include "dng_utils.h"
     23 
     24 #include <time.h>
     25 
     26 #if qMacOS
     27 #include <TargetConditionals.h>
     28 #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
     29 #include <MobileCoreServices/MobileCoreServices.h>
     30 #else
     31 #include <CoreServices/CoreServices.h>
     32 #endif  // TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
     33 #endif  // qMacOS
     34 
     35 #if qWinOS
     36 #include <windows.h>
     37 #endif
     38 
     39 /******************************************************************************/
     40 
     41 // MWG says don't use fake time zones in XMP, but there is some
     42 // old software that requires them to work correctly.
     43 
     44 bool gDNGUseFakeTimeZonesInXMP = false;
     45 
     46 /******************************************************************************/
     47 
     48 dng_date_time::dng_date_time ()
     49 
     50 	:	fYear   (0)
     51 	,	fMonth  (0)
     52 	,	fDay    (0)
     53 	,	fHour   (0)
     54 	,	fMinute (0)
     55 	,	fSecond (0)
     56 
     57 	{
     58 
     59 	}
     60 
     61 /******************************************************************************/
     62 
     63 dng_date_time::dng_date_time (uint32 year,
     64 					  		  uint32 month,
     65 					  		  uint32 day,
     66 					  		  uint32 hour,
     67 					  		  uint32 minute,
     68 					  		  uint32 second)
     69 
     70 	:	fYear   (year)
     71 	,	fMonth  (month)
     72 	,	fDay    (day)
     73 	,	fHour   (hour)
     74 	,	fMinute (minute)
     75 	,	fSecond (second)
     76 
     77 	{
     78 
     79 	}
     80 
     81 /******************************************************************************/
     82 
     83 bool dng_date_time::IsValid () const
     84 	{
     85 
     86 	return fYear   >= 1 && fYear   <= 9999 &&
     87 		   fMonth  >= 1 && fMonth  <= 12   &&
     88 		   fDay    >= 1 && fDay    <= 31   &&
     89 		   fHour   <= 23   &&
     90 		   fMinute <= 59   &&
     91 		   fSecond <= 59;
     92 
     93 	}
     94 
     95 /*****************************************************************************/
     96 
     97 void dng_date_time::Clear ()
     98 	{
     99 
    100 	*this = dng_date_time ();
    101 
    102 	}
    103 
    104 /*****************************************************************************/
    105 
    106 static uint32 DateTimeParseU32 (const char *&s)
    107 	{
    108 
    109 	uint32 x = 0;
    110 
    111 	while (*s == ' ' || *s == ':')
    112 		s++;
    113 
    114 	while (*s >= '0' && *s <= '9')
    115 		{
    116 		x = SafeUint32Mult(x, 10);
    117 		x = SafeUint32Add(x, (uint32) (*(s++) - '0'));
    118 		}
    119 
    120 	return x;
    121 
    122 	}
    123 
    124 /*****************************************************************************/
    125 
    126 bool dng_date_time::Parse (const char *s)
    127 	{
    128 
    129 	fYear   = DateTimeParseU32 (s);
    130 	fMonth  = DateTimeParseU32 (s);
    131 	fDay    = DateTimeParseU32 (s);
    132 	fHour   = DateTimeParseU32 (s);
    133 	fMinute = DateTimeParseU32 (s);
    134 	fSecond = DateTimeParseU32 (s);
    135 
    136 	return IsValid ();
    137 
    138 	}
    139 
    140 /*****************************************************************************/
    141 
    142 dng_string dng_time_zone::Encode_ISO_8601 () const
    143 	{
    144 
    145 	dng_string result;
    146 
    147 	if (IsValid ())
    148 		{
    149 
    150 		if (OffsetMinutes () == 0)
    151 			{
    152 
    153 			result.Set ("Z");
    154 
    155 			}
    156 
    157 		else
    158 			{
    159 
    160 			char s [64];
    161 
    162 			int offset = OffsetMinutes ();
    163 
    164 			if (offset > 0)
    165 				{
    166 
    167 				sprintf (s, "+%02d:%02d", offset / 60, offset % 60);
    168 
    169 				}
    170 
    171 			else
    172 				{
    173 
    174 				offset = -offset;
    175 
    176 				sprintf (s, "-%02d:%02d", offset / 60, offset % 60);
    177 
    178 				}
    179 
    180 			result.Set (s);
    181 
    182 			}
    183 
    184 		}
    185 
    186 	return result;
    187 
    188 	}
    189 
    190 /*****************************************************************************/
    191 
    192 dng_date_time_info::dng_date_time_info ()
    193 
    194 	:	fDateOnly   (true)
    195 	,	fDateTime   ()
    196 	,	fSubseconds ()
    197 	,	fTimeZone   ()
    198 
    199 	{
    200 
    201 	}
    202 
    203 /*****************************************************************************/
    204 
    205 bool dng_date_time_info::IsValid () const
    206 	{
    207 
    208 	return fDateTime.IsValid ();
    209 
    210 	}
    211 
    212 /*****************************************************************************/
    213 
    214 void dng_date_time_info::SetDate (uint32 year,
    215 								  uint32 month,
    216 								  uint32 day)
    217 	{
    218 
    219 	fDateTime.fYear  = year;
    220 	fDateTime.fMonth = month;
    221 	fDateTime.fDay   = day;
    222 
    223 	}
    224 
    225 /*****************************************************************************/
    226 
    227 void dng_date_time_info::SetTime (uint32 hour,
    228 								  uint32 minute,
    229 								  uint32 second)
    230 	{
    231 
    232 	fDateOnly = false;
    233 
    234 	fDateTime.fHour   = hour;
    235 	fDateTime.fMinute = minute;
    236 	fDateTime.fSecond = second;
    237 
    238 	}
    239 
    240 /*****************************************************************************/
    241 
    242 void dng_date_time_info::Decode_ISO_8601 (const char *s)
    243 	{
    244 
    245 	Clear ();
    246 
    247 	uint32 len = (uint32) strlen (s);
    248 
    249 	if (!len)
    250 		{
    251 		return;
    252 		}
    253 
    254 	unsigned year  = 0;
    255 	unsigned month = 0;
    256 	unsigned day   = 0;
    257 
    258 	if (sscanf (s,
    259 				"%u-%u-%u",
    260 				&year,
    261 				&month,
    262 				&day) != 3)
    263 		{
    264 		return;
    265 		}
    266 
    267 	SetDate ((uint32) year,
    268 			 (uint32) month,
    269 			 (uint32) day);
    270 
    271 	if (fDateTime.NotValid ())
    272 		{
    273 		Clear ();
    274 		return;
    275 		}
    276 
    277 	for (uint32 j = 0; j < len; j++)
    278 		{
    279 
    280 		if (s [j] == 'T')
    281 			{
    282 
    283 			unsigned hour   = 0;
    284 			unsigned minute = 0;
    285 			unsigned second = 0;
    286 
    287 			int items = sscanf (s + j + 1,
    288 								"%u:%u:%u",
    289 								&hour,
    290 								&minute,
    291 								&second);
    292 
    293 			if (items >= 2 && items <= 3)
    294 				{
    295 
    296 				SetTime ((uint32) hour,
    297 						 (uint32) minute,
    298 						 (uint32) second);
    299 
    300 				if (fDateTime.NotValid ())
    301 					{
    302 					Clear ();
    303 					return;
    304 					}
    305 
    306 				if (items == 3)
    307 					{
    308 
    309 					for (uint32 k = j + 1; k < len; k++)
    310 						{
    311 
    312 						if (s [k] == '.')
    313 							{
    314 
    315 							while (++k < len && s [k] >= '0' && s [k] <= '9')
    316 								{
    317 
    318 								char ss [2];
    319 
    320 								ss [0] = s [k];
    321 								ss [1] = 0;
    322 
    323 								fSubseconds.Append (ss);
    324 
    325 								}
    326 
    327 							break;
    328 
    329 							}
    330 
    331 						}
    332 
    333 					}
    334 
    335 				for (uint32 k = j + 1; k < len; k++)
    336 					{
    337 
    338 					if (s [k] == 'Z')
    339 						{
    340 
    341 						fTimeZone.SetOffsetMinutes (0);
    342 
    343 						break;
    344 
    345 						}
    346 
    347 					if (s [k] == '+' || s [k] == '-')
    348 						{
    349 
    350 						int32 sign = (s [k] == '-' ? -1 : 1);
    351 
    352 						unsigned tzhour = 0;
    353 						unsigned tzmin  = 0;
    354 
    355 						if (sscanf (s + k + 1,
    356 									"%u:%u",
    357 									&tzhour,
    358 									&tzmin) > 0)
    359 							{
    360 
    361 							fTimeZone.SetOffsetMinutes (sign * (tzhour * 60 + tzmin));
    362 
    363 							}
    364 
    365 						break;
    366 
    367 						}
    368 
    369 					}
    370 
    371 				}
    372 
    373 			break;
    374 
    375 			}
    376 
    377 		}
    378 
    379 	}
    380 
    381 /*****************************************************************************/
    382 
    383 dng_string dng_date_time_info::Encode_ISO_8601 () const
    384 	{
    385 
    386 	dng_string result;
    387 
    388 	if (IsValid ())
    389 		{
    390 
    391 		char s [256];
    392 
    393 		sprintf (s,
    394 				 "%04u-%02u-%02u",
    395 				 (unsigned) fDateTime.fYear,
    396 				 (unsigned) fDateTime.fMonth,
    397 				 (unsigned) fDateTime.fDay);
    398 
    399 		result.Set (s);
    400 
    401 		if (!fDateOnly)
    402 			{
    403 
    404 			sprintf (s,
    405 					 "T%02u:%02u:%02u",
    406 					 (unsigned) fDateTime.fHour,
    407 					 (unsigned) fDateTime.fMinute,
    408 					 (unsigned) fDateTime.fSecond);
    409 
    410 			result.Append (s);
    411 
    412 			if (fSubseconds.NotEmpty ())
    413 				{
    414 
    415 				bool subsecondsValid = true;
    416 
    417 				uint32 len = fSubseconds.Length ();
    418 
    419 				for (uint32 index = 0; index < len; index++)
    420 					{
    421 
    422 					if (fSubseconds.Get () [index] < '0' ||
    423 						fSubseconds.Get () [index] > '9')
    424 						{
    425 						subsecondsValid = false;
    426 						break;
    427 						}
    428 
    429 					}
    430 
    431 				if (subsecondsValid)
    432 					{
    433 					result.Append (".");
    434 					result.Append (fSubseconds.Get ());
    435 					}
    436 
    437 				}
    438 
    439 			if (gDNGUseFakeTimeZonesInXMP)
    440 				{
    441 
    442 				// Kludge: Early versions of the XMP toolkit assume Zulu time
    443 				// if the time zone is missing.  It is safer for fill in the
    444 				// local time zone.
    445 
    446 				dng_time_zone tempZone = fTimeZone;
    447 
    448 				if (tempZone.NotValid ())
    449 					{
    450 					tempZone = LocalTimeZone (fDateTime);
    451 					}
    452 
    453 				result.Append (tempZone.Encode_ISO_8601 ().Get ());
    454 
    455 				}
    456 
    457 			else
    458 				{
    459 
    460 				// MWG: Now we don't fill in the local time zone.  So only
    461 				// add the time zone if it is known and valid.
    462 
    463 				if (fTimeZone.IsValid ())
    464 					{
    465 					result.Append (fTimeZone.Encode_ISO_8601 ().Get ());
    466 					}
    467 
    468 				}
    469 
    470 			}
    471 
    472 		}
    473 
    474 	return result;
    475 
    476 	}
    477 
    478 /*****************************************************************************/
    479 
    480 void dng_date_time_info::Decode_IPTC_Date (const char *s)
    481 	{
    482 
    483 	if (strlen (s) == 8)
    484 		{
    485 
    486 		unsigned year   = 0;
    487 		unsigned month  = 0;
    488 		unsigned day    = 0;
    489 
    490 		if (sscanf (s,
    491 					"%4u%2u%2u",
    492 					&year,
    493 					&month,
    494 					&day) == 3)
    495 			{
    496 
    497 			SetDate ((uint32) year,
    498 					 (uint32) month,
    499 					 (uint32) day);
    500 
    501 			}
    502 
    503 		}
    504 
    505 	}
    506 
    507 /*****************************************************************************/
    508 
    509 dng_string dng_date_time_info::Encode_IPTC_Date () const
    510 	{
    511 
    512 	dng_string result;
    513 
    514 	if (IsValid ())
    515 		{
    516 
    517 		char s [64];
    518 
    519 		sprintf (s,
    520 			     "%04u%02u%02u",
    521 			     (unsigned) fDateTime.fYear,
    522 			     (unsigned) fDateTime.fMonth,
    523 			     (unsigned) fDateTime.fDay);
    524 
    525 		result.Set (s);
    526 
    527 		}
    528 
    529 	return result;
    530 
    531 	}
    532 
    533 /*****************************************************************************/
    534 
    535 void dng_date_time_info::Decode_IPTC_Time (const char *s)
    536 	{
    537 
    538 	if (strlen (s) == 11)
    539 		{
    540 
    541 		char time [12];
    542 
    543 		memcpy (time, s, sizeof (time));
    544 
    545 		if (time [6] == '+' ||
    546 			time [6] == '-')
    547 			{
    548 
    549 			int tzsign = (time [6] == '-') ? -1 : 1;
    550 
    551 			time [6] = 0;
    552 
    553 			unsigned hour   = 0;
    554 			unsigned minute = 0;
    555 			unsigned second = 0;
    556 			unsigned tzhour = 0;
    557 			unsigned tzmin  = 0;
    558 
    559 			if (sscanf (time,
    560 						"%2u%2u%2u",
    561 						&hour,
    562 						&minute,
    563 						&second) == 3 &&
    564 				sscanf (time + 7,
    565 						"%2u%2u",
    566 						&tzhour,
    567 						&tzmin) == 2)
    568 				{
    569 
    570 				dng_time_zone zone;
    571 
    572 				zone.SetOffsetMinutes (tzsign * (tzhour * 60 + tzmin));
    573 
    574 				if (zone.IsValid ())
    575 					{
    576 
    577 					SetTime ((uint32) hour,
    578 							 (uint32) minute,
    579 							 (uint32) second);
    580 
    581 					SetZone (zone);
    582 
    583 					}
    584 
    585 				}
    586 
    587 			}
    588 
    589 		}
    590 
    591 	else if (strlen (s) == 6)
    592 		{
    593 
    594 		unsigned hour   = 0;
    595 		unsigned minute = 0;
    596 		unsigned second = 0;
    597 
    598 		if (sscanf (s,
    599 					"%2u%2u%2u",
    600 					&hour,
    601 					&minute,
    602 					&second) == 3)
    603 			{
    604 
    605 			SetTime ((uint32) hour,
    606 					 (uint32) minute,
    607 					 (uint32) second);
    608 
    609 			}
    610 
    611 		}
    612 
    613 	else if (strlen (s) == 4)
    614 		{
    615 
    616 		unsigned hour   = 0;
    617 		unsigned minute = 0;
    618 
    619 		if (sscanf (s,
    620 					"%2u%2u",
    621 					&hour,
    622 					&minute) == 2)
    623 			{
    624 
    625 			SetTime ((uint32) hour,
    626 					 (uint32) minute,
    627 					 0);
    628 
    629 			}
    630 
    631 		}
    632 
    633 	}
    634 
    635 /*****************************************************************************/
    636 
    637 dng_string dng_date_time_info::Encode_IPTC_Time () const
    638 	{
    639 
    640 	dng_string result;
    641 
    642 	if (IsValid () && !fDateOnly)
    643 		{
    644 
    645 		char s [64];
    646 
    647 		if (fTimeZone.IsValid ())
    648 			{
    649 
    650 			sprintf (s,
    651 					 "%02u%02u%02u%c%02u%02u",
    652 					 (unsigned) fDateTime.fHour,
    653 					 (unsigned) fDateTime.fMinute,
    654 					 (unsigned) fDateTime.fSecond,
    655 					 (int) (fTimeZone.OffsetMinutes () >= 0 ? '+' : '-'),
    656 					 (unsigned) (Abs_int32 (fTimeZone.OffsetMinutes ()) / 60),
    657 					 (unsigned) (Abs_int32 (fTimeZone.OffsetMinutes ()) % 60));
    658 
    659 			}
    660 
    661 		else
    662 			{
    663 
    664 			sprintf (s,
    665 					 "%02u%02u%02u",
    666 					 (unsigned) fDateTime.fHour,
    667 					 (unsigned) fDateTime.fMinute,
    668 					 (unsigned) fDateTime.fSecond);
    669 
    670 			}
    671 
    672 		result.Set (s);
    673 
    674 		}
    675 
    676 	return result;
    677 
    678 	}
    679 
    680 /*****************************************************************************/
    681 
    682 static dng_mutex gDateTimeMutex ("gDateTimeMutex");
    683 
    684 /*****************************************************************************/
    685 
    686 void CurrentDateTimeAndZone (dng_date_time_info &info)
    687 	{
    688 
    689 	time_t sec;
    690 
    691 	time (&sec);
    692 
    693 	tm t;
    694 	tm zt;
    695 
    696 		{
    697 
    698 		dng_lock_mutex lock (&gDateTimeMutex);
    699 
    700 		t  = *localtime (&sec);
    701 		zt = *gmtime    (&sec);
    702 
    703 		}
    704 
    705 	dng_date_time dt;
    706 
    707 	dt.fYear   = t.tm_year + 1900;
    708 	dt.fMonth  = t.tm_mon + 1;
    709 	dt.fDay    = t.tm_mday;
    710 	dt.fHour   = t.tm_hour;
    711 	dt.fMinute = t.tm_min;
    712 	dt.fSecond = t.tm_sec;
    713 
    714 	info.SetDateTime (dt);
    715 
    716 	int tzHour = t.tm_hour - zt.tm_hour;
    717 	int tzMin  = t.tm_min  - zt.tm_min;
    718 
    719 	bool zonePositive = (t.tm_year >  zt.tm_year) ||
    720 						(t.tm_year == zt.tm_year && t.tm_yday >  zt.tm_yday) ||
    721 						(t.tm_year == zt.tm_year && t.tm_yday == zt.tm_yday && tzHour > 0) ||
    722 						(t.tm_year == zt.tm_year && t.tm_yday == zt.tm_yday && tzHour == 0 && tzMin >= 0);
    723 
    724 	tzMin += tzHour * 60;
    725 
    726 	if (zonePositive)
    727 		{
    728 
    729 		while (tzMin < 0)
    730 			tzMin += 24 * 60;
    731 
    732 		}
    733 
    734 	else
    735 		{
    736 
    737 		while (tzMin > 0)
    738 			tzMin -= 24 * 60;
    739 
    740 		}
    741 
    742 	dng_time_zone zone;
    743 
    744 	zone.SetOffsetMinutes (tzMin);
    745 
    746 	info.SetZone (zone);
    747 
    748 	}
    749 
    750 /*****************************************************************************/
    751 
    752 void DecodeUnixTime (uint32 unixTime, dng_date_time &dt)
    753 	{
    754 
    755 	time_t sec = (time_t) unixTime;
    756 
    757 	tm t;
    758 
    759 		{
    760 
    761 		dng_lock_mutex lock (&gDateTimeMutex);
    762 
    763 		#if qMacOS && !defined(__MACH__)
    764 
    765 		// Macintosh CFM stores time in local time zone.
    766 
    767 		tm *tp = localtime (&sec);
    768 
    769 		#else
    770 
    771 		// Macintosh Mach-O and Windows stores time in Zulu time.
    772 
    773 		tm *tp = gmtime (&sec);
    774 
    775 		#endif
    776 
    777 		if (!tp)
    778 			{
    779 			dt.Clear ();
    780 			return;
    781 			}
    782 
    783 		t = *tp;
    784 
    785 		}
    786 
    787 	dt.fYear   = t.tm_year + 1900;
    788 	dt.fMonth  = t.tm_mon + 1;
    789 	dt.fDay    = t.tm_mday;
    790 	dt.fHour   = t.tm_hour;
    791 	dt.fMinute = t.tm_min;
    792 	dt.fSecond = t.tm_sec;
    793 
    794 	}
    795 
    796 /*****************************************************************************/
    797 
    798 dng_time_zone LocalTimeZone (const dng_date_time &dt)
    799 	{
    800 
    801 	dng_time_zone result;
    802 
    803 	if (dt.IsValid ())
    804 		{
    805 
    806 		#if qMacOS
    807 
    808 		CFTimeZoneRef zoneRef = CFTimeZoneCopyDefault ();
    809 
    810 		if (zoneRef)
    811 			{
    812 
    813 			CFGregorianDate gregDate;
    814 
    815 			gregDate.year   = dt.fYear;
    816 			gregDate.month  = (SInt8) dt.fMonth;
    817 			gregDate.day    = (SInt8) dt.fDay;
    818 			gregDate.hour   = (SInt8) dt.fHour;
    819 			gregDate.minute = (SInt8) dt.fMinute;
    820 			gregDate.second = (SInt8) dt.fSecond;
    821 
    822 			CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime (gregDate, zoneRef);
    823 
    824 			CFTimeInterval secondsDelta = CFTimeZoneGetSecondsFromGMT (zoneRef, absTime);
    825 
    826 			CFRelease (zoneRef);
    827 
    828 			result.SetOffsetSeconds (Round_int32 (secondsDelta));
    829 
    830 			if (result.IsValid ())
    831 				{
    832 				return result;
    833 				}
    834 
    835 			}
    836 
    837 		#endif
    838 
    839 		#if qWinOS
    840 
    841 		if (GetTimeZoneInformation          != NULL &&
    842 			SystemTimeToTzSpecificLocalTime != NULL &&
    843 		    SystemTimeToFileTime            != NULL)
    844 			{
    845 
    846 			TIME_ZONE_INFORMATION tzInfo;
    847 
    848 			DWORD x = GetTimeZoneInformation (&tzInfo);
    849 
    850 			SYSTEMTIME localST;
    851 
    852 			memset (&localST, 0, sizeof (localST));
    853 
    854 			localST.wYear   = (WORD) dt.fYear;
    855 			localST.wMonth  = (WORD) dt.fMonth;
    856 			localST.wDay    = (WORD) dt.fDay;
    857 			localST.wHour   = (WORD) dt.fHour;
    858 			localST.wMinute = (WORD) dt.fMinute;
    859 			localST.wSecond = (WORD) dt.fSecond;
    860 
    861 			SYSTEMTIME utcST;
    862 
    863 			if (TzSpecificLocalTimeToSystemTime (&tzInfo, &localST, &utcST))
    864 				{
    865 
    866 				FILETIME localFT;
    867 				FILETIME utcFT;
    868 
    869 				(void) SystemTimeToFileTime (&localST, &localFT);
    870 				(void) SystemTimeToFileTime (&utcST  , &utcFT  );
    871 
    872 				uint64 time1 = (((uint64) localFT.dwHighDateTime) << 32) + localFT.dwLowDateTime;
    873 				uint64 time2 = (((uint64) utcFT  .dwHighDateTime) << 32) + utcFT  .dwLowDateTime;
    874 
    875 				// FILETIMEs are in units to 100 ns.  Convert to seconds.
    876 
    877 				int64 time1Sec = time1 / 10000000;
    878 				int64 time2Sec = time2 / 10000000;
    879 
    880 				int32 delta = (int32) (time1Sec - time2Sec);
    881 
    882 				result.SetOffsetSeconds (delta);
    883 
    884 				if (result.IsValid ())
    885 					{
    886 					return result;
    887 					}
    888 
    889 				}
    890 
    891 			}
    892 
    893 		#endif
    894 
    895 		}
    896 
    897 	// Figure out local time zone.
    898 
    899 	dng_date_time_info current_info;
    900 
    901 	CurrentDateTimeAndZone (current_info);
    902 
    903 	result = current_info.TimeZone ();
    904 
    905 	return result;
    906 
    907 	}
    908 
    909 /*****************************************************************************/
    910 
    911 dng_date_time_storage_info::dng_date_time_storage_info ()
    912 
    913 	:	fOffset	(kDNGStreamInvalidOffset     )
    914 	,	fFormat	(dng_date_time_format_unknown)
    915 
    916 	{
    917 
    918 	}
    919 
    920 /*****************************************************************************/
    921 
    922 dng_date_time_storage_info::dng_date_time_storage_info (uint64 offset,
    923 														dng_date_time_format format)
    924 
    925 	:	fOffset	(offset)
    926 	,	fFormat	(format)
    927 
    928 	{
    929 
    930 	}
    931 
    932 /*****************************************************************************/
    933 
    934 bool dng_date_time_storage_info::IsValid () const
    935 	{
    936 
    937 	return fOffset != kDNGStreamInvalidOffset;
    938 
    939 	}
    940 
    941 /*****************************************************************************/
    942 
    943 uint64 dng_date_time_storage_info::Offset () const
    944 	{
    945 
    946 	if (!IsValid ())
    947 		ThrowProgramError ();
    948 
    949 	return fOffset;
    950 
    951 	}
    952 
    953 /*****************************************************************************/
    954 
    955 dng_date_time_format dng_date_time_storage_info::Format () const
    956 	{
    957 
    958 	if (!IsValid ())
    959 		ThrowProgramError ();
    960 
    961 	return fFormat;
    962 
    963 	}
    964 
    965 /*****************************************************************************/
    966