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
dng_date_time()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
dng_date_time(uint32 year,uint32 month,uint32 day,uint32 hour,uint32 minute,uint32 second)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
IsValid() const83 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
Clear()97 void dng_date_time::Clear ()
98 {
99
100 *this = dng_date_time ();
101
102 }
103
104 /*****************************************************************************/
105
DateTimeParseU32(const char * & s)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
Parse(const char * s)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
Encode_ISO_8601() const142 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
dng_date_time_info()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
IsValid() const205 bool dng_date_time_info::IsValid () const
206 {
207
208 return fDateTime.IsValid ();
209
210 }
211
212 /*****************************************************************************/
213
SetDate(uint32 year,uint32 month,uint32 day)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
SetTime(uint32 hour,uint32 minute,uint32 second)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
Decode_ISO_8601(const char * s)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
Encode_ISO_8601() const383 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
Decode_IPTC_Date(const char * s)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
Encode_IPTC_Date() const509 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
Decode_IPTC_Time(const char * s)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
Encode_IPTC_Time() const637 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
CurrentDateTimeAndZone(dng_date_time_info & info)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
DecodeUnixTime(uint32 unixTime,dng_date_time & dt)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
LocalTimeZone(const dng_date_time & dt)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 CFReleaseHelper<CFTimeZoneRef> zoneRefDeleter (zoneRef);
811
812 if (zoneRef)
813 {
814
815 // New path that doesn't use deprecated CFGregorian-based APIs.
816
817 CFCalendarRef calendar =
818 CFCalendarCreateWithIdentifier (kCFAllocatorDefault,
819 kCFGregorianCalendar);
820
821 CFReleaseHelper<CFCalendarRef> calendarDeleter (calendar);
822
823 CFAbsoluteTime absTime;
824
825 if (CFCalendarComposeAbsoluteTime (calendar,
826 &absTime,
827 "yMdHms",
828 dt.fYear,
829 dt.fMonth,
830 dt.fDay,
831 dt.fHour,
832 dt.fMinute,
833 dt.fSecond))
834 {
835
836 CFTimeInterval secondsDelta = CFTimeZoneGetSecondsFromGMT (zoneRef, absTime);
837
838 result.SetOffsetSeconds (Round_int32 (secondsDelta));
839
840 if (result.IsValid ())
841 {
842 return result;
843 }
844
845 }
846
847 }
848
849 #endif
850
851 #if qWinOS
852
853 if (GetTimeZoneInformation != NULL &&
854 SystemTimeToTzSpecificLocalTime != NULL &&
855 SystemTimeToFileTime != NULL)
856 {
857
858 TIME_ZONE_INFORMATION tzInfo;
859
860 DWORD x = GetTimeZoneInformation (&tzInfo);
861
862 SYSTEMTIME localST;
863
864 memset (&localST, 0, sizeof (localST));
865
866 localST.wYear = (WORD) dt.fYear;
867 localST.wMonth = (WORD) dt.fMonth;
868 localST.wDay = (WORD) dt.fDay;
869 localST.wHour = (WORD) dt.fHour;
870 localST.wMinute = (WORD) dt.fMinute;
871 localST.wSecond = (WORD) dt.fSecond;
872
873 SYSTEMTIME utcST;
874
875 if (TzSpecificLocalTimeToSystemTime (&tzInfo, &localST, &utcST))
876 {
877
878 FILETIME localFT;
879 FILETIME utcFT;
880
881 (void) SystemTimeToFileTime (&localST, &localFT);
882 (void) SystemTimeToFileTime (&utcST , &utcFT );
883
884 uint64 time1 = (((uint64) localFT.dwHighDateTime) << 32) + localFT.dwLowDateTime;
885 uint64 time2 = (((uint64) utcFT .dwHighDateTime) << 32) + utcFT .dwLowDateTime;
886
887 // FILETIMEs are in units to 100 ns. Convert to seconds.
888
889 int64 time1Sec = time1 / 10000000;
890 int64 time2Sec = time2 / 10000000;
891
892 int32 delta = (int32) (time1Sec - time2Sec);
893
894 result.SetOffsetSeconds (delta);
895
896 if (result.IsValid ())
897 {
898 return result;
899 }
900
901 }
902
903 }
904
905 #endif
906
907 }
908
909 // Figure out local time zone.
910
911 dng_date_time_info current_info;
912
913 CurrentDateTimeAndZone (current_info);
914
915 result = current_info.TimeZone ();
916
917 return result;
918
919 }
920
921 /*****************************************************************************/
922
dng_date_time_storage_info()923 dng_date_time_storage_info::dng_date_time_storage_info ()
924
925 : fOffset (kDNGStreamInvalidOffset )
926 , fFormat (dng_date_time_format_unknown)
927
928 {
929
930 }
931
932 /*****************************************************************************/
933
dng_date_time_storage_info(uint64 offset,dng_date_time_format format)934 dng_date_time_storage_info::dng_date_time_storage_info (uint64 offset,
935 dng_date_time_format format)
936
937 : fOffset (offset)
938 , fFormat (format)
939
940 {
941
942 }
943
944 /*****************************************************************************/
945
IsValid() const946 bool dng_date_time_storage_info::IsValid () const
947 {
948
949 return fOffset != kDNGStreamInvalidOffset;
950
951 }
952
953 /*****************************************************************************/
954
Offset() const955 uint64 dng_date_time_storage_info::Offset () const
956 {
957
958 if (!IsValid ())
959 ThrowProgramError ();
960
961 return fOffset;
962
963 }
964
965 /*****************************************************************************/
966
Format() const967 dng_date_time_format dng_date_time_storage_info::Format () const
968 {
969
970 if (!IsValid ())
971 ThrowProgramError ();
972
973 return fFormat;
974
975 }
976
977 /*****************************************************************************/
978