diff --git a/defs.h b/defs.h index 1ab851799..3c39d2408 100644 --- a/defs.h +++ b/defs.h @@ -966,8 +966,6 @@ QDateTime dotnet_time_to_qdatetime(long long dotnet); long long qdatetime_to_dotnet_time(const QDateTime& dt); QString strip_html(const QString& utfstring); QString strip_nastyhtml(const QString& in); -QString convert_human_date_format(const char* human_datef); /* "MM,YYYY,DD" -> "%m,%Y,%d" */ -QString convert_human_time_format(const char* human_timef); /* "HH+mm+ss" -> "%H+%M+%S" */ QString pretty_deg_format(double lat, double lon, char fmt, const char* sep, bool html); /* decimal -> dd.dddd or dd mm.mmm or dd mm ss */ QString get_filename(const QString& fname); /* extract the filename portion */ diff --git a/garmin_txt.cc b/garmin_txt.cc index 8a6a1433f..f1d5fe50d 100644 --- a/garmin_txt.cc +++ b/garmin_txt.cc @@ -29,10 +29,9 @@ #include // for toupper #include // for fabs, floor #include // for uint16_t -#include // for sscanf, fprintf, snprintf, stderr -#include // for abs -#include // for strstr, strlen -#include // for time_t, gmtime, localtime, strftime +#include // for sscanf, fprintf, stderr +#include // for abs, div +#include // for strstr #include // for optional #include // for pair, make_pair @@ -83,7 +82,7 @@ static const char* datum_str; static int current_line; static QString date_time_format; static int precision = 3; -static time_t utc_offs = 0; +static int utc_offs = 0; static gtxt_flags_t gtxt_flags; enum header_type { @@ -121,7 +120,7 @@ static std::array>, unknown_header> header_mapping static QStringList header_column_names; static constexpr double kGarminUnknownAlt = 1.0e25; -static constexpr char kDefaultDateFormat[] = "dd/mm/yyyy"; +static constexpr char kDefaultDateFormat[] = "dd/MM/yyyy"; static constexpr char kDefaultTimeFormat[] = "HH:mm:ss"; static bool is_valid_alt(double alt) @@ -140,13 +139,13 @@ static char* opt_grid = nullptr; static QVector garmin_txt_args = { - {"date", &opt_date_format, "Read/Write date format (i.e. yyyy/mm/dd)", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, + {"date", &opt_date_format, "Read/Write date format (e.g. yyyy/MM/dd)", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, {"datum", &opt_datum, "GPS datum (def. WGS 84)", "WGS 84", ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, {"dist", &opt_dist, "Distance unit [m=metric, s=statute]", "m", ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, {"grid", &opt_grid, "Write position using this grid.", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, {"prec", &opt_precision, "Precision of coordinates", "3", ARGTYPE_INT, ARG_NOMINMAX, nullptr}, {"temp", &opt_temp, "Temperature unit [c=Celsius, f=Fahrenheit]", "c", ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, - {"time", &opt_time_format, "Read/Write time format (i.e. HH:mm:ss xx)", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, + {"time", &opt_time_format, "Read/Write time format (e.g. HH:mm:ss)", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr}, {"utc", &opt_utc, "Write timestamps with offset x to UTC time", nullptr, ARGTYPE_INT, "-23", "+23", nullptr}, }; @@ -154,8 +153,8 @@ class PathInfo { public: double length {0}; - time_t start {0}; - time_t time {0}; + QDateTime start; + int time {0}; double speed {0}; double total {0}; int count {0}; @@ -190,16 +189,9 @@ get_option_val(const char* option, const char* def) static void init_date_and_time_format() { - // This is old, and weird, code.. date_time_format is a global that's - // explicitly malloced and freed elsewhere. This isn't very C++ at all, - // but this format is on its deathbead for deprecation. const char* d = get_option_val(opt_date_format, kDefaultDateFormat); - QString d1 = convert_human_date_format(d); - const char* t = get_option_val(opt_time_format, kDefaultTimeFormat); - QString t1 = convert_human_time_format(t); - - date_time_format = QStringLiteral("%1 %2").arg(d1, t1); + date_time_format = QStringLiteral("%1 %2").arg(d, t); } static void @@ -263,11 +255,11 @@ prework_wpt_cb(const Waypoint* wpt) const Waypoint* prev = cur_info->prev_wpt; if (prev != nullptr) { - cur_info->time += (wpt->GetCreationTime().toTime_t() - prev->GetCreationTime().toTime_t()); + cur_info->time += prev->GetCreationTime().secsTo(wpt->GetCreationTime()); cur_info->length += waypt_distance_ex(prev, wpt); } else { cur_info->first_wpt = wpt; - cur_info->start = wpt->GetCreationTime().toTime_t(); + cur_info->start = wpt->GetCreationTime(); } cur_info->prev_wpt = wpt; cur_info->count++; @@ -364,30 +356,37 @@ print_position(const Waypoint* wpt) } static void -print_date_and_time(const time_t time, const bool time_only) +print_duration(int time) { - std::tm tm{}; - char tbuf[32]; - if (time < 0) { *fout << "\t"; return; } - if (time_only) { - tm = *gmtime(&time); - snprintf(tbuf, sizeof(tbuf), "%d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec); - *fout << QString::asprintf("%s", tbuf); - } else if (time != 0) { - if (gtxt_flags.utc) { - time_t t = time + utc_offs; - tm = *gmtime(&t); - } else { - tm = *localtime(&time); - } - strftime(tbuf, sizeof(tbuf), CSTR(date_time_format), &tm); - *fout << QString::asprintf("%s ", tbuf); +#if 1 + // perhaps durations can be longer than the max QTime of 23:59:59 + auto res = std::div(time, 60); + auto sec = res.rem; + res = std::div(res.quot, 60); + *fout << QString::asprintf("%d:%02d:%02d\t", res.quot, res.rem, sec); +#else + QTime qt = QTime(0, 0).addSecs(time); + *fout << qt.toString("H:mm:ss\t"); +#endif +} + +static void +print_date_and_time(const QDateTime& dt) +{ + if (!dt.isValid()) { + *fout << "\t"; + return; } - *fout << "\t"; + if (gtxt_flags.utc) { + *fout << dt.toOffsetFromUtc(utc_offs).toString(date_time_format); + } else { + *fout << dt.toLocalTime().toString(date_time_format); + } + *fout << " \t"; } static void @@ -444,7 +443,7 @@ print_distance(const double distance, const bool no_scale, const bool with_tab, } static void -print_speed(const double distance, const time_t time) +print_speed(const double distance, int time) { double dist = distance; const char* unit; @@ -576,7 +575,7 @@ write_waypt(const Waypoint* wpt) print_string("%s\t", garmin_fs_t::get_state(gmsd, "")); const char* country = gt_get_icao_country(garmin_fs_t::get_cc(gmsd, "")); print_string("%s\t", (country != nullptr) ? country : ""); - print_date_and_time(wpt->GetCreationTime().toTime_t(), false); + print_date_and_time(wpt->GetCreationTime()); if (wpt->HasUrlLink()) { UrlLink l = wpt->GetUrlLink(); print_string("%s\t", l.url_); @@ -657,8 +656,8 @@ track_disp_hdr_cb(const route_head* track) *fout << QStringLiteral("\r\n\r\nHeader\t%1\r\n").arg(headers[track_header]); } print_string("\r\nTrack\t%s\t", track->rte_name); - print_date_and_time(cur_info->start, false); - print_date_and_time(cur_info->time, true); + print_date_and_time(cur_info->start); + print_duration(cur_info->time); print_distance(cur_info->length, false, true, 0); print_speed(cur_info->length, cur_info->time); if (track->rte_urls.HasUrlLink()) { @@ -679,13 +678,11 @@ static void track_disp_wpt_cb(const Waypoint* wpt) { const Waypoint* prev = cur_info->prev_wpt; - time_t delta; - double dist; *fout << "Trackpoint\t"; print_position(wpt); - print_date_and_time(wpt->GetCreationTime().toTime_t(), false); + print_date_and_time(wpt->GetCreationTime()); if (is_valid_alt(wpt->altitude)) { print_distance(wpt->altitude, true, false, 0); } @@ -698,15 +695,15 @@ track_disp_wpt_cb(const Waypoint* wpt) if (prev != nullptr) { *fout << "\t"; - delta = wpt->GetCreationTime().toTime_t() - prev->GetCreationTime().toTime_t(); + int delta = prev->GetCreationTime().secsTo(wpt->GetCreationTime()); float temp = wpt->temperature_value_or(-999); if (temp != -999) { print_temperature(temp); } *fout << "\t"; - dist = waypt_distance_ex(prev, wpt); + double dist = waypt_distance_ex(prev, wpt); print_distance(dist, false, true, 0); - print_date_and_time(delta, true); + print_duration(delta); print_speed(dist, delta); print_course(prev, wpt); } @@ -737,7 +734,7 @@ static void garmin_txt_adjust_time(QDateTime& dt) { if (gtxt_flags.utc) { - dt = dt.toUTC().addSecs(dt.offsetFromUtc() - utc_offs); + dt.setOffsetFromUtc(utc_offs); } } @@ -867,88 +864,12 @@ free_headers() [](auto& list)->void { list.clear(); }); } -// Super simple attempt to convert strftime/strptime spec to Qt spec. -// This misses a LOT of cases and vagaries, but the reality is that we -// see very few date formats here. -static QString -strftime_to_timespec(const char* s) -{ - QString q; - int l = strlen(s); - q.reserve(l * 2); // no penalty if our guess is wrong. - - for (int i = 0; i < l; i++) { - switch (s[i]) { - case '%': - if (i < l-1) { - switch (s[++i]) { - case 'd': - q += "dd"; - continue; - case 'm': - q += "MM"; - continue; - case 'y': - q += "yy"; - continue; - case 'Y': - q += "yyyy"; - continue; - case 'H': - q += "HH"; - continue; - case 'M': - q += "mm"; - continue; - case 'S': - q += "ss"; - continue; - case 'A': - q += "dddd"; - continue; - case 'a': - q += "ddd"; - continue; - case 'B': - q += "MMMM"; - continue; - case 'C': - q += "yy"; - continue; - case 'D': - q += "MM/dd/yyyy"; - continue; - case 'T': - q += "hh:mm:ss"; - continue; - case 'F': - q += "yyyy-MM-dd"; - continue; - case 'p': - q += "AP"; - continue; - default: - warning(MYNAME ": omitting unknown strptime conversion \"%%%c\" in \"%s\"\n", s[i], s); - break; - } - } - break; - default: - q += s[i]; - break; - } - } - return q; -} - - /* data parsers */ static QDateTime parse_date_and_time(const QString& str) { - QString timespec = strftime_to_timespec(CSTR(date_time_format)); - return QDateTime::fromString(QString(str).trimmed(), timespec); + return QDateTime::fromString(QString(str).trimmed(), date_time_format); } static uint16_t diff --git a/gpsbabel-sample.ini b/gpsbabel-sample.ini index d2feb0217..7e8358420 100644 --- a/gpsbabel-sample.ini +++ b/gpsbabel-sample.ini @@ -16,8 +16,8 @@ ;------------------------------------------------------------------ [ garmin_txt ] -Date = DD.MM.YYYY -Time = HH:mm:ss XX +Date = dd.MM.yyyy +Time = HH:mm:ss Dist = M Temp = C Prec = 6 diff --git a/reference/format3.txt b/reference/format3.txt index e647cda7f..cd2f43cc5 100644 --- a/reference/format3.txt +++ b/reference/format3.txt @@ -210,7 +210,7 @@ option gdb roadbook Include major turn points (with description) from calculated file rwrwrw garmin_txt txt Garmin MapSource - txt (tab delimited) garmin_txt https://www.gpsbabel.org/WEB_DOC_DIR/fmt_garmin_txt.html -option garmin_txt date Read/Write date format (i.e. yyyy/mm/dd) string https://www.gpsbabel.org/WEB_DOC_DIR/fmt_garmin_txt.html#fmt_garmin_txt_o_date +option garmin_txt date Read/Write date format (e.g. yyyy/MM/dd) string https://www.gpsbabel.org/WEB_DOC_DIR/fmt_garmin_txt.html#fmt_garmin_txt_o_date option garmin_txt datum GPS datum (def. WGS 84) string WGS 84 https://www.gpsbabel.org/WEB_DOC_DIR/fmt_garmin_txt.html#fmt_garmin_txt_o_datum @@ -222,7 +222,7 @@ option garmin_txt prec Precision of coordinates integer 3 https://www.gpsbabel option garmin_txt temp Temperature unit [c=Celsius, f=Fahrenheit] string c https://www.gpsbabel.org/WEB_DOC_DIR/fmt_garmin_txt.html#fmt_garmin_txt_o_temp -option garmin_txt time Read/Write time format (i.e. HH:mm:ss xx) string https://www.gpsbabel.org/WEB_DOC_DIR/fmt_garmin_txt.html#fmt_garmin_txt_o_time +option garmin_txt time Read/Write time format (e.g. HH:mm:ss) string https://www.gpsbabel.org/WEB_DOC_DIR/fmt_garmin_txt.html#fmt_garmin_txt_o_time option garmin_txt utc Write timestamps with offset x to UTC time integer -23 +23 https://www.gpsbabel.org/WEB_DOC_DIR/fmt_garmin_txt.html#fmt_garmin_txt_o_utc diff --git a/reference/garmincategories.txt b/reference/garmincategories.txt index b7ec5292c..ccce49a8b 100644 --- a/reference/garmincategories.txt +++ b/reference/garmincategories.txt @@ -3,5 +3,5 @@ Datum WGS 84 Header Name Description Type Position Altitude Depth Proximity Temperature Display Mode Color Symbol Facility City State Country Date Modified Link Categories -Waypoint Hwy 119 The Diagonal User Waypoint N39 58.432183 W105 27.951022 Symbol & Name Unknown Flag, Blue 09.03.2013 13:45:12 PM Slow food,Category 11 -Waypoint Hwy 72 The Peak to Peak User Waypoint N40 00.238037 W105 29.937744 Symbol & Name Unknown Flag, Blue 09.03.2013 13:45:02 PM +Waypoint Hwy 119 The Diagonal User Waypoint N39 58.432183 W105 27.951022 Symbol & Name Unknown Flag, Blue 09.03.2013 13:45:12 Slow food,Category 11 +Waypoint Hwy 72 The Peak to Peak User Waypoint N40 00.238037 W105 29.937744 Symbol & Name Unknown Flag, Blue 09.03.2013 13:45:02 diff --git a/reference/help.txt b/reference/help.txt index 8a6f24c5e..fe8c1794e 100644 --- a/reference/help.txt +++ b/reference/help.txt @@ -110,13 +110,13 @@ File Types (-i and -o options): dropwpt (0/1) Don't create waypoints for non-user points roadbook (0/1) Include major turn points (with description) from garmin_txt Garmin MapSource - txt (tab delimited) - date Read/Write date format (i.e. yyyy/mm/dd) + date Read/Write date format (e.g. yyyy/MM/dd) datum GPS datum (def. WGS 84) dist Distance unit [m=metric, s=statute] grid Write position using this grid. prec Precision of coordinates temp Temperature unit [c=Celsius, f=Fahrenheit] - time Read/Write time format (i.e. HH:mm:ss xx) + time Read/Write time format (e.g. HH:mm:ss) utc Write timestamps with offset x to UTC time garmin_poi Garmin POI database snlen Max synthesized shortname length diff --git a/util.cc b/util.cc index c625e95d4..3d6f776db 100644 --- a/util.cc +++ b/util.cc @@ -20,13 +20,13 @@ */ #include // for sort -#include // for isspace, isalpha, ispunct, tolower, toupper +#include // for isspace, tolower #include // for errno #include // for INT_MAX, INT_MIN #include // for fabs, floor #include // for size_t, vsnprintf, FILE, fopen, printf, sprintf, stderr, stdin, stdout #include // for abs, calloc, free, malloc, realloc -#include // for strlen, strcat, strstr, memcpy, strcmp, strcpy, strdup, strchr, strerror +#include // for strcmp, strstr, memcpy, strdup, strchr, strerror, strlen #include // for QByteArray #include // for QChar, operator<=, operator>= @@ -563,169 +563,6 @@ rot13(const QString& s) return r; } -/* - * Convert a human readable date format (i.e. "YYYY/MM/DD") into - * a format usable for strftime and others - */ - -QString -convert_human_date_format(const char* human_datef) -{ - char* result = (char*) xcalloc((2*strlen(human_datef)) + 1, 1); - char* cout = result; - char prev = '\0'; - int ylen = 0; - - for (const char* cin = human_datef; *cin; cin++) { - char okay = 1; - - if (toupper(*cin) != 'Y') { - ylen = 0; - } - if (isalpha(*cin)) { - switch (*cin) { - case 'y': - case 'Y': - if (prev != 'Y') { - strcat(cout, "%y"); - cout += 2; - prev = 'Y'; - } - ylen++; - if (ylen > 2) { - *(cout-1) = 'Y'; - } - break; - case 'm': - case 'M': - if (prev != 'M') { - strcat(cout, "%m"); - cout += 2; - prev = 'M'; - } - break; - case 'd': - case 'D': - if (prev != 'D') { - strcat(cout, "%d"); - cout += 2; - prev = 'D'; - } - break; - default: - okay = 0; - } - } else if (ispunct(*cin)) { - *cout++ = *cin; - prev = '\0'; - } else { - okay = 0; - } - - if (okay == 0) { - fatal("Invalid character \"%c\" in date format \"%s\"!\n", *cin, human_datef); - } - } - QString rv(result); - xfree(result); - return rv; -} - -/* - * Convert a human readable time format (i.e. "HH:mm:ss") into - * a format usable for strftime and others - */ - -QString -convert_human_time_format(const char* human_timef) -{ - char* result = (char*) xcalloc((2*strlen(human_timef)) + 1, 1); - char* cout = result; - char prev = '\0'; - - for (const char* cin = human_timef; *cin; cin++) { - int okay = 1; - - if (isalpha(*cin)) { - switch (*cin) { - case 'S': - case 's': - if (prev != 'S') { - strcat(cout, "%S"); - cout += 2; - prev = 'S'; - } - break; - - case 'M': - case 'm': - if (prev != 'M') { - strcat(cout, "%M"); - cout += 2; - prev = 'M'; - } - break; - - case 'h': /* 12-hour-clock */ - if (prev != 'H') { - strcat(cout, "%l"); /* 1 .. 12 */ - cout += 2; - prev = 'H'; - } else { - *(cout-1) = 'I'; /* 01 .. 12 */ - } - break; - - case 'H': /* 24-hour-clock */ - if (prev != 'H') { - strcat(cout, "%k"); - cout += 2; - prev = 'H'; - } else { - *(cout-1) = 'H'; - } - break; - - case 'x': - if (prev != 'X') { - strcat(cout, "%P"); - cout += 2; - prev = 'X'; - } else { - *(cout-1) = 'P'; - } - break; - - case 'X': - if (prev != 'X') { - strcat(cout, "%p"); - cout += 2; - prev = 'X'; - } else { - *(cout-1) = 'p'; - } - break; - - default: - okay = 0; - } - } else if (ispunct(*cin) || isspace(*cin)) { - *cout++ = *cin; - prev = '\0'; - } else { - okay = 0; - } - - if (okay == 0) { - fatal("Invalid character \"%c\" in time format \"%s\"!\n", *cin, human_timef); - } - } - QString rv(result); - xfree(result); - return rv; -} - - /* * Return a decimal degree pair as * DD.DDDDD DD MM.MMM or DD MM SS.S diff --git a/xmldoc/formats/options/garmin_txt-date.xml b/xmldoc/formats/options/garmin_txt-date.xml index 188eb225d..dbe78cd2d 100644 --- a/xmldoc/formats/options/garmin_txt-date.xml +++ b/xmldoc/formats/options/garmin_txt-date.xml @@ -1,4 +1,8 @@ -This option specifies the input and output format for the date. The format -is written similarly to those in Windows. An example format is "YYYY/MM/DD". +This option specifies the input and output format for the date. +Examples are + +"yyyy/MM/dd" - four digit year, month with leading zero, day with leading zero. +"M/d/yy" - month without leading zero, day without leading zero, two digit year. + diff --git a/xmldoc/formats/options/garmin_txt-time.xml b/xmldoc/formats/options/garmin_txt-time.xml index 21675249b..0b4e13822 100644 --- a/xmldoc/formats/options/garmin_txt-time.xml +++ b/xmldoc/formats/options/garmin_txt-time.xml @@ -1,4 +1,10 @@ -This option specifies the input and output format for the time. The format -is written similarly to those in Windows. An example format is "hh:mm:ss xx". +This option specifies the input and output format for the time. +Examples are: + +"h:mm:ss AP" - hour without leading zero and range 1-12, AM/PM display. +"hh:mm:ss AP" - hour with leading zero and range 1-12, AM/PM display. +"H:mm:ss" - hour without leading zero and range 0-23. +"HH:mm:ss" - hour with leading zero and range 0-23. +