Changeset 4f70ebc in git


Ignore:
Timestamp:
14/01/14 02:23:57 (6 years ago)
Author:
Olly Betts <olly@…>
Branches:
line_contents, master, stereo, travis-osx
Children:
5dc0378
Parents:
ad95991
git-author:
Olly Betts <olly@…> (14/01/14 00:44:01)
git-committer:
Olly Betts <olly@…> (14/01/14 02:23:57)
Message:

src/: Add new "datestamp_numeric" field to struct img giving the
datestamp as a time_t in UTC (or (time_t)-1 if there's no datestamp
or we failed to convert it). For .3d >= v8, this field is reliable.
We attempt to convert date strings in .3d <= v7 and CMAP XYZ
files, but may get the timezone wrong.

Files:
5 edited

Legend:

Unmodified
Added
Removed
  • ChangeLog

    rad95991 r4f70ebc  
     1Tue Jan 14 00:40:06 GMT 2014  Olly Betts <olly@survex.com>
     2
     3        * src/: Add new "datestamp_numeric" field to struct img giving the
     4          datestamp as a time_t in UTC (or (time_t)-1 if there's no datestamp
     5          or we failed to convert it).  For .3d >= v8, this field is reliable.
     6          We attempt to convert date strings in .3d <= v7 and CMAP XYZ
     7          files, but may get the timezone wrong.
     8
    19Tue Jan 14 00:29:35 GMT 2014  Olly Betts <olly@survex.com>
    210
  • src/dump3d.c

    rad95991 r4f70ebc  
    11/* dump3d.c */
    22/* Show raw contents of .3d file in text form */
    3 /* Copyright (C) 2001,2002,2006,2011,2012,2013 Olly Betts
     3/* Copyright (C) 2001,2002,2006,2011,2012,2013,2014 Olly Betts
    44 *
    55 * This program is free software; you can redistribute it and/or modify
     
    9494   printf("TITLE \"%s\"\n", pimg->title);
    9595   printf("DATE \"%s\"\n", pimg->datestamp);
     96   printf("DATE_NUMERIC %ld\n", pimg->datestamp_numeric);
    9697   printf("VERSION %d\n", pimg->version);
    9798   if (pimg->is_extended_elevation)
  • src/img.c

    rad95991 r4f70ebc  
    2222#endif
    2323
     24#include <ctype.h>
     25#include <errno.h>
     26#include <limits.h>
     27#include <locale.h>
    2428#include <stdio.h>
     29#include <stdlib.h>
    2530#include <string.h>
    26 #include <stdlib.h>
    2731#include <time.h>
    28 #include <ctype.h>
    29 #include <locale.h>
    3032
    3133#include "img.h"
     
    152154#endif
    153155
     156static char * my_strdup(const char *str);
     157
     158static time_t
     159mktime_with_tz(struct tm * tm, const char * tz)
     160{
     161    time_t r;
     162    char * old_tz = getenv("TZ");
     163#ifdef _WIN32
     164    if (old_tz) {
     165        old_tz = my_strdup(old_tz);
     166        if (!old_tz)
     167            return (time_t)-1;
     168    }
     169    if (_putenv_s("TZ", tz) != 0) {
     170        osfree(old_tz);
     171        return (time_t)-1;
     172    }
     173#elif defined HAVE_SETENV
     174    if (old_tz) {
     175        old_tz = my_strdup(old_tz);
     176        if (!old_tz)
     177            return (time_t)-1;
     178    }
     179    if (setenv("TZ", tz, 1) < 0) {
     180        osfree(old_tz);
     181        return (time_t)-1;
     182    }
     183#else
     184    char * p;
     185    if (old_tz) {
     186        size_t len = strlen(old_tz) + 1;
     187        p = (char *)xosmalloc(len + 3);
     188        if (!p)
     189            return (time_t)-1;
     190        memcpy(p, "TZ=", 3);
     191        memcpy(p + 3, tz, len);
     192        old_tz = p;
     193    }
     194    p = (char *)xosmalloc(strlen(tz) + 4);
     195    if (!p) {
     196        osfree(old_tz);
     197        return (time_t)-1;
     198    }
     199    memcpy(p, "TZ=", 3);
     200    strcpy(p + 3, tz);
     201    if (putenv(p) != 0) {
     202        osfree(p);
     203        osfree(old_tz);
     204        return (time_t)-1;
     205    }
     206#endif
     207    tzset();
     208    r = mktime(tm);
     209    if (old_tz) {
     210#ifdef _WIN32
     211        _putenv_s("TZ", old_tz);
     212#elif !defined HAVE_SETENV
     213        putenv(old_tz);
     214        osfree(p);
     215#else
     216        setenv("TZ", old_tz, 1);
     217#endif
     218        osfree(old_tz);
     219    } else {
     220#ifdef _WIN32
     221        _putenv_s("TZ", "");
     222#elif !defined HAVE_UNSETENV
     223        putenv((char*)"TZ");
     224        osfree(p);
     225#else
     226        unsetenv("TZ");
     227#endif
     228    }
     229    return r;
     230}
     231
    154232static unsigned short
    155233getu16(FILE *fh)
     
    348426
    349427   pimg->title = pimg->datestamp = NULL;
     428   pimg->datestamp_numeric = (time_t)-1;
     429
    350430   if (survey) {
    351431      len = strlen(survey);
     
    481561         goto error;
    482562      }
    483       /* FIXME: reparse date? */
     563      /* There doesn't seem to be a spec for what happens after 1999 with cmap
     564       * files, so this code allows for:
     565       *  * 21xx -> xx (up to 2150)
     566       *  * 21xx -> 1xx (up to 2199)
     567       *  * full year being specified instead of 2 digits
     568       */
    484569      len = strlen(line);
    485       if (len > 59) line[59] = '\0';
     570      if (len > 59) {
     571         /* Don't just truncate at column 59, allow for a > 2 digit year. */
     572         char * p = strstr(line + len, "Page");
     573         if (p) {
     574            while (p > line && p[-1] == ' ')
     575               --p;
     576            *p = '\0';
     577            len = p - line;
     578         } else {
     579            line[59] = '\0';
     580         }
     581      }
    486582      if (len > 45) {
     583         /* YY/MM/DD HH:MM */
     584         struct tm tm;
     585         unsigned long v;
     586         char * p;
    487587         pimg->datestamp = my_strdup(line + 45);
     588         p = pimg->datestamp;
     589         v = strtoul(p, &p, 10);
     590         if (v <= 50) {
     591            /* In the absence of a spec for cmap files, assume <= 50 means 21st
     592             * century. */
     593            v += 2000;
     594         } else if (v < 200) {
     595            /* Map 100-199 to 21st century. */
     596            v += 1900;
     597         }
     598         if (v == ULONG_MAX || *p++ != '/')
     599            goto bad_cmap_date;
     600         tm.tm_year = v - 1900;
     601         v = strtoul(p, &p, 10);
     602         if (v < 1 || v > 12 || *p++ != '/')
     603            goto bad_cmap_date;
     604         tm.tm_mon = v - 1;
     605         v = strtoul(p, &p, 10);
     606         if (v < 1 || v > 31 || *p++ != ' ')
     607            goto bad_cmap_date;
     608         tm.tm_mday = v;
     609         v = strtoul(p, &p, 10);
     610         if (v >= 24 || *p++ != ':')
     611            goto bad_cmap_date;
     612         tm.tm_hour = v;
     613         v = strtoul(p, &p, 10);
     614         if (v >= 60)
     615            goto bad_cmap_date;
     616         tm.tm_min = v;
     617         if (*p == ':') {
     618            v = strtoul(p + 1, &p, 10);
     619            if (v > 60)
     620               goto bad_cmap_date;
     621            tm.tm_sec = v;
     622         } else {
     623            tm.tm_sec = 0;
     624         }
     625         tm.tm_isdst = 0;
     626         /* We have no indication of what timezone this timestamp is in.  It's
     627          * probably local time for whoever processed the data, so just assume
     628          * UTC, which is at least fairly central in the possibilities.
     629          */
     630         pimg->datestamp_numeric = mktime_with_tz(&tm, "");
    488631      } else {
    489632         pimg->datestamp = my_strdup(TIMENA);
    490633      }
     634bad_cmap_date:
    491635      if (strncmp(line, "  Cave Survey Data Processed by CMAP ",
    492636                  LITLEN("  Cave Survey Data Processed by CMAP ")) == 0) {
     
    622766      }
    623767   }
     768
     769   if (pimg->datestamp[0] == '@') {
     770      unsigned long v;
     771      char * p;
     772      errno = 0;
     773      v = strtoul(pimg->datestamp + 1, &p, 10);
     774      if (errno == 0 && *p == '\0')
     775         pimg->datestamp_numeric = v;
     776      /* FIXME: We're assuming here that the C time_t epoch is 1970, which is
     777       * true for Unix-like systems, Mac OS X and Windows, but isn't guaranteed
     778       * by ISO C.
     779       */
     780   } else {
     781      /* %a,%Y.%m.%d %H:%M:%S %Z */
     782      struct tm tm;
     783      unsigned long v;
     784      char * p = pimg->datestamp;
     785      while (isalpha((unsigned char)*p)) ++p;
     786      if (*p == ',') ++p;
     787      while (isspace((unsigned char)*p)) ++p;
     788      v = strtoul(p, &p, 10);
     789      if (v == ULONG_MAX || *p++ != '.')
     790         goto bad_3d_date;
     791      tm.tm_year = v - 1900;
     792      v = strtoul(p, &p, 10);
     793      if (v < 1 || v > 12 || *p++ != '.')
     794         goto bad_3d_date;
     795      tm.tm_mon = v - 1;
     796      v = strtoul(p, &p, 10);
     797      if (v < 1 || v > 31 || *p++ != ' ')
     798         goto bad_3d_date;
     799      tm.tm_mday = v;
     800      v = strtoul(p, &p, 10);
     801      if (v >= 24 || *p++ != ':')
     802         goto bad_3d_date;
     803      tm.tm_hour = v;
     804      v = strtoul(p, &p, 10);
     805      if (v >= 60 || *p++ != ':')
     806         goto bad_3d_date;
     807      tm.tm_min = v;
     808      v = strtoul(p, &p, 10);
     809      if (v > 60)
     810         goto bad_3d_date;
     811      tm.tm_sec = v;
     812      tm.tm_isdst = 0;
     813      while (isspace((unsigned char)*p)) ++p;
     814      /* p now points to the timezone string.
     815       *
     816       * However, it's likely to be a string like "BST", and such strings can
     817       * be ambiguous (BST could be UTC+1 or UTC+6), so it is impossible to
     818       * reliably convert in all cases.  Just pass what we have to tzset() - if
     819       * it doesn't handle it, UTC will be used.
     820       */
     821      pimg->datestamp_numeric = mktime_with_tz(&tm, p);
     822   }
     823bad_3d_date:
    624824
    625825   pimg->start = ftell(pimg->fh);
  • src/img.h

    rad95991 r4f70ebc  
    11/* img.h
    22 * Header file for routines to read and write Survex ".3d" image files
    3  * Copyright (C) Olly Betts 1993,1994,1997,2001,2002,2003,2004,2005,2006,2010,2011,2012,2013
     3 * Copyright (C) Olly Betts 1993,1994,1997,2001,2002,2003,2004,2005,2006,2010,2011,2012,2013,2014
    44 *
    55 * This program is free software; you can redistribute it and/or modify
     
    9999    */
    100100   char *datestamp;
     101   /* The datestamp as a time_t (or (time_t)-1 if not available).
     102    *
     103    * For 3d format versions >= 8, this is a reliable value and in UTC.  Older
     104    * 3d format versions store a human readable time, which img will attempt
     105    * to decode, but it may fail, particularly with handling timezones.  Even
     106    * if it does work, beware that times in local time where DST applies are
     107    * inherently ambiguous around when the clocks go back.
     108    *
     109    * CMAP XYZ files contain a timestamp.  It's probably in localtime (but
     110    * without any timezone information) and the example files are all pre-2000
     111    * and have two digit years.  We do our best to turn these into a useful
     112    * time_t value.
     113    */
     114   time_t datestamp_numeric;
    101115   char separator; /* character used to separate survey levels ('.' usually) */
    102116#if IMG_API_VERSION == 0
  • src/mainfrm.cc

    rad95991 r4f70ebc  
    55//
    66//  Copyright (C) 2000-2002,2005,2006 Mark R. Shinwell
    7 //  Copyright (C) 2001-2003,2004,2005,2006,2010,2011,2012,2013 Olly Betts
     7//  Copyright (C) 2001-2003,2004,2005,2006,2010,2011,2012,2013,2014 Olly Betts
    88//  Copyright (C) 2005 Martin Green
    99//
     
    13381338        m_DateStamp = wmsg(/*Date and time not available.*/108);
    13391339    } else if (survey->datestamp[0] == '@') {
    1340         char * end;
    1341         time_t t = (time_t)strtol(survey->datestamp + 1, &end, 10);
    1342         if (*end == '\0') {
    1343             const struct tm * tm = localtime(&t);
    1344             char buf[256];
    1345             strftime(buf, 256, msg(/*%a,%Y.%m.%d %H:%M:%S %Z*/107), tm);
    1346             m_DateStamp = wxString(buf, wxConvUTF8);
    1347         }
     1340        const struct tm * tm = localtime(&survey->datestamp_numeric);
     1341        char buf[256];
     1342        strftime(buf, 256, msg(/*%a,%Y.%m.%d %H:%M:%S %Z*/107), tm);
     1343        m_DateStamp = wxString(buf, wxConvUTF8);
    13481344    }
    13491345    if (m_DateStamp.empty()) {
Note: See TracChangeset for help on using the changeset viewer.