Skip to content
Browse files

Rationalize the handling of times

Stop abusing local time functions to handle UTC times. This fixes
an edge condition with handling DST.
  • Loading branch information...
1 parent c28db8a commit 0176129a5f1bb2d2180e0fd9f2830e13d7d6f043 @dfandrich dfandrich committed Dec 11, 2012
Showing with 42 additions and 36 deletions.
  1. +1 −0 RELEASES
  2. +6 −2 correlate.c
  3. +3 −1 doc/gpscorrelate-manpage.xml.in
  4. +1 −21 exif-gps.cpp
  5. +4 −3 main-command.c
  6. +27 −9 unixtime.c
View
1 RELEASES
@@ -141,3 +141,4 @@ v1.6.2git: TBD
- The progress graph that shows while processing many images on a slow
machine is now displayed as the files are processed.
- --machine output is no longer dependent on the current locale
+ - Fixed time calculations spanning DST discontinuities
View
8 correlate.c
@@ -77,11 +77,15 @@ struct GPSPoint* CorrelatePhoto(const char* Filename,
* as the time for correlating all the remainder. */
time_t RealTime;
- /* PhotoTime isn't actually Epoch-based, but will be wrong by
- * an amount equal to the the local time zone offset. */
+ /* PhotoTime isn't a true epoch time, but is rather out
+ * by the local offset from UTC */
time_t PhotoTime =
ConvertToUnixTime(TimeTemp, EXIF_DATE_FORMAT, 0, 0);
+
+ /* Extract the component time values */
struct tm *PhotoTm = gmtime(&PhotoTime);
+
+ /* Then create a true epoch-based local time, including DST */
PhotoTm->tm_isdst = -1;
RealTime = mktime(PhotoTm);
View
4 doc/gpscorrelate-manpage.xml.in
@@ -347,7 +347,9 @@
<option>--fix-timestamps</option>
</term>
<listitem>
- <para>Fix broken GPS datestamps written with versions &lt; 1.5.2</para>
+ <para>Fix broken GPS datestamps written with versions &lt; 1.5.2 by
+ replacing them with the photo's time stamp. This option implies
+ <userinput>--no-mtime</userinput></para>
</listitem>
</varlistentry>
View
22 exif-gps.cpp
@@ -362,7 +362,7 @@ char* ReadGPSTimestamp(const char* File, char* DateStamp, char* TimeStamp, int*
RatNum2 = GPSData.toRational(1);
RatNum3 = GPSData.toRational(2);
snprintf(DateStamp, 12, "%04d:%02d:%02d",
- RatNum1.first, RatNum2.first, RatNum3.first);
+ RatNum1.first, RatNum2.first, RatNum3.first);
}
return Copy;
@@ -601,16 +601,6 @@ int WriteGPSData(const char* File, const struct GPSPoint* Point,
// The timestamp is taken as the UTC time of the photo.
// If interpolation occurred, then this time is the time of the photo.
struct tm TimeStamp = *gmtime(&(Point->Time));
- TimeStamp.tm_isdst = -1;
-
- if (Point->Time != mktime(&TimeStamp)) {
- // What happened is gmtime subtracted the current time zone.
- // I thought it was called "gmtime" for a reason.
- // Oh well. Add the difference and try again.
- // This is a hack.
- time_t CorrectedTime = Point->Time + (Point->Time - mktime(&TimeStamp));
- TimeStamp = *gmtime(&CorrectedTime);
- }
Value = Exiv2::Value::create(Exiv2::unsignedRational);
snprintf(ScratchBuf, sizeof(ScratchBuf), "%d/1 %d/1 %d/1",
@@ -675,16 +665,6 @@ int WriteFixedDatestamp(const char* File, time_t Time)
Exiv2::ExifData &ExifToWrite = Image->exifData();
struct tm TimeStamp = *gmtime(&Time);
- TimeStamp.tm_isdst = -1;
-
- if (Time != mktime(&TimeStamp)) {
- // What happened is gmtime subtracted the current time zone.
- // I thought it was called "gmtime" for a reason.
- // Oh well. Add the difference and try again.
- // This is a hack.
- time_t CorrectedTime = Time + (Time - mktime(&TimeStamp));
- TimeStamp = *gmtime(&CorrectedTime);
- }
char ScratchBuf[100];
View
7 main-command.c
@@ -206,10 +206,11 @@ static int FixDatestamp(const char* File, int AdjustmentHours, int AdjustmentMin
char GPSTimeFormat[100];
strftime(PhotoTimeFormat, sizeof(PhotoTimeFormat),
- "%a %b %e %T %Y\n", localtime(&PhotoTime));
+ "%a %b %e %T %Y UTC", gmtime(&PhotoTime));
strftime(GPSTimeFormat, sizeof(GPSTimeFormat),
- "%a %b %e %T %Y\n", localtime(&GPSTime));
- printf(_("%s: Wrong timestamp:\n Photo: %s GPS: %s Corrected: %s"),
+ "%a %b %e %T %Y UTC", gmtime(&GPSTime));
+ printf(_("%s: Wrong timestamp:\n Photo: %s\n"
+ " GPS: %s\n Corrected: %s\n"),
File, PhotoTimeFormat, GPSTimeFormat, PhotoTimeFormat);
} else {
/* Inside the range. Do nothing! */
View
36 unixtime.c
@@ -32,6 +32,25 @@
#include "unixtime.h"
+/* Some systems have a version of this called timegm(), but it's not portable */
+static time_t portable_timegm(struct tm *tm)
+{
+ const char *tz = getenv("TZ");
+
+ /* Set an empty TZ to force UTC */
+ setenv("TZ", "", 1);
+ tzset();
+ time_t ret = mktime(tm);
+
+ /* Restore the original TZ */
+ if (tz)
+ setenv("TZ", tz, 1);
+ else
+ unsetenv("TZ");
+ tzset();
+ return ret;
+}
+
time_t ConvertToUnixTime(const char* StringTime, const char* Format,
int TZOffsetHours, int TZOffsetMinutes)
{
@@ -51,7 +70,7 @@ time_t ConvertToUnixTime(const char* StringTime, const char* Format,
struct tm Time;
Time.tm_wday = 0;
Time.tm_yday = 0;
- Time.tm_isdst = -1;
+ Time.tm_isdst = 0; // there is no DST in UTC
/* Read out the time from the string using our format. */
sscanf(StringTime, Format, &Time.tm_year, &Time.tm_mon,
@@ -62,16 +81,15 @@ time_t ConvertToUnixTime(const char* StringTime, const char* Format,
Time.tm_year -= 1900;
Time.tm_mon -= 1;
+ /* Calculate and return the Unix time. */
+ time_t thetime = portable_timegm(&Time);
+
/* Add our timezone offset to the time.
- * We don't check to see if it overflowed anything;
- * mktime does this and fixes it for us. */
- /* Note also that we SUBTRACT these times. We want the
+ * Note also that we SUBTRACT these times. We want the
* result to be in UTC. */
+ thetime -= TZOffsetHours * 60 * 60;
+ thetime -= TZOffsetMinutes * 60;
- Time.tm_hour -= TZOffsetHours;
- Time.tm_min -= TZOffsetMinutes;
-
- /* Calculate and return the unix time. */
- return mktime(&Time);
+ return thetime;
}

0 comments on commit 0176129

Please sign in to comment.
Something went wrong with that request. Please try again.