Skip to content

fix: resolve 'now' token in Joi.date().default() consistently with comparison rules - #3117

Open
sapirbaruch wants to merge 4 commits into
hapijs:masterfrom
sapirbaruch:fix/date-default-now-token
Open

fix: resolve 'now' token in Joi.date().default() consistently with comparison rules#3117
sapirbaruch wants to merge 4 commits into
hapijs:masterfrom
sapirbaruch:fix/date-default-now-token

Conversation

@sapirbaruch

Copy link
Copy Markdown

Ran into an unexpected inconsistency while using Joi.date().default('now') — the 'now' token that works as a dynamic "current time" reference in .min('now'), .max('now'), .greater('now'), and .less('now') is treated as a plain string literal when used as a default.

const schema = Joi.date().default('now');
schema.validate(undefined).value; // 'now'  (expected: current Date)

The root cause: defaults are applied in internals.finalize after coercion, and a string default is returned as-is by internals.default. The comparison rules work around this by storing 'now' as-is and resolving it dynamically at validation time, but the default path has no such special handling.

The fix intercepts default('now') in an overrides.default method on the date type — the same pattern used by keys.js for deepDefault — and converts it to a function () => new Date(). Since internals.default already calls function-typed defaults at validation time, this makes 'now' resolve dynamically per-validation-call, consistent with the comparison rules.

All 1802 existing tests pass.

Fixes #3112.

@AbdelrahmanHafez AbdelrahmanHafez left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would probably be nice to add some test to prevent regressions

@AbdelrahmanHafez AbdelrahmanHafez left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One nitpick about an extra Date.now() in runtime, otherwise LGTM. Thanks 👍

Comment thread lib/types/date.js Outdated
// treated in the min/max/greater/less rules.

if (value === 'now') {
return this.$_parent('default', () => new Date(Date.now()), options);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return this.$_parent('default', () => new Date(Date.now()), options);
return this.$_parent('default', () => new Date(), options);

No reason to use Date.now() with new Date()

Edit: Turns out this is a workaround for testing, probably better to find a way to mock the constructor just for those tests that need it.

@sapirbaruch

Copy link
Copy Markdown
Author

Hey @AbdelrahmanHafez — just pushed an update. The test suite now has 4 cases covering the default 'now' behavior (resolves to Date, doesn't touch non-undefined input, doesn't touch non-now string defaults, doesn't touch function defaults). All 1806 tests pass at 100% coverage.

Regarding the overlap with #3122 — I noticed it opened after this one. Our approach is identical; happy to close this in favor of that PR if you'd prefer to keep things tidy, or to merge whichever you think is ready.

@AbdelrahmanHafez

Copy link
Copy Markdown
Contributor

Thanks @sapirbaruch. Just to clarify, I'm not a maintainer in this repo, there's a discussion in #3112 on whether or not this is the desired behavior. It's up to @Marsup to decide whether or not 'now' should resolve to the current date, and whether or not to merge this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Joi.date().default('now') does not produce current time

2 participants