MongoDB $dateFromParts

In MongoDB, the $dateFromParts aggregation pipeline operator constructs and returns a Date object from the date’s constituent parts.

You provide each date part as a separate field.

You can specify your constituent date fields in ISO week date format if required.

Example

Suppose we have a collection called dateParts with the following document:

{
	"_id" : 1,
	"year" : 2020,
	"month" : 12,
	"day" : 31,
	"hour" : 23,
	"minute" : 30,
	"second" : 25,
	"millisecond" : 123
}

The document contains a different field for each date part.

We can run the following code to return a date object from the fields in those documents.

db.dateParts.aggregate([
{
   $project: {
      date: {
         $dateFromParts: {
            "year": "$year", 
            "month": "$month", 
            "day": "$day", 
            "hour": "$hour", 
            "minute": "$minute", 
            "second": "$second", 
            "millisecond": "$millisecond"
         }
      }
   }
}])

Result:

{ "_id" : 1, "date" : ISODate("2020-12-31T23:30:25.123Z") }

All date/time parts have been converted to a single date object.

Timezones

You can use the timezone field to specify a timezone.

The timezone can be specified using either the Olson timezone identifier (e.g. "Europe/London", "GMT") or the UTC offset (e.g. "+02:30", "-1030").

Olson Timezone Identifier

Here’s an example that uses Olson timezone IDs to output three different dates from a single document, based on three different timezones.

db.dateParts.aggregate([
{
   $project: {
      dateUTC: {
         $dateFromParts: {
            "year": "$year", 
            "month": "$month", 
            "day": "$day", 
            "hour": "$hour", 
            "minute": "$minute", 
            "second": "$second", 
            "millisecond": "$millisecond",
            "timezone": "Pacific/Auckland"
         }
      },
      dateHonolulu: {
         $dateFromParts: {
            "year": "$year", 
            "month": "$month", 
            "day": "$day", 
            "hour": "$hour", 
            "minute": "$minute", 
            "second": "$second", 
            "millisecond": "$millisecond",
            "timezone": "Pacific/Honolulu"
         }
      },
      dateAuckland: {
         $dateFromParts: {
            "year": "$year", 
            "month": "$month", 
            "day": "$day", 
            "hour": "$hour", 
            "minute": "$minute", 
            "second": "$second", 
            "millisecond": "$millisecond",
            "timezone": "Pacific/Auckland"
         }
      }
   }
}]).pretty()

Result:

{
	"_id" : 1,
	"dateUTC" : ISODate("2020-12-31T10:30:25.123Z"),
	"dateHonolulu" : ISODate("2021-01-01T09:30:25.123Z"),
	"dateAuckland" : ISODate("2020-12-31T10:30:25.123Z")
}

UTC Offset

Here’s an example that uses the UTC offset.

db.dateParts.aggregate([
{
   $project: {
      "date+00:00": {
         $dateFromParts: {
            "year": "$year", 
            "month": "$month", 
            "day": "$day", 
            "hour": "$hour", 
            "minute": "$minute", 
            "second": "$second", 
            "millisecond": "$millisecond",
            "timezone": "+00:00"
         }
      },
      "date-10:00": {
         $dateFromParts: {
            "year": "$year", 
            "month": "$month", 
            "day": "$day", 
            "hour": "$hour", 
            "minute": "$minute", 
            "second": "$second", 
            "millisecond": "$millisecond",
            "timezone": "-10:00"
         }
      },
      "date+12:00": {
         $dateFromParts: {
            "year": "$year", 
            "month": "$month", 
            "day": "$day", 
            "hour": "$hour", 
            "minute": "$minute", 
            "second": "$second", 
            "millisecond": "$millisecond",
            "timezone": "+12:00"
         }
      }
   }
}]).pretty()

Result:

{
	"_id" : 1,
	"date+00:00" : ISODate("2020-12-31T23:30:25.123Z"),
	"date-10:00" : ISODate("2021-01-01T09:30:25.123Z"),
	"date+12:00" : ISODate("2020-12-31T11:30:25.123Z")
}

