Skip to content

feat: add :date-range property type with year/month/day precision + ranges#12643

Open
ricardo37 wants to merge 11 commits into
logseq:masterfrom
ricardo37:feat/date-range-property
Open

feat: add :date-range property type with year/month/day precision + ranges#12643
ricardo37 wants to merge 11 commits into
logseq:masterfrom
ricardo37:feat/date-range-property

Conversation

@ricardo37
Copy link
Copy Markdown

Summary

Adds a new :date-range property type that lets users specify dates at a range of granularities and optionally with an end date for the chosen level of granularity.

  • Year — "2026"
  • Month — "May 2026"
  • Day — "May 13, 2026" (existing :date behaviour, but now accessible via the new type too)
  • Ranges — any combination: "May 13, 2026 – May 20, 2026" or "1715 – 1720"

The existing :date and :datetime types are unchanged (though there is no reason the proposed new property couldn't eventually just replace the existing :date property).

Storage

Each value is a lightweight DataScript entity (no :block/uuid) with three new schema attributes:

Attribute Type Meaning
:logseq.property.date/precision keyword :year | :month | :day
:logseq.property.date/start YYYYMMDD integer start of range (or single point)
:logseq.property.date/end YYYYMMDD integer end of range — absent for single-point values

The entity type is identified in malli_schema/entity-dispatch-key by the presence of :logseq.property.date/precision, and validated by the new date-range-value malli schema.

Files changed

File Change
deps/db/.../schema.cljs 3 new DataScript attributes
deps/db/.../property.cljs "logseq.property.date" namespace registered
deps/db/.../property/type.cljs :date-range in 6 sets/maps
deps/db/.../malli_schema.cljs date-range-value schema + dispatch key
src/.../date_range_picker.cljs New — picker component
src/.../property/value.cljs :date-range display + entity creation
src/.../property/value.css CSS selectors extended
src/.../property.cljs icon, layout, hide-key-label
src/.../property/config.cljs property-type-label case
src/resources/dicts/en.edn "Date range" i18n string

UI

The new picker (date-range-picker-inner) provides:

  • Precision chips — [Year] [Month] [Day] — switching coerces the existing value
  • Year picker — ‹ 2026 › with prev/next buttons
  • Month grid — 4×3 grid of months with year navigation (shown for :month precision)
  • Day calendar — delegates to the existing ui/nlp-calendar (shown for :day)
  • "Add end" / "Single date" toggle — converts between point and range
  • Delete button — removes the value

Test plan

  • Create a user property with type "Date range"
  • Set a year-only value (e.g. 1715); confirm display shows "1715"
  • Set a month value (e.g. May 2026); confirm display shows "May 2026"
  • Set a day value; confirm display shows "May 13, 2026"
  • Click "Add end" and set an end date; confirm range display "May 13 – May 20, 2026"
  • Switch precision from Day to Year; confirm both start and end are coerced
  • Delete the value; confirm property reverts to empty state
  • Run logseq graph validate on a graph with :date-range properties
  • Confirm existing :date and :datetime properties are unaffected
  • Confirm the malli assert in property/type.cljs still passes (ClojureScript compile succeeds)

🤖 Generated with Claude Code

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 14, 2026

CLA assistant check
All committers have signed the CLA.

@tiensonqin
Copy link
Copy Markdown
Contributor

@ricardo37 :date-range looks great, can you add tests and maybe record a video on this feature?

@TheSnakePolyglot
Copy link
Copy Markdown
Contributor

I see that you can add not only a range but also individual dates, like select the year "2021" or the month "January 2021". This is awesome.
I wonder if we can combine this with how the existing #Journal pages work, which represent single Days. It would be really cool if this created new pages for Years and Months. So clicking on the year "2021" would create that page with the tag #Year, and the same for the #Months.
Furthermore, the Month pages would be inside the Year pages (either via links or by a Namespace), and the Journal pages would be inside the Month ones. So, the Journal page "Jan 1st 2021" would be inside the "Jan 2021" page, which would be inside the "2021" page. This is how Workflowy and Tana do it.
And last but not least, this has been a much requested feature, see https://discuss.logseq.com/t/monthly-journal-pages/28106, https://discuss.logseq.com/t/is-there-an-easy-way-to-have-weekly-or-monthly-journals/1863 , https://www.reddit.com/r/logseq/comments/16lzd7f/weekly_journals/ , https://discord.com/channels/725182569297215569/1286605001220689952 , https://discord.com/channels/725182569297215569/1351634937824022548

@ricardo37
Copy link
Copy Markdown
Author

I'm glad the proposed change would prove useful for other purposes too. I hadn't actually compiled the code to see if it works; I just wanted to get the basic idea out there. But I see now the existing code falls at the very first hurdle. In any case, I've started working on the video and tests and will make them available as soon as I can.

@ricardo37
Copy link
Copy Markdown
Author

Here is a short video demonstrating some of the main features of the PR.

daterange-demo.mp4

ricardo37 and others added 2 commits May 17, 2026 10:54
… range support

A new :date-range property type lets users attach temporal metadata at
varying granularities — a full year ("2026"), a month ("May 2026"), or a
specific day ("May 13, 2026") — and optionally pair any two of those into
a start/end range.

Storage
-------
Each value is a lightweight DataScript entity (no :block/uuid) with three
new schema attributes:
  :logseq.property.date/precision  — keyword :year | :month | :day
  :logseq.property.date/start      — YYYYMMDD integer
  :logseq.property.date/end        — YYYYMMDD integer (absent for single-point dates)

The entity type is identified in malli_schema/entity-dispatch-key by the
presence of :logseq.property.date/precision, and validated by the new
date-range-value malli schema.

Type system changes
-------------------
  • :date-range added to user-built-in-property-types (after :date in UI order)
  • :date-range added to cardinality-property-types (supports :many)
  • :date-range added to user-ref-property-types (value stored as DB ref)
  • date-range? predicate added to built-in-validation-schemas
  • :date-range added to property-types-with-db (validation needs DB)
  • "logseq.property.date" added to logseq-property-namespaces

UI
--
  • New frontend.components.date-range-picker namespace with:
      - format-label  — human-readable display string
      - year-picker   — ‹ year › navigation
      - month-grid    — 4×3 month grid with year navigation
      - day-calendar  — delegates to existing ui/nlp-calendar
      - endpoint-picker  — single-date picker (switches on precision)
      - date-range-picker-inner — full popup with precision chips and
                                  "Add end" / "Single date" toggle
  • date-range-picker-trigger wired into property-value-date-picker
  • <find-or-create-date-range-entity! handles entity dedup/creation
  • property.cljs and value.css updated to treat :date-range like :date
    (inline layout, cursor-pointer, calendar-range icon)
  • en.edn: :property/type-date-range "Date range"
  • property/config.cljs: property-type-label case for :date-range

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add :daterange to property type system (malli schema, type sets, view sorting)
- New date-range picker component: year/month/day precision, optional end date,
  free-text input with multi-format parser, space-hyphen trigger for range mode
- Tests: type-set membership, DB validation, value-entity validation,
  sort behaviour (year/month/day precision, range values, nil handling)
@ricardo37 ricardo37 force-pushed the feat/date-range-property branch from fdca492 to 00e2c73 Compare May 17, 2026 17:01
Year-precision start values were stored as yyyy0101 and month-precision
as yyyymm01, requiring the sort code to compensate. Store them directly
in the canonical form (yyyy0000, yyyymm00) so sort keys equal stored
values, and simplify view.cljs sort-val accordingly. Update all tests
to match the new storage format.
@scheinriese
Copy link
Copy Markdown
Contributor

Cool proof of concept but from a UX perspective (imagining a non technical user) i would like to see Date, DateTime and DateRange unified in one date picker UI… 🤔

@ricardo37
Copy link
Copy Markdown
Author

Cool proof of concept but from a UX perspective (imagining a non technical user) i would like to see Date, DateTime and DateRange unified in one date picker UI… 🤔

I entirely agree. I've proposed this approach only because I thought it might get a quicker implementation than one that involved altering the two existing date-related types.

@scheinriese
Copy link
Copy Markdown
Contributor

scheinriese commented May 24, 2026

I thought it might get a quicker implementation

Makes a lot of sense!

From a design perspective i have another suggestion: it would be great if the different states of the picker wouldn't change width (so switching between Year | Month | Day). Height changes i think are tolerable, but a consistent width makes it more easy on the eyes.

@ricardo37
Copy link
Copy Markdown
Author

@scheinriese Good advice; I'll work on implementing that change.

- Add precision-from-int helper; derive display format from the integer
  encoding (yyyy0000/yyyymm00/yyyymmdd) so :precision key is no longer
  required by format-label
- Replace single ::precision atom with ::start-precision / ::end-precision
  so start and end dates can have independent Year/Month/Day precision;
  each endpoint column now has its own precision-button row
- set-start-precision! / set-end-precision! adjust the draft integer to
  the new precision and update the text box immediately (fix for month
  switch from year: use (max m 1) to avoid collapsing to yyyy0000 format)
- endpoint-picker uses fixed height: 330px (not min-height) so the
  pop-over never resizes when switching precision or toggling range mode
- Start label kept in DOM via visibility:hidden when not in range mode,
  preventing height drop on "Remove end"
- Live-parse on-change: fix prev-text sentinel so clearing the box when
  the picker opens onto an existing range correctly collapses range mode;
  restore " -" → show end calendar behaviour (blank p2 no longer hides it)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@ricardo37 ricardo37 force-pushed the feat/date-range-property branch from 727b6af to 8c6847b Compare May 25, 2026 15:10
@ricardo37
Copy link
Copy Markdown
Author

I've modified the code to ensure that both width and height of the datepicker remain constant as the precision level varies. In the process I also added the possibility for start and end dates to be of different precision levels. The attached video illustrates these changes.

datepicker-test.mp4

@ricardo37
Copy link
Copy Markdown
Author

Hi, just checking in on this PR. Happy to address any feedback. Could you approve the CI workflows when you get a chance?

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.

5 participants