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