ISO Week Date Format

The date parts can be specified using the ISO 8601 format if required.

In particular, you can use:

Format SpecifierOutput
isoWeekYearYear in ISO 8601 format. This field is required if not using year (and year is required if not using isoWeekYear).
isoWeek Week of the year in ISO 8601 format. Can only be used with isoWeekYear.
isoDayOfWeekDay of week (1-Monday, 7-Sunday). Can only be used with isoWeekYear.

Suppose we insert a second document that looks like this:

{
	"_id" : 2,
	"isoWeekYear" : 2021,
	"isoWeek" : 32,
	"isoDayOfWeek" : 7,
	"hour" : 23,
	"minute" : 30,
	"second" : 25,
	"millisecond" : 123,
	"timezone" : "UTC"
}

We can see that it uses isoWeekYear, isoWeek, and isoDayOfWeek instead of year, month, and day (which is what the first document uses).

We can use the following code to construct a Date object from this document:

db.dateParts.aggregate([
  { $match: { _id: 2} },
  {
    $project: {
        date: {
          $dateFromParts: {
              "isoWeekYear": "$isoWeekYear", 
              "isoWeek": "$isoWeek", 
              "isoDayOfWeek": "$isoDayOfWeek", 
              "hour": "$hour", 
              "minute": "$minute", 
              "second": "$second", 
              "millisecond": "$millisecond", 
              "timezone": "$timezone"
          }
        }
    }
  }
])

Result:

{ "_id" : 2, "date" : ISODate("2021-08-15T23:30:25.123Z") } 

Out of Range Fields

Starting in MongoDB 4.4, the supported value range for year and isoWeekYear is 1-9999. In prior versions, the lower bound for these values was 0 and the supported value range was 0-9999.

Starting in MongoDB 4.0, if the value specified for fields other than yearisoWeekYear, and timezone is outside the valid range, the $dateFromParts operator carries or subtracts the difference from other date parts to calculate the date.

Values Higher than the Range

Suppose we add the following document to our collection:

{
	"_id" : 3,
	"year" : 2020,
	"month" : 14,
	"day" : 65,
	"hour" : 48,
	"minute" : 130,
	"second" : 625,
	"millisecond" : 123
}

Many of the date and time fields in this document are higher than their respective valid ranges.

Let’s run the following command to convert it into a Date object:

db.dateParts.aggregate([
  { $match: { _id: 3} },
  {
    $project: {
        date: {
          $dateFromParts: {
              "year": "$year", 
              "month": "$month", 
              "day": "$day", 
              "hour": "$hour", 
              "minute": "$minute", 
              "second": "$second", 
              "millisecond": "$millisecond"
          }
        }
    }
  }
])

Result:

{ "_id" : 3, "date" : ISODate("2021-04-08T02:20:25.123Z") }

We can see that the date parts in the resulting Date object are different to their respective date parts in the document. This is because $dateFromParts recalculated the date to account for the date part values that exceeded their normal range.

Values Lower than the Range

Suppose we add the following document to our collection:

{
	"_id" : 4,
	"year" : 2020,
	"month" : 0,
	"day" : 0,
	"hour" : 0,
	"minute" : 0,
	"second" : 0,
	"millisecond" : 0
}

Many of the date and time fields in this document are lower than their respective valid ranges.

Let’s run the following command to convert it into a Date object:

db.dateParts.aggregate([
  { $match: { _id: 4} },
  {
    $project: {
        date: {
          $dateFromParts: {
              "year": "$year", 
              "month": "$month", 
              "day": "$day", 
              "hour": "$hour", 
              "minute": "$minute", 
              "second": "$second", 
              "millisecond": "$millisecond"
          }
        }
    }
  }
])

Result:

{ "_id" : 4, "date" : ISODate("2019-11-30T00:00:00Z") }