Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

date time formatting, take 3. #1209

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -966,8 +966,23 @@ 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" */

struct datetimefield_t {
bool year{false};
bool month{false};
bool day{false};
bool hour{false};
bool minute{false};
bool second{false};
bool ampm{false};
};

std::pair<QString, datetimefield_t> convert_human_datetime_format(QStringView human_datetimef, bool read);
inline std::pair<QString, datetimefield_t>
convert_human_datetime_format(const char* human_datetimef, bool read)
{
return convert_human_datetime_format(QString(human_datetimef), read);
}
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 */
Expand Down
178 changes: 50 additions & 128 deletions garmin_txt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,9 @@
#include <cctype> // for toupper
#include <cmath> // for fabs, floor
#include <cstdint> // for uint16_t
#include <cstdio> // for sscanf, fprintf, snprintf, stderr
#include <cstdlib> // for abs
#include <cstring> // for strstr, strlen
#include <ctime> // for time_t, gmtime, localtime, strftime
#include <cstdio> // for sscanf, fprintf, stderr
#include <cstdlib> // for abs, div
#include <cstring> // for strstr
#include <optional> // for optional
#include <utility> // for pair, make_pair

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -121,7 +120,7 @@ static std::array<QList<std::pair<QString, int>>, 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)
Expand Down Expand Up @@ -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};
Expand Down Expand Up @@ -187,19 +186,13 @@ get_option_val(const char* option, const char* def)
return c;
}

static void
init_date_and_time_format()
static QString
init_date_and_time_format(bool read)
{
// 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);
return convert_human_datetime_format(QStringLiteral("%1 %2").arg(d, t), read).first;
}

static void
Expand Down Expand Up @@ -263,11 +256,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++;
Expand Down Expand Up @@ -364,28 +357,35 @@ print_position(const Waypoint* wpt)
}

static void
print_date_and_time(const time_t time, const bool time_only)
print_duration(int duration_secs)
{
std::tm tm{};
char tbuf[32];
if (duration_secs < 0) {
*fout << "\t";
return;
}
#if 1
// perhaps durations can be longer than the max QTime of 23:59:59
auto res = std::div(duration_secs, 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(duration_secs);
*fout << qt.toString("H:mm:ss\t");
#endif
}

if (time < 0) {
static void
print_date_and_time(const QDateTime& dt)
{
if (!dt.isValid()) {
*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 (gtxt_flags.utc) {
*fout << dt.toOffsetFromUtc(utc_offs).toString(date_time_format);
} else {
*fout << dt.toLocalTime().toString(date_time_format);
}
*fout << "\t";
}
Expand Down Expand Up @@ -444,7 +444,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;
Expand Down Expand Up @@ -576,7 +576,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_);
Expand Down Expand Up @@ -657,8 +657,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()) {
Expand All @@ -679,13 +679,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);
}
Expand All @@ -698,15 +696,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);
}
Expand Down Expand Up @@ -737,7 +735,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);
}
}

Expand All @@ -751,7 +749,7 @@ garmin_txt_wr_init(const QString& fname)

gtxt_flags.metric = (toupper(*get_option_val(opt_dist, "m")) == 'M');
gtxt_flags.celsius = (toupper(*get_option_val(opt_temp, "c")) == 'C');
init_date_and_time_format();
date_time_format = init_date_and_time_format(false);
if (opt_precision) {
precision = xstrtoi(opt_precision, nullptr, 10);
if (precision < 0) {
Expand Down Expand Up @@ -867,88 +865,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
Expand Down Expand Up @@ -1182,7 +1104,7 @@ parse_waypoint(const QStringList& lineparts)
garmin_txt_adjust_time(dt);
wpt->SetCreationTime(dt);
}
break;
break;
case 17: {
wpt->AddUrlLink(str);
}
Expand Down Expand Up @@ -1318,7 +1240,7 @@ parse_track_waypoint(const QStringList& lineparts)
garmin_txt_adjust_time(dt);
wpt->SetCreationTime(dt);
}
break;
break;
case 3:
if (parse_distance(str, &x, 1, MYNAME)) {
wpt->altitude = x;
Expand Down Expand Up @@ -1363,7 +1285,7 @@ garmin_txt_rd_init(const QString& fname)
datum_index = -1;
grid_index = (grid_type) -1;

init_date_and_time_format();
date_time_format = init_date_and_time_format(true);
garmin_txt_utc_option();
}

