const { ObjectId } = require("mongodb");
> new Date(-1)
1969-12-31T23:59:59.999Z
> ObjectId(-1)
Uncaught:
RangeError [ERR_OUT_OF_RANGE]: The value of "value" is out of range. It must be >= 0 and <= 4294967295. Received -1
Why is the possible input range so limited:
> new Date(0)
1970-01-01T00:00:00.000Z
> new Date(4294967294)
1970-02-19T17:02:47.294Z
You seem to have two different questions here. Let's start with the latter which is:
Why is the possible input range so limited:
... new Date(4294967294) 1970-02-19T17:02:47.294Z
The documentation for ObjectId()
states the following regarding the timestamp portion of the object (emphasis added):
A 4-byte timestamp, representing the ObjectId's creation, measured in seconds since the Unix epoch.
Contrast this with the documentation for Date()
which specifies the following for the argument type you are providing to it (emphasis again added):
new Date(<integer>)
specifies the datetime as milliseconds since the UNIX epoch (Jan 1, 1970), and returns the resultingISODate
instance.
Therefore you have a mismatch between your comparison here as it is off by a factor of 1,000. Adjusting for that we have:
> new Date(4294967294 * 1000)
ISODate("2106-02-07T06:28:14.000Z")
There are other questions on the site that talk about this range, e.g. How can mongodb handle ObjectId timestamp beyond Tue, 19 Jan 2038? (which mistakenly believed the value to be signed rather than unsigned).
Now turning to the question in the title:
How to construct an ObjectId from a negative time value
I believe the answer is that you cannot do that, at least not with the current implementation of ObjectIds. We can see the following in the Design Rationale of ObjectID format:
Timestamp: The timestamp is a 32-bit unsigned integer, as it allows us to extend the furthest date that the timestamp can represent from the year 2038 to 2106. There is no reason why MongoDB would generate a timestamp to mean a date before 1970, as MongoDB did not exist back then.
You could, of course, use a different data type for the _id
field if other values are needed for your purposes.