payload icon indicating copy to clipboard operation
payload copied to clipboard

Date fields always include milliseconds even when seconds default to zero

Open geeron opened this issue 9 months ago • 0 comments

Describe the Bug.

When using type: 'date' fields in Payload (e.g. with pickerAppearance: 'timeOnly'), the stored ISO timestamp always contains milliseconds (.323) even if users only select hours/minutes (and seconds default to 00). In many scenarios this millisecond precision is unnecessary and can lead to inconsistent comparisons or unexpected behavior when you expect “round” times.

Current Behavior

  • Seconds are correctly set to 00 by default, but milliseconds are not, but probably to the milliseconds at time of saving.
  • There is no built-in way to suppress or zero out the millisecond component at save time - except using a before change hook.

Expected Behavior

  • When the UI picker only provides hours and minutes (and seconds default to zero), the saved ISO string should omit or zero the millisecond fraction.
  • Ideally Payload would either:
    1. Strip the milliseconds automatically and set them to zero, resulting in "2025-05-23T20:00:00Z", or
    2. Expose a field-level config option (e.g. admin.date.trimMilliseconds: true) to control inclusion of fractional seconds.

Workaround

Use a beforeChange hook to manually zero‐out milliseconds:

hooks: {
    beforeChange: [
      async (payload) => {
        const newDate = new Date(payload.data?.startTime)
        newDate.setMilliseconds(0)
        return newDate.toISOString()
      },
    ],
  },

Proposal

Add millisecond-trimming behavior to the core date field or expose a simple config flag:

{
  name: 'startTime',
  type: 'date',
  admin: {
    date: {
      pickerAppearance: 'timeOnly',
      trimMilliseconds: true,   // ← new option
    },
  },
  required: true,
}

This would improve DX (no need for custom hooks) and ensure stored timestamps match the intended precision.

Reproduction Steps

  1. Define a collection with a date field:

    {
      name: 'startTime',
      type: 'date',
      admin: {
        date: { pickerAppearance: 'timeOnly' },
      },
      required: true,
    }
    
  2. In the admin UI, pick “08:00 PM.”

  3. Save the document and inspect the stored value in the database or via the API.

You’ll see something like:

"startTime": "2025-05-23T20:00:00.323Z"

Environment Info

Binaries:
  Node: 18.20.8
  npm: 10.8.2
  Yarn: 1.22.22
  pnpm: 10.8.0
Relevant Packages:
  payload: 3.33.0
  next: 15.3.0
  @payloadcms/db-postgres: 3.33.0
  @payloadcms/email-nodemailer: 3.33.0
  @payloadcms/graphql: 3.33.0
  @payloadcms/next/utilities: 3.33.0
  @payloadcms/payload-cloud: 3.33.0
  @payloadcms/richtext-lexical: 3.33.0
  @payloadcms/translations: 3.33.0
  @payloadcms/ui/shared: 3.33.0
  react: 19.1.0
  react-dom: 19.1.0
Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.6.0: Mon Jul 29 21:14:30 PDT 2024; root:xnu-10063.141.2~1/RELEASE_ARM64_T6000
  Available memory (MB): 32768
  Available CPU cores: 10

geeron avatar May 23 '25 20:05 geeron