diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 7a143ac06..998c82702 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -18,11 +18,6 @@ jobs: fail-fast: false matrix: include: - - QT_VERSION: '6.2.4' - XCODE_VERSION: '13.4.1' - GENERATOR: 'Ninja' - RELEASE: false - os: macos-12 - QT_VERSION: '6.2.4' XCODE_VERSION: '14.3.1' GENERATOR: 'Ninja' @@ -38,6 +33,11 @@ jobs: GENERATOR: 'Ninja' RELEASE: true os: macos-14 + - QT_VERSION: '6.8.0' + XCODE_VERSION: '16.0' + GENERATOR: 'Ninja' + RELEASE: false + os: macos-15 steps: - name: Checkout repository diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b37a74a0d..6b9d5d932 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -44,6 +44,14 @@ jobs: RELEASE: true GENERATOR: 'Ninja' os: windows-latest + - QT_VERSION: '6.8.0' + ARCH: 'amd64' + HOST_ARCH: 'amd64' + COMPILER: 'msvc2022_64' + METHOD: 'aqt' + RELEASE: false + GENERATOR: 'Ninja' + os: windows-latest steps: - name: Checkout repository diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a9a1df0d..c0841ee75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,9 @@ if(${Qt6Core_VERSION} VERSION_LESS 6.2) else() message(STATUS "Using Qt6 version ${Qt6Core_VERSION}") endif() +if(${Qt6Core_VERSION} VERSION_GREATER_EQUAL 6.5) + target_compile_definitions(gpsbabel PRIVATE LIGHTWEIGHT_TIMEZONES_SUPPORTED) +endif() option(GPSBABEL_ENABLE_PCH "enable precompiled headers." ON) if (GPSBABEL_ENABLE_PCH) diff --git a/defs.h b/defs.h index 316468c2e..84ecf4636 100644 --- a/defs.h +++ b/defs.h @@ -37,6 +37,9 @@ #include // for QString #include // for QStringView #include // for QTextCodec +#ifdef LIGHTWEIGHT_TIMEZONES_SUPPORTED +#include // for QTimeZone +#endif #include // for CaseInsensitive #include // for QForeachContainer, qMakeForeachContainer, foreach, qint64 @@ -1042,4 +1045,12 @@ int color_to_bbggrr(const char* cname); constexpr double unknown_alt = -99999999.0; constexpr int unknown_color = -1; +#ifdef LIGHTWEIGHT_TIMEZONES_SUPPORTED +constexpr QTimeZone::Initialization QtLocalTime = QTimeZone::LocalTime; +constexpr QTimeZone::Initialization QtUTC = QTimeZone::UTC; +#else +constexpr Qt::TimeSpec QtLocalTime = Qt::LocalTime; +constexpr Qt::TimeSpec QtUTC = Qt::UTC; +#endif + #endif // DEFS_H_INCLUDED_ diff --git a/dg-100.cc b/dg-100.cc index 53bc50fd8..3279daeae 100644 --- a/dg-100.cc +++ b/dg-100.cc @@ -109,7 +109,7 @@ Dg100Format::bintime2utc(int date, int time) date /= 100; int day = date; - return QDateTime(QDate(year, mon, day), QTime(hour, min, sec), Qt::UTC); + return QDateTime(QDate(year, mon, day), QTime(hour, min, sec), QtUTC); } void diff --git a/exif.cc b/exif.cc index c53a2db96..624f5b4ed 100644 --- a/exif.cc +++ b/exif.cc @@ -47,6 +47,9 @@ #include // for QString #include // for QTextCodec #include // for QTime +#ifdef LIGHTWEIGHT_TIMEZONES_SUPPORTED +#include // for QTimeZone +#endif #include // for QVariant #include // for QVector #include // for UTC, ISODate @@ -728,7 +731,11 @@ ExifFormat::exif_get_exif_time(ExifApp* app) const // Correct the date time by supplying the offset from UTC. int offset_hours = match.captured(1).append(match.captured(2)).toInt(); int offset_mins = match.captured(1).append(match.captured(3)).toInt(); +#ifdef LIGHTWEIGHT_TIMEZONES_SUPPORTED + res.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(((offset_hours * 60) + offset_mins) * 60)); +#else res.setOffsetFromUtc(((offset_hours * 60) + offset_mins) * 60); +#endif } else if (opt_offsettime) { // Only warn for user supplied offsets. // Offset tags may indicate the offset was unknown, e.g. " : ". @@ -912,7 +919,7 @@ ExifFormat::exif_waypt_from_exif_app(ExifApp* app) const } } - gps_datetime = QDateTime(datestamp, timestamp, Qt::UTC); + gps_datetime = QDateTime(datestamp, timestamp, QtUTC); if (gps_datetime.isValid()) { if (global_opts.debug_level >= 3) { printf(MYNAME "-GPSTimeStamp = %s\n", qPrintable(gps_datetime.toString(Qt::ISODateWithMs))); diff --git a/garmin_gpi.cc b/garmin_gpi.cc index 745f68c2f..3d0ee0332 100644 --- a/garmin_gpi.cc +++ b/garmin_gpi.cc @@ -178,7 +178,7 @@ GarminGPIFormat::read_header() if (GPI_DBG) { time_t crdate = GPS_Math_Gtime_To_Utime(rdata->crdate); warning("crdate = %lld (%s)\n", (long long) rdata->crdate, - CSTR(QDateTime::fromSecsSinceEpoch(crdate, Qt::UTC).toString(Qt::ISODate))); + CSTR(QDateTime::fromSecsSinceEpoch(crdate, QtUTC).toString(Qt::ISODate))); } (void) gbfgetint16(fin); /* 0 */ diff --git a/gdb.cc b/gdb.cc index 67cd26aec..3b024e063 100644 --- a/gdb.cc +++ b/gdb.cc @@ -62,7 +62,7 @@ constexpr unsigned int GDB_DBG_TRK = 4; constexpr unsigned int GDB_DBG_WPTe = 8; constexpr unsigned int GDB_DBG_RTEe = 16; -constexpr unsigned int GDB_DBG_TRKe = 32; +//constexpr unsigned int GDB_DBG_TRKe = 32; //constexpr unsigned int GDB_DEBUG = (GDB_DBG_WPTe) /* | GDB_DBG_RTE) */; //constexpr unsigned int GDB_DEBUG = 0xff; @@ -1126,7 +1126,7 @@ GdbFormat::write_header() * This is our "Watermark" to show this file was created by GPSbabel. * The date/time used to be from CVS, and may be from git in the future. */ - static const QDateTime gdb_release_dt = QDateTime(QDate(2011, 4, 14), QTime(1, 30, 1), Qt::UTC); + static const QDateTime gdb_release_dt = QDateTime(QDate(2011, 4, 14), QTime(1, 30, 1), QtUTC); gdb_write_cstr(QStringLiteral("GPSBabel-%1").arg(gpsbabel_version)); gdb_write_cstr(gdb_release_dt.toString(u"MMM dd yyyy")); gdb_write_cstr(gdb_release_dt.toString(u"HH:mm:ss")); diff --git a/globalsat_sport.cc b/globalsat_sport.cc index 813586728..e4a69c383 100644 --- a/globalsat_sport.cc +++ b/globalsat_sport.cc @@ -529,7 +529,7 @@ GlobalsatSportFormat::track_read() if (timezn != nullptr) { gpsDateTime = gpsbabel::DateTime(QDateTime(gpsDate, gpsTime, *timezn).toUTC()); } else { - gpsDateTime = gpsbabel::DateTime(QDateTime(gpsDate, gpsTime, Qt::LocalTime).toUTC()); + gpsDateTime = gpsbabel::DateTime(QDateTime(gpsDate, gpsTime, QtLocalTime).toUTC()); } int laps_in_package = header.gh_laprec.LapIndex - header.gh_ptrec.Index + 1; diff --git a/gpx.cc b/gpx.cc index 96af32faa..080ad5659 100644 --- a/gpx.cc +++ b/gpx.cc @@ -517,7 +517,7 @@ xml_parse_time(const QString& dateTimeString) if (res > 0) { QDate date(year, mon, mday); QTime time(hour, min, sec); - dt = QDateTime(date, time, Qt::UTC); + dt = QDateTime(date, time, QtUTC); // Fractional part of time. if (fsec) { diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 04e095498..dc4e0939c 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -22,7 +22,7 @@ if(NOT UNIX OR APPLE) endif() # Find the QtCore library -find_package(Qt6 REQUIRED COMPONENTS Core Gui Network SerialPort Widgets Xml) +find_package(Qt6 REQUIRED COMPONENTS Core Gui Network SerialPort Widgets Xml OPTIONAL_COMPONENTS WebEngineWidgets WebChannel) list(APPEND QT_LIBRARIES Qt6::Core Qt6::Gui Qt6::Network Qt6::SerialPort Qt6::Widgets Qt6::Xml) if(${Qt6Core_VERSION} VERSION_LESS 6.2) message(FATAL_ERROR "Qt version ${Qt6Core_VERSION} found, but version 6.2 or newer is required.") @@ -30,11 +30,13 @@ else() message(STATUS "Using Qt6 version ${Qt6Core_VERSION}") endif() -option(GPSBABEL_MAPPREVIEW "enable map preview." ON) +cmake_dependent_option(GPSBABEL_MAPPREVIEW "enable map preview." ON "Qt6WebEngineWidgets_FOUND;Qt6WebChannel_FOUND" OFF) if (GPSBABEL_MAPPREVIEW) - find_package(Qt6 REQUIRED COMPONENTS WebEngineWidgets WebChannel) list(APPEND QT_LIBRARIES Qt6::WebEngineWidgets Qt6::WebChannel) else() + if (NOT Qt6WebEngineWidgets_FOUND OR NOT Qt6WebChannel_FOUND) + message(WARNING "Optional components Qt6 WebEngineWidgets and/or Qt6 WebChannel not found, MAPPREVIEW is not available.") + endif() target_compile_definitions(gpsbabelfe PRIVATE DISABLE_MAPPREVIEW) endif() diff --git a/gui/filterwidgets.cc b/gui/filterwidgets.cc index 39426e171..9600149a9 100644 --- a/gui/filterwidgets.cc +++ b/gui/filterwidgets.cc @@ -30,6 +30,9 @@ #include // for QEvent, QEvent::LocaleChange #include // for QLabel #include // for QRadioButton +#if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)) +#include // for QTimeZone +#endif #include // for LocalTime, UTC @@ -75,17 +78,27 @@ TrackWidget::TrackWidget(QWidget* parent, TrackFilterData& tfd): FilterWidget(pa ui.startEdit->setDisplayFormat("dd MMM yyyy hh:mm:ss AP"); ui.stopEdit->setDisplayFormat("dd MMM yyyy hh:mm:ss AP"); - assert(tfd.startTime.timeSpec() == tfd.stopTime.timeSpec()); - assert((tfd.startTime.timeSpec() == Qt::UTC) || (tfd.startTime.timeSpec() == Qt::LocalTime)); // Qt5 QDateTimeEdit::setDateTime ignored the passed QDateTime::timeSpec. // Qt6 QDateTimeEdit::setDateTime will convert the passed QDateTime if the passed // QDateTime::timeSpec doesn't match QDateTimeEdit::timeSpec. // If the two timeSpecs match Qt5 and Qt6 behave the same. +#if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)) + assert(tfd.startTime.timeZone() == tfd.stopTime.timeZone()); + assert((tfd.startTime.timeZone() == QTimeZone::UTC) || (tfd.startTime.timeZone() == QTimeZone::LocalTime)); + ui.startEdit->setTimeZone(tfd.startTime.timeZone()); + ui.stopEdit->setTimeZone(tfd.stopTime.timeZone()); + // Make sure the initial state of the localTime and utc radio buttons + // is in agreement with the startTime::timeSpec and stopTime::timeSpec. + tfd.localTime = tfd.startTime.timeZone() == QTimeZone::LocalTime; +#else + assert(tfd.startTime.timeSpec() == tfd.stopTime.timeSpec()); + assert((tfd.startTime.timeSpec() == Qt::UTC) || (tfd.startTime.timeSpec() == Qt::LocalTime)); ui.startEdit->setTimeSpec(tfd.startTime.timeSpec()); ui.stopEdit->setTimeSpec(tfd.stopTime.timeSpec()); // Make sure the initial state of the localTime and utc radio buttons // is in agreement with the startTime::timeSpec and stopTime::timeSpec. tfd.localTime = tfd.startTime.timeSpec() == Qt::LocalTime; +#endif tfd.utc = !tfd.localTime; // Collect the data fields. @@ -188,11 +201,21 @@ void TrackWidget::splitDistanceX() void TrackWidget::TZX() { if (ui.localTime->isChecked()) { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)) + ui.startEdit->setTimeZone(QTimeZone::LocalTime); + ui.stopEdit->setTimeZone(QTimeZone::LocalTime); +#else ui.startEdit->setTimeSpec(Qt::LocalTime); ui.stopEdit->setTimeSpec(Qt::LocalTime); +#endif } else { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)) + ui.startEdit->setTimeZone(QTimeZone::UTC); + ui.stopEdit->setTimeZone(QTimeZone::UTC); +#else ui.startEdit->setTimeSpec(Qt::UTC); ui.stopEdit->setTimeSpec(Qt::UTC); +#endif } // Force update of Edit displays, so the displayed // datetimes are in sync with the specified time spec. diff --git a/gui/upgrade.cc b/gui/upgrade.cc index 73211c4cf..3009b6a72 100644 --- a/gui/upgrade.cc +++ b/gui/upgrade.cc @@ -262,14 +262,20 @@ void UpgradeCheck::httpRequestFinished(QNetworkReply* reply) QString oresponse(reply->readAll()); QDomDocument document; - int line = -1; - QString error_text; // This shouldn't ever be seen by a user. - if (!document.setContent(oresponse, &error_text, &line)) { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)) + if (auto result = document.setContent(oresponse); !result) { +#else + struct { + int errorLine{-1}; + QString errorMessage; + } result; + if (!document.setContent(oresponse, &result.errorMessage, &result.errorLine)) { +#endif QMessageBox::critical(nullptr, tr("Error"), tr("Invalid return data at line %1: %2.") - .arg(line) - .arg(error_text)); + .arg(result.errorLine) + .arg(result.errorMessage)); babelData_.upgradeErrors_++; replyId_ = nullptr; reply->deleteLater(); diff --git a/igc.cc b/igc.cc index 7ef925ee9..1b831a043 100644 --- a/igc.cc +++ b/igc.cc @@ -162,7 +162,7 @@ void IgcFormat::TaskRecordReader::igc_task_rec(const char* rec) } else { year += 1900; } - creation = QDateTime(QDate(year, month, day), QTime(hour, minute, second), Qt::UTC); + creation = QDateTime(QDate(year, month, day), QTime(hour, minute, second), QtUTC); if (!creation.isValid()) { fatal(MYNAME ": bad date time\n%s\n", rec); } @@ -398,7 +398,7 @@ void IgcFormat::read() pres_wpt->fs.FsChainAdd(fsdata); } - pres_wpt->SetCreationTime(QDateTime(date, tod, Qt::UTC)); + pres_wpt->SetCreationTime(QDateTime(date, tod, QtUTC)); // Add the waypoint to the pressure altitude track if (pres_alt) { diff --git a/lowranceusr.cc b/lowranceusr.cc index 8e38e6f8a..9e12d3931 100644 --- a/lowranceusr.cc +++ b/lowranceusr.cc @@ -246,7 +246,7 @@ LowranceusrFormat::lowranceusr4_writestr(const QString& buf, gbfile* file, int b gpsbabel::DateTime LowranceusrFormat::lowranceusr4_get_timestamp(unsigned int jd_number, unsigned int msecs) { - QDateTime qdt = QDateTime(QDate::fromJulianDay(jd_number), QTime(0, 0, 0), Qt::UTC).addMSecs(msecs); + QDateTime qdt = QDateTime(QDate::fromJulianDay(jd_number), QTime(0, 0, 0), QtUTC).addMSecs(msecs); return qdt; } diff --git a/mtk_logger.cc b/mtk_logger.cc index ea7fcebbf..3a2217c85 100644 --- a/mtk_logger.cc +++ b/mtk_logger.cc @@ -839,7 +839,7 @@ int MtkLoggerBase::csv_line(gbfile* csvFile, int idx, unsigned long bmask, data_ , (itm->rcr&0x0004)?"D":"", (itm->rcr&0x0008)?"B":""); if (bmask & (1U<timestamp, Qt::UTC); + QDateTime dt = QDateTime::fromSecsSinceEpoch(itm->timestamp, QtUTC); dt = dt.addMSecs(itm->timestamp_ms); QString timestamp = dt.toUTC().toString(u"yyyy/MM/dd,hh:mm:ss.zzz"); diff --git a/nmea.cc b/nmea.cc index f07d78b9f..62f906242 100644 --- a/nmea.cc +++ b/nmea.cc @@ -324,14 +324,14 @@ void NmeaFormat::nmea_set_waypoint_time(Waypoint* wpt, QDateTime* prev, const QDate& date, const QTime& time) { if (date.isValid()) { - wpt->SetCreationTime(QDateTime(date, time, Qt::UTC)); + wpt->SetCreationTime(QDateTime(date, time, QtUTC)); if (wpt->wpt_flags.fmt_use != 0) { wpt->wpt_flags.fmt_use = 0; without_date--; } *prev = wpt->GetCreationTime(); } else if (prev->date().isValid()) { - wpt->SetCreationTime(QDateTime(prev->date(), time, Qt::UTC)); + wpt->SetCreationTime(QDateTime(prev->date(), time, QtUTC)); if (*prev > wpt->creation_time) { /* go over midnight ? */ wpt->creation_time = wpt->creation_time.addDays(1); @@ -342,7 +342,7 @@ NmeaFormat::nmea_set_waypoint_time(Waypoint* wpt, QDateTime* prev, const QDate& } *prev = wpt->GetCreationTime(); } else { - wpt->SetCreationTime(QDateTime(QDate(), time, Qt::UTC)); + wpt->SetCreationTime(QDateTime(QDate(), time, QtUTC)); if (wpt->wpt_flags.fmt_use == 0) { wpt->wpt_flags.fmt_use = 1; without_date++; @@ -639,7 +639,7 @@ NmeaFormat::gpzda_parse(const QString& ibuf) // The prev_datetime data member might be used by // nmea_fix_timestamps and nmea_set_waypoint_time. - prev_datetime = QDateTime(date, time, Qt::UTC); + prev_datetime = QDateTime(date, time, QtUTC); } } @@ -839,7 +839,7 @@ NmeaFormat::nmea_fix_timestamps(route_head* track) return; } - QDateTime prev = QDateTime(opt_tm, QTime(0, 0), Qt::UTC); + QDateTime prev = QDateTime(opt_tm, QTime(0, 0), QtUTC); foreach (Waypoint* wpt, track->waypoint_list) { diff --git a/reference/ricoh-rdc5300.jpg.jpg b/reference/ricoh-rdc5300.jpg.jpg index 9aa1027cd..feda00cbd 100644 Binary files a/reference/ricoh-rdc5300.jpg.jpg and b/reference/ricoh-rdc5300.jpg.jpg differ diff --git a/reference/ricoh-rdc5300_offset.csv b/reference/ricoh-rdc5300_offset.csv new file mode 100644 index 000000000..abac49f4e --- /dev/null +++ b/reference/ricoh-rdc5300_offset.csv @@ -0,0 +1,2 @@ +No,Latitude,Longitude,Name,Speed,utc_d,utc_t +1,44.315150,15.265690,"ricoh-rdc5300",0.00,2000/05/31,22:50:40 diff --git a/reference/ricoh-rdc5300_offsettime.jpg b/reference/ricoh-rdc5300_offsettime.jpg new file mode 100644 index 000000000..dc46e1392 Binary files /dev/null and b/reference/ricoh-rdc5300_offsettime.jpg differ diff --git a/reference/ricoh-rdc5300_offsettime.jpg.jpg b/reference/ricoh-rdc5300_offsettime.jpg.jpg new file mode 100644 index 000000000..07aefa110 Binary files /dev/null and b/reference/ricoh-rdc5300_offsettime.jpg.jpg differ diff --git a/skytraq.cc b/skytraq.cc index f6df657af..2b2721367 100644 --- a/skytraq.cc +++ b/skytraq.cc @@ -534,7 +534,7 @@ SkytraqBase::gpstime_to_qdatetime(int week, int sec) const int override = xstrtoi(opt_gps_utc_offset, nullptr, 10); if (override) { gps_timet -= override; - return QDateTime::fromSecsSinceEpoch(gps_timet, Qt::UTC); + return QDateTime::fromSecsSinceEpoch(gps_timet, QtUTC); } /* leap second compensation: */ @@ -557,7 +557,7 @@ SkytraqBase::gpstime_to_qdatetime(int week, int sec) const // Future: Consult http://maia.usno.navy.mil/ser7/tai-utc.dat // use http://www.stevegs.com/utils/jd_calc/ for Julian to UNIX sec - return QDateTime::fromSecsSinceEpoch(gps_timet, Qt::UTC); + return QDateTime::fromSecsSinceEpoch(gps_timet, QtUTC); } void diff --git a/src/core/datetime.h b/src/core/datetime.h index 579c50089..30962f23b 100644 --- a/src/core/datetime.h +++ b/src/core/datetime.h @@ -29,6 +29,9 @@ #include #include #include +#ifdef LIGHTWEIGHT_TIMEZONES_SUPPORTED +#include +#endif // As this code began in C, we have several hundred places that set and // read creation_time as a time_t. Provide some operator overloads to make @@ -54,7 +57,11 @@ class DateTime : public QDateTime // Qt::LocalTime compared to Qt::UTC on ubuntu bionic. // Note that these conversions can be required if the Qt::TimeSpec is // set to Qt:LocalTime after construction. +#ifdef LIGHTWEIGHT_TIMEZONES_SUPPORTED + DateTime() : QDateTime(QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC)) +#else DateTime() : QDateTime(QDateTime::fromMSecsSinceEpoch(0, Qt::UTC)) +#endif { } diff --git a/subrip.cc b/subrip.cc index ee6116b58..062e690b4 100644 --- a/subrip.cc +++ b/subrip.cc @@ -221,7 +221,7 @@ SubripFormat::wr_init(const QString& fname) fatal(FatalMsg().nospace() << MYNAME ": option gps_time value (" << opt_gpstime << ") is invalid. Expected hhmmss[.sss]"); } } - gps_datetime = QDateTime(gps_date, gps_time, Qt::UTC); + gps_datetime = QDateTime(gps_date, gps_time, QtUTC); } video_offset_ms = 0; diff --git a/testo.d/exif.test b/testo.d/exif.test index 26c39b29b..6cd71360e 100644 --- a/testo.d/exif.test +++ b/testo.d/exif.test @@ -23,6 +23,56 @@ bincompare ${REFERENCE}/kodak-dc210.jpg.jpg ${TMPDIR}/kodak-dc210.jpg.jpg # test a big endian image # image from https://github.com/ianare/exif-samples.git cp ${REFERENCE}/ricoh-rdc5300.jpg ${TMPDIR}/ricoh-rdc5300.jpg -gpsbabel -i unicsv -f ${REFERENCE}/IMG_2065_retag.csv -o exif,name=IMG_2065 -F ${TMPDIR}/ricoh-rdc5300.jpg +gpsbabel -i unicsv -f ${REFERENCE}/ricoh-rdc5300_offset.csv -o exif,name=ricoh-rdc5300 -F ${TMPDIR}/ricoh-rdc5300.jpg bincompare ${REFERENCE}/ricoh-rdc5300.jpg.jpg ${TMPDIR}/ricoh-rdc5300.jpg.jpg +# test offset option. photo has no gps info and no exif offset tags. +# the offset is only required if the current system timezone is differnt from utc. +# cat reference/ricoh-rdc5300_offset.csv +# No,Latitude,Longitude,Name,Speed,utc_d,utc_t +# 1,44.315150,15.265690,"ricoh-rdc5300",0.00,2000/05/31,22:50:40 +# $ exiftool -D -G -time:all -composite:GPSPosition -c "%+.6f" reference/ricoh-rdc5300.jpg +# [File] - File Modification Date/Time : 2024:09:27 07:18:42-06:00 +# [File] - File Access Date/Time : 2024:09:28 07:19:24-06:00 +# [File] - File Inode Change Date/Time : 2024:09:27 07:18:42-06:00 +# [EXIF] 36867 Date/Time Original : 2000:05:31 21:50:40 +# [EXIF] 36868 Create Date : 2000:05:31 21:50:40 +# $ exiftool -D -G -time:all -composite:GPSPosition -c "%+.6f" reference/ricoh-rdc5300.jpg.jpg +# [File] - File Modification Date/Time : 2024:09:29 07:04:04-06:00 +# [File] - File Access Date/Time : 2024:09:29 07:04:06-06:00 +# [File] - File Inode Change Date/Time : 2024:09:29 07:04:04-06:00 +# [EXIF] 36867 Date/Time Original : 2000:05:31 21:50:40 +# [EXIF] 36868 Create Date : 2000:05:31 21:50:40 +# [EXIF] 7 GPS Time Stamp : 22:50:40 +# [EXIF] 29 GPS Date Stamp : 2000:05:31 +# [Composite] - GPS Date/Time : 2000:05:31 22:50:40Z +# [Composite] - GPS Position : +44.315150, +15.265690 +cp ${REFERENCE}/ricoh-rdc5300.jpg ${TMPDIR}/ricoh-rdc5300_offset.jpg +gpsbabel -i unicsv -f ${REFERENCE}/ricoh-rdc5300_offset.csv -o exif,offset="-01:00" -F ${TMPDIR}/ricoh-rdc5300_offset.jpg +bincompare ${REFERENCE}/ricoh-rdc5300.jpg.jpg ${TMPDIR}/ricoh-rdc5300_offset.jpg.jpg + +# prove the option value is handled the same as the OffsetTimeOriginal tag. +# $ exiftool -D -G -time:all -composite:GPSPosition -c "%+.6f" reference/ricoh-rdc5300_offsettime.jpg +# [File] - File Modification Date/Time : 2024:09:29 06:32:54-06:00 +# [File] - File Access Date/Time : 2024:09:29 06:33:20-06:00 +# [File] - File Inode Change Date/Time : 2024:09:29 06:32:54-06:00 +# [EXIF] 36867 Date/Time Original : 2000:05:31 21:50:40 +# [EXIF] 36868 Create Date : 2000:05:31 21:50:40 +# [EXIF] 36881 Offset Time Original : -01:00 +# [Composite] - Date/Time Original : 2000:05:31 21:50:40-01:00 +# $ exiftool -D -G -time:all -composite:GPSPosition -c "%+.6f" reference/ricoh-rdc5300_offsettime.jpg.jpg +# [File] - File Modification Date/Time : 2024:09:29 07:09:20-06:00 +# [File] - File Access Date/Time : 2024:09:29 07:09:53-06:00 +# [File] - File Inode Change Date/Time : 2024:09:29 07:09:20-06:00 +# [EXIF] 36867 Date/Time Original : 2000:05:31 21:50:40 +# [EXIF] 36868 Create Date : 2000:05:31 21:50:40 +# [EXIF] 36881 Offset Time Original : -01:00 +# [EXIF] 7 GPS Time Stamp : 22:50:40 +# [EXIF] 29 GPS Date Stamp : 2000:05:31 +# [Composite] - Date/Time Original : 2000:05:31 21:50:40-01:00 +# [Composite] - GPS Date/Time : 2000:05:31 22:50:40Z +# [Composite] - GPS Position : +44.315150, +15.265690 +cp ${REFERENCE}/ricoh-rdc5300_offsettime.jpg ${TMPDIR}/ricoh-rdc5300_offsettime.jpg +gpsbabel -i unicsv -f ${REFERENCE}/ricoh-rdc5300_offset.csv -o exif -F ${TMPDIR}/ricoh-rdc5300_offsettime.jpg +bincompare ${REFERENCE}/ricoh-rdc5300_offsettime.jpg.jpg ${TMPDIR}/ricoh-rdc5300_offsettime.jpg.jpg + diff --git a/tools/ci_install_qt.sh b/tools/ci_install_qt.sh index 611aeb291..246787f56 100755 --- a/tools/ci_install_qt.sh +++ b/tools/ci_install_qt.sh @@ -19,6 +19,9 @@ remove=( \ debug_info \ qtcharts \ qtdatavis3d \ +qtgraphs \ +qtgrpc \ +qthttpserver \ qtlottie \ qtnetworkauth \ qtquick3d \ @@ -40,6 +43,9 @@ do skip=true fi done + if [[ "$a" == *".debug_information" ]]; then + skip=true + fi if [ $skip == false ]; then mods+=( "$a" ) fi diff --git a/tools/ci_install_windows.sh b/tools/ci_install_windows.sh index 1f2cad96c..7753a2b38 100755 --- a/tools/ci_install_windows.sh +++ b/tools/ci_install_windows.sh @@ -30,6 +30,8 @@ elif [ "${COMPILER}" = "msvc2019_64" ]; then PACKAGE_SUFFIX=win64_msvc2019_64 elif [ "${COMPILER}" = "msvc2019" ]; then PACKAGE_SUFFIX=win32_msvc2019 +elif [ "${COMPILER}" = "msvc2022_64" ]; then + PACKAGE_SUFFIX=win64_msvc2022_64 else echo "ERROR: unrecognized Qt compiler ${COMPILER}." >&2 exit 1 diff --git a/tools/ci_script_osx.sh b/tools/ci_script_osx.sh index 7222bc86d..a59d49890 100755 --- a/tools/ci_script_osx.sh +++ b/tools/ci_script_osx.sh @@ -17,7 +17,10 @@ if [ $# -ge 3 ]; then GENERATOR[1]=$3 fi fi -if version_ge "${QTVER}" 6.5.0; then +if version_ge "${QTVER}" 6.8.0; then + DEPLOY_TARGET="12.0" + ARCHS="x86_64;arm64" +elif version_ge "${QTVER}" 6.5.0; then DEPLOY_TARGET="11.0" ARCHS="x86_64;arm64" elif version_ge "${QTVER}" 6.0.0; then diff --git a/trackfilter.cc b/trackfilter.cc index 06f7c8bf3..835cd6099 100644 --- a/trackfilter.cc +++ b/trackfilter.cc @@ -41,6 +41,9 @@ static constexpr bool TRACKF_DBG = false; #include // for QRegularExpression, QRegularExpression::CaseInsensitiveOption, QRegularExpression::PatternOptions #include // for QRegularExpressionMatch #include // for QString +#ifdef LIGHTWEIGHT_TIMEZONES_SUPPORTED +#include // for QTimeZone +#endif #include // for UTC, CaseInsensitive #include // for foreach, qPrintable, QAddConst<>::Type, qint64 @@ -298,7 +301,7 @@ void TrackFilter::trackfilter_title() fatal(MYNAME "-title: Missing your title!\n"); } for (auto* track : std::as_const(track_list)) { - trackfilter_pack_init_rte_name(track, QDateTime::fromMSecsSinceEpoch(0, Qt::UTC)); + trackfilter_pack_init_rte_name(track, QDateTime::fromMSecsSinceEpoch(0, QtUTC)); } } @@ -676,7 +679,11 @@ QDateTime TrackFilter::trackfilter_range_check(const char* timestr) if (match.hasMatch()) { // QTime::fromString zzz expects exactly 3 digits representing milliseconds. result = QDateTime::fromString(match.captured(0), u"yyyyMMddHHmmss.zzz"); +#ifdef LIGHTWEIGHT_TIMEZONES_SUPPORTED + result.setTimeZone(QTimeZone::UTC); +#else result.setTimeSpec(Qt::UTC); +#endif if (!result.isValid()) { fatal(MYNAME "-range-check: Invalid timestamp \"%s\"!\n", timestr); } @@ -834,7 +841,11 @@ TrackFilter::faketime_t TrackFilter::trackfilter_faketime_check(const char* time QString fmtstart("00000101000000"); fmtstart.replace(0, start.size(), start); result.start = QDateTime::fromString(fmtstart, u"yyyyMMddHHmmss"); +#ifdef LIGHTWEIGHT_TIMEZONES_SUPPORTED + result.start.setTimeZone(QTimeZone::UTC); +#else result.start.setTimeSpec(Qt::UTC); +#endif if (!result.start.isValid()) { fatal(MYNAME "-faketime-check: Invalid timestamp \"%s\"!\n", qPrintable(start)); } diff --git a/util.cc b/util.cc index 53e3b983f..bf54ad498 100644 --- a/util.cc +++ b/util.cc @@ -294,6 +294,31 @@ QDateTime make_datetime(QDate date, QTime time, bool is_localtime, bool force_utc, int utc_offset) { QDateTime result; +#ifdef LIGHTWEIGHT_TIMEZONES_SUPPORTED + QTimeZone timezone; + + if (is_localtime) { + if (force_utc) { // override with passed option value + timezone = QTimeZone::fromSecondsAheadOfUtc(utc_offset); + } else { + timezone = QTimeZone::LocalTime; + } + } else { + timezone = QTimeZone::UTC; + } + + if (date.isValid() && time.isValid()) { + result = QDateTime(date, time, timezone); + } else if (time.isValid()) { + // TODO: Wouldn't it be better to return an invalid QDateTime + // that contained an invalid QDate, a valid QTime and a valid + // Qt::TimeSpec? + result = QDateTime(QDate(1970, 1, 1), time, timezone); + } else if (date.isValid()) { + // no time, use start of day in the given Qt::TimeSpec. + result = date.startOfDay(timezone); + } +#else Qt::TimeSpec timespec; int offset = 0; @@ -326,6 +351,7 @@ make_datetime(QDate date, QTime time, bool is_localtime, bool force_utc, int utc result = date.startOfDay(timespec, offset); } +#endif return result; } @@ -347,7 +373,7 @@ gpsbabel::DateTime current_time() { if (gpsbabel_testmode()) { - return QDateTime::fromMSecsSinceEpoch(0, Qt::UTC); + return QDateTime::fromMSecsSinceEpoch(0, QtUTC); } return QDateTime::currentDateTimeUtc(); @@ -361,14 +387,14 @@ current_time() */ QDateTime dotnet_time_to_qdatetime(long long dotnet) { - QDateTime epoch = QDateTime(QDate(1, 1, 1), QTime(0, 0, 0), Qt::UTC); + QDateTime epoch = QDateTime(QDate(1, 1, 1), QTime(0, 0, 0), QtUTC); qint64 millisecs = (dotnet + 5000)/ 10000; return epoch.addMSecs(millisecs); } long long qdatetime_to_dotnet_time(const QDateTime& dt) { - QDateTime epoch = QDateTime(QDate(1, 1, 1), QTime(0, 0, 0), Qt::UTC); + QDateTime epoch = QDateTime(QDate(1, 1, 1), QTime(0, 0, 0), QtUTC); qint64 millisecs = epoch.msecsTo(dt); return millisecs * 10000; } diff --git a/v900.cc b/v900.cc index 4b8fb942e..baf5a326b 100644 --- a/v900.cc +++ b/v900.cc @@ -143,7 +143,7 @@ V900Format::bintime2utc(int date, int time) { // What's left in 'date' is year. QDate dt(date + 2000, month, day); - return QDateTime(dt, tm, Qt::UTC); + return QDateTime(dt, tm, QtUTC); } void