{"record":{"id":"177ecdc957dda4b0","repo":"peass-ng/PEASS-ng","slug":"datetime-value-may-not-be-before-the-epoch","errorCode":null,"errorMessage":"DateTime value may not be before the epoch","messagePattern":"DateTime value may not be before the epoch","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"winPEAS/winPEASexe/winPEAS/3rdParty/BouncyCastle/util/date/DateTimeUtilities.cs","lineNumber":23,"sourceCode":"\tpublic class DateTimeUtilities\n\t{\n\t\tpublic static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1);\n\n\t\tprivate DateTimeUtilities()\n\t\t{\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Return the number of milliseconds since the Unix epoch (1 Jan., 1970 UTC) for a given DateTime value.\n\t\t/// </summary>\n\t\t/// <param name=\"dateTime\">A UTC DateTime value not before epoch.</param>\n\t\t/// <returns>Number of whole milliseconds after epoch.</returns>\n\t\t/// <exception cref=\"ArgumentException\">'dateTime' is before epoch.</exception>\n\t\tpublic static long DateTimeToUnixMs(\n\t\t\tDateTime dateTime)\n\t\t{\n\t\t\tif (dateTime.CompareTo(UnixEpoch) < 0)\n\t\t\t\tthrow new ArgumentException(\"DateTime value may not be before the epoch\", \"dateTime\");\n\n\t\t\treturn (dateTime.Ticks - UnixEpoch.Ticks) / TimeSpan.TicksPerMillisecond;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Create a DateTime value from the number of milliseconds since the Unix epoch (1 Jan., 1970 UTC).\n\t\t/// </summary>\n\t\t/// <param name=\"unixMs\">Number of milliseconds since the epoch.</param>\n\t\t/// <returns>A UTC DateTime value</returns>\n\t\tpublic static DateTime UnixMsToDateTime(\n\t\t\tlong unixMs)\n\t\t{\n\t\t\treturn new DateTime(unixMs * TimeSpan.TicksPerMillisecond + UnixEpoch.Ticks);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Return the current number of milliseconds since the Unix epoch (1 Jan., 1970 UTC).\n\t\t/// </summary>","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/peass-ng/PEASS-ng/blob/53fb989abc2219826385683a6fee826bd6cd38d6/winPEAS/winPEASexe/winPEAS/3rdParty/BouncyCastle/util/date/DateTimeUtilities.cs#L5-L41","documentation":"DateTimeUtilities.DateTimeToUnixMs converts a DateTime to whole milliseconds since the Unix epoch (1 Jan 1970 UTC). It throws ArgumentException with the message 'DateTime value may not be before the epoch' when the input compares earlier than UnixEpoch, because a negative result cannot be represented as a valid 'whole milliseconds after epoch' per the API contract (used e.g. in OCSP/TSP timestamp math).","triggerScenarios":"Passing a DateTime with CompareTo(UnixEpoch) < 0 to DateTimeToUnixMs — i.e. any date before 1970-01-01 00:00:00 UTC, including DateTime.MinValue, zero-initialized DateTime fields, or default(DateTime) (0001-01-01).","commonSituations":"Uninitialized/default DateTime structs flowing into certificate/timestamp validation code; parsing legacy dates stored as 0 or negative values; timezone conversion mistakes that shift a value near the epoch boundary below the epoch; deserializing missing date fields as DateTime.MinValue.","solutions":["Validate the input before calling: reject or clamp any dateTime earlier than new DateTime(1970,1,1) (BouncyCastle's UnixEpoch) to zero or to the epoch itself.","Fix the data source so uninitialized/missing dates are not passed through (use nullable DateTime or a sentinel check).","Check DateTimeKind and normalize to UTC before comparing, since a Kind mismatch can push a valid boundary value below the epoch.","Catch ArgumentException around DateTimeToUnixMs and fall back to epoch (0 ms) or surface a user-facing validation error."],"exampleFix":"// before\nlong ms = DateTimeUtilities.DateTimeToUnixMs(record.CreatedAt); // record.CreatedAt may be default(DateTime)\n// after\nvar epoch = new DateTime(1970, 1, 1);\nvar dt = record.CreatedAt == default ? epoch : record.CreatedAt.ToUniversalTime();\nif (dt < epoch) dt = epoch;\nlong ms = DateTimeUtilities.DateTimeToUnixMs(dt);","handlingStrategy":"validation","validationCode":"private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);\n\nif (dateTime == default || dateTime < UnixEpoch)\n    dateTime = UnixEpoch; // clamp, or reject with your own validation error\nlong ms = Org.BouncyCastle.Utilities.Date.DateTimeUtilities.DateTimeToUnixMs(dateTime.ToUniversalTime());","typeGuard":"static bool IsAfterEpoch(DateTime dt)\n{\n    var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);\n    return dt != default && dt.ToUniversalTime().CompareTo(epoch) >= 0;\n}","tryCatchPattern":"try\n{\n    long ms = DateTimeUtilities.DateTimeToUnixMs(dateTime);\n}\ncatch (ArgumentException ex) when (ex.Message.Contains(\"before the epoch\"))\n{\n    long ms = 0; // or log and surface a validation error\n}","preventionTips":["Normalize all dates to UTC and validate >= 1970-01-01 before timestamp math.","Never pass default(DateTime) or DateTime.MinValue into epoch conversions.","Use DateTime? (nullable) to distinguish missing dates from real values.","Check DateTimeKind and call ToUniversalTime() to avoid boundary shifts.","Add boundary unit tests for dates at and just before the epoch."],"tags":["dotnet","bouncycastle","argument-exception","datetime","epoch"],"backgroundTag":"datetime-before-epoch","analyzedSha":"53fb989abc2219826385683a6fee826bd6cd38d6","analyzedAt":"2026-09-02T04:25:09.259Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T11:17:12.671Z"}