Expand Down
4 changes: 2 additions & 2 deletions gpsbabel-sample.ini
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

;------------------------------------------------------------------
[ garmin_txt ]
Date = DD.MM.YYYY
Time = HH:mm:ss XX
Date = dd.MM.yyyy
Time = hh:mm:ss XX
Dist = M
Temp = C
Prec = 6
Expand Down
2 changes: 1 addition & 1 deletion reference/datetime_read.xcsv
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
LAT_DECIMAL,LON_DECIMAL,EXCEL_TIME,GMT_TIME-12HR,GMT_TIME-DATE-24HR,GMT_TIME-24HR,LOCAL_TIME-12HR,LOCAL_TIME-DATE-24HR,LOCAL_TIME-24HR,ISO_TIME,ISO_TIME_MS,NET_TIME,TIMET_TIME,TIMET_TIME_MS,YYYYMMDD_TIME
LAT_DECIMAL,LON_DECIMAL,EXCEL_TIME,GMT_TIME-12HR,GMT_TIME-DATE-24HR,GMT_TIME-24HR,GMT_DATE,LOCAL_TIME-12HR,LOCAL_TIME-DATE-24HR,LOCAL_TIME-24HR,LOCAL_DATE,ISO_TIME,ISO_TIME_MS,NET_TIME,TIMET_TIME,TIMET_TIME_MS,YYYYMMDD_TIME
40.000000,-105.070000,1970-01-03T07:04:05Z
40.050000,-105.120000,1970-01-03T12:04:05Z
40.120000,-105.190000,1970-01-04T19:04:05Z
Expand Down
14 changes: 7 additions & 7 deletions reference/datetime~xcsv.xcsv
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
LAT_DECIMAL,LON_DECIMAL,EXCEL_TIME,GMT_TIME-12HR,GMT_TIME-DATE-24HR,GMT_TIME-24HR,LOCAL_TIME-12HR,LOCAL_TIME-DATE-24HR,LOCAL_TIME-24HR,ISO_TIME,ISO_TIME_MS,NET_TIME,TIMET_TIME,TIMET_TIME_MS,YYYYMMDD_TIME
40.000000,-105.070000,2.557129450231e+04,07:04:05 AM,1970-01-03 07:04:05,07:04:05,12:04:05 AM,1970-01-03 00:04:05,00:04:05,1970-01-03T07:04:05Z,1970-01-03T07:04:05Z,621357950450000000,198245,198245000,19700103
40.050000,-105.120000,2.557150283565e+04,12:04:05 PM,1970-01-03 12:04:05,12:04:05,05:04:05 AM,1970-01-03 05:04:05,05:04:05,1970-01-03T12:04:05Z,1970-01-03T12:04:05Z,621358130450000000,216245,216245000,19700103
40.120000,-105.190000,2.557279450231e+04,07:04:05 PM,1970-01-04 19:04:05,19:04:05,12:04:05 PM,1970-01-04 12:04:05,12:04:05,1970-01-04T19:04:05Z,1970-01-04T19:04:05Z,621359246450000000,327845,327845000,19700104
40.170000,-105.000000,2.557300283565e+04,12:04:05 AM,1970-01-05 00:04:05,00:04:05,05:04:05 PM,1970-01-04 17:04:05,17:04:05,1970-01-05T00:04:05Z,1970-01-05T00:04:05Z,621359426450000000,345845,345845000,19700105
40.040000,-105.110000,6.209346187500e+04,11:05:06 AM,2069-12-31 11:05:06,11:05:06,04:05:06 AM,2069-12-31 04:05:06,04:05:06,2069-12-31T11:05:06Z,2069-12-31T11:05:06Z,652913103060000000,3155713506,3155713506000,20691231
40.900000,-105.900000,,,,,,,,,,,,,
LAT_DECIMAL,LON_DECIMAL,EXCEL_TIME,GMT_TIME-12HR,GMT_TIME-DATE-24HR,GMT_TIME-24HR,GMT_DATE,LOCAL_TIME-12HR,LOCAL_TIME-DATE-24HR,LOCAL_TIME-24HR,LOCAL_DATE,ISO_TIME,ISO_TIME_MS,NET_TIME,TIMET_TIME,TIMET_TIME_MS,YYYYMMDD_TIME
40.000000,-105.070000,2.557129450231e+04,07:04:05 AM,1970-01-03 07:04:05,07:04:05,1970-01-03,12:04:05 AM,1970-01-03 00:04:05,00:04:05,1970-01-03,1970-01-03T07:04:05Z,1970-01-03T07:04:05Z,621357950450000000,198245,198245000,19700103
40.050000,-105.120000,2.557150283565e+04,12:04:05 PM,1970-01-03 12:04:05,12:04:05,1970-01-03,05:04:05 AM,1970-01-03 05:04:05,05:04:05,1970-01-03,1970-01-03T12:04:05Z,1970-01-03T12:04:05Z,621358130450000000,216245,216245000,19700103
40.120000,-105.190000,2.557279450231e+04,07:04:05 PM,1970-01-04 19:04:05,19:04:05,1970-01-04,12:04:05 PM,1970-01-04 12:04:05,12:04:05,1970-01-04,1970-01-04T19:04:05Z,1970-01-04T19:04:05Z,621359246450000000,327845,327845000,19700104
40.170000,-105.000000,2.557300283565e+04,12:04:05 AM,1970-01-05 00:04:05,00:04:05,1970-01-05,05:04:05 PM,1970-01-04 17:04:05,17:04:05,1970-01-04,1970-01-05T00:04:05Z,1970-01-05T00:04:05Z,621359426450000000,345845,345845000,19700105
40.040000,-105.110000,6.209346187500e+04,11:05:06 AM,2069-12-31 11:05:06,11:05:06,2069-12-31,04:05:06 AM,2069-12-31 04:05:06,04:05:06,2069-12-31,2069-12-31T11:05:06Z,2069-12-31T11:05:06Z,652913103060000000,3155713506,3155713506000,20691231
40.900000,-105.900000,,,,,,,,,,,,,,,
1 change: 1 addition & 0 deletions reference/filter1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ option track name Use only track(s) where title matches given name string htt
option track start Use only track points after or at this timestamp string https://www.gpsbabel.org/WEB_DOC_DIR/filter_track.html#fmt_track_o_start
option track stop Use only track points before or at this timestamp string https://www.gpsbabel.org/WEB_DOC_DIR/filter_track.html#fmt_track_o_stop
option track title Basic title for new track(s) string https://www.gpsbabel.org/WEB_DOC_DIR/filter_track.html#fmt_track_o_title
option track dtitle Dynamic title for new track(s) string https://www.gpsbabel.org/WEB_DOC_DIR/filter_track.html#fmt_track_o_dtitle
option track fix Synthesize GPS fixes (PPS, DGPS, 3D, 2D, NONE) string https://www.gpsbabel.org/WEB_DOC_DIR/filter_track.html#fmt_track_o_fix
option track course Synthesize course boolean https://www.gpsbabel.org/WEB_DOC_DIR/filter_track.html#fmt_track_o_course
option track speed Synthesize speed boolean https://www.gpsbabel.org/WEB_DOC_DIR/filter_track.html#fmt_track_o_speed
Expand Down
Loading