From 647ff9fd0b6a908e123d41620aa72ddf406275a2 Mon Sep 17 00:00:00 2001 From: Joachim Spange Date: Wed, 29 Jan 2025 13:59:40 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Parse=20date=20and=20time=20(mmddyy?= =?UTF-8?q?=20and=20hhmmss.sss)=20format=20to=20UTC=20DateTime(fullYear,?= =?UTF-8?q?=20month,=20day,=20hours,=20minutes,=20seconds,=20milliseconds,?= =?UTF-8?q?=20DateTimeKind.Utc)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/NmeaParser/Nmea/NmeaMessage.cs | 62 ++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/NmeaParser/Nmea/NmeaMessage.cs b/src/NmeaParser/Nmea/NmeaMessage.cs index 6566bca..a1cd9f2 100644 --- a/src/NmeaParser/Nmea/NmeaMessage.cs +++ b/src/NmeaParser/Nmea/NmeaMessage.cs @@ -309,6 +309,68 @@ namespace NmeaParser.Messages return TimeSpan.Zero; } + /// + /// Parse from mmddyy + hhmmss.ss to DateTime (used in e.g. Ggk) + /// https://receiverhelp.trimble.com/alloy-gnss/en-us/NMEA-0183messages_PTNL_GGK.html + /// + internal static DateTime? StringsToUtcDateTime(string dateField, string timeField) + { + if (string.IsNullOrWhiteSpace(dateField) || string.IsNullOrWhiteSpace(timeField)) + return null; + + // 2. Parse date (mmddyy) + // - Typically the year is 2 digits; you must decide how to handle the century. + // - For example, if year < 80 => 20xx; else => 19xx + if (dateField.Length < 6) return null; + if (!int.TryParse(dateField.Substring(0, 2), out int month)) return null; // mm + if (!int.TryParse(dateField.Substring(2, 2), out int day)) return null; // dd + if (!int.TryParse(dateField.Substring(4, 2), out int year)) return null; // yy + + int fullYear = (year < 80) ? (2000 + year) : (1900 + year); + + // 3. Parse time (hhmmss.ss) + // - The fractional seconds might be optional or might have variable length. + // - Parse hours, minutes, and then the part after the decimal. + // - Time can be "hhmmss", or "hhmmss.sss", etc. + // Example: "102939.00" => hh=10, mm=29, ss=39, fraction=0 + if (timeField.Length < 6) return null; + if (!int.TryParse(timeField.Substring(0, 2), out int hours)) return null; // hh + if (!int.TryParse(timeField.Substring(2, 2), out int minutes)) return null; // mm + + // The seconds part can have a decimal fraction + // So we split around the decimal point: + double secondsDouble; + if (timeField.Length > 4) + { + string secString = timeField.Substring(4); // "39.00 in example above" + if (!double.TryParse(secString, + System.Globalization.NumberStyles.Float, + System.Globalization.CultureInfo.InvariantCulture, + out secondsDouble)) + { + return null; + } + } + else + { + secondsDouble = 0.0; + } + int seconds = (int)secondsDouble; + int milliseconds = (int)((secondsDouble - seconds) * 1000.0); + + // 4. Construct the UTC DateTime + try + { + var result = new DateTime(fullYear, month, day, hours, minutes, seconds, milliseconds, DateTimeKind.Utc); + return result; + } + catch + { + // If the date/time is invalid (e.g. month=13, day=32), we return null or handle differently + return null; + } + } + /// /// Indicates whether the current object is equal to another object of the same type. ///