peass-ng/PEASS-ng · error · ArgumentException

DateTime value may not be before the epoch

Error message

DateTime value may not be before the epoch

What it means

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).

Source

Thrown at winPEAS/winPEASexe/winPEAS/3rdParty/BouncyCastle/util/date/DateTimeUtilities.cs:23

	public class DateTimeUtilities
	{
		public static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1);

		private DateTimeUtilities()
		{
		}

		/// <summary>
		/// Return the number of milliseconds since the Unix epoch (1 Jan., 1970 UTC) for a given DateTime value.
		/// </summary>
		/// <param name="dateTime">A UTC DateTime value not before epoch.</param>
		/// <returns>Number of whole milliseconds after epoch.</returns>
		/// <exception cref="ArgumentException">'dateTime' is before epoch.</exception>
		public static long DateTimeToUnixMs(
			DateTime dateTime)
		{
			if (dateTime.CompareTo(UnixEpoch) < 0)
				throw new ArgumentException("DateTime value may not be before the epoch", "dateTime");

			return (dateTime.Ticks - UnixEpoch.Ticks) / TimeSpan.TicksPerMillisecond;
		}

		/// <summary>
		/// Create a DateTime value from the number of milliseconds since the Unix epoch (1 Jan., 1970 UTC).
		/// </summary>
		/// <param name="unixMs">Number of milliseconds since the epoch.</param>
		/// <returns>A UTC DateTime value</returns>
		public static DateTime UnixMsToDateTime(
			long unixMs)
		{
			return new DateTime(unixMs * TimeSpan.TicksPerMillisecond + UnixEpoch.Ticks);
		}

		/// <summary>
		/// Return the current number of milliseconds since the Unix epoch (1 Jan., 1970 UTC).
		/// </summary>

View on GitHub (pinned to 53fb989abc)

Solutions

  1. 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.
  2. Fix the data source so uninitialized/missing dates are not passed through (use nullable DateTime or a sentinel check).
  3. Check DateTimeKind and normalize to UTC before comparing, since a Kind mismatch can push a valid boundary value below the epoch.
  4. Catch ArgumentException around DateTimeToUnixMs and fall back to epoch (0 ms) or surface a user-facing validation error.

Example fix

// before
long ms = DateTimeUtilities.DateTimeToUnixMs(record.CreatedAt); // record.CreatedAt may be default(DateTime)
// after
var epoch = new DateTime(1970, 1, 1);
var dt = record.CreatedAt == default ? epoch : record.CreatedAt.ToUniversalTime();
if (dt < epoch) dt = epoch;
long ms = DateTimeUtilities.DateTimeToUnixMs(dt);
Defensive patterns

Strategy: validation

Validate before calling

private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

if (dateTime == default || dateTime < UnixEpoch)
    dateTime = UnixEpoch; // clamp, or reject with your own validation error
long ms = Org.BouncyCastle.Utilities.Date.DateTimeUtilities.DateTimeToUnixMs(dateTime.ToUniversalTime());

Type guard

static bool IsAfterEpoch(DateTime dt)
{
    var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    return dt != default && dt.ToUniversalTime().CompareTo(epoch) >= 0;
}

Try / catch

try
{
    long ms = DateTimeUtilities.DateTimeToUnixMs(dateTime);
}
catch (ArgumentException ex) when (ex.Message.Contains("before the epoch"))
{
    long ms = 0; // or log and surface a validation error
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of peass-ng/PEASS-ng@53fb989abc (2026-09-02). Data as JSON: /api/errors/177ecdc957dda4b0. Report an issue: GitHub.