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
44bool gDNGUseFakeTimeZonesInXMP = false;
45
46/******************************************************************************/
47
48dng_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
63dng_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
83bool 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
97void dng_date_time::Clear ()
98 {
99
100 *this = dng_date_time ();
101
102 }
103
104/*****************************************************************************/
105
106static 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
126bool 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
142dng_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
192dng_date_time_info::dng_date_time_info ()
193
194 : fDateOnly (true)
195 , fDateTime ()
196 , fSubseconds ()
197 , fTimeZone ()
198
199 {
200
201 }
202
203/*****************************************************************************/
204
205bool dng_date_time_info::IsValid () const
206 {
207
208 return fDateTime.IsValid ();
209
210 }
211
212/*****************************************************************************/
213
214void 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
227void 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
242void 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
383dng_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
480void 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
509dng_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
535void 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
637dng_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
682static dng_mutex gDateTimeMutex ("gDateTimeMutex");
683
684/*****************************************************************************/
685
686void 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
752void 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
798dng_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
911dng_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
922dng_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
934bool dng_date_time_storage_info::IsValid () const
935 {
936
937 return fOffset != kDNGStreamInvalidOffset;
938
939 }
940
941/*****************************************************************************/
942
943uint64 dng_date_time_storage_info::Offset () const
944 {
945
946 if (!IsValid ())
947 ThrowProgramError ();
948
949 return fOffset;
950
951 }
952
953/*****************************************************************************/
954
955dng_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