From 8ac1273acb6661bab0762e330c495e8043de947c Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Wed, 12 Aug 2026 12:56:16 -0400 Subject: [PATCH] Support citing EDTF dates in the Date field and Extra EDTF dates -- ranges ("2021/2026"), uncertain/approximate dates ("2004-06~"), and BCE dates ("-0429") -- and common equivalent notations ("1995-1996", "2021-22", "~1995", "ca. 1995", "429 BCE") are now passed to citeproc-js as CSL date ranges, circa flags, and negative years. Previously, such dates were mangled or dropped entirely unless entered as CSL variables in Extra. CSL date variables in Extra get the same parsing. Other date handling in the client doesn't understand EDTF yet: the y/m/d indicator in the date field doesn't reflect EDTF parsing, date searches only match a range by its start date, and BCE dates still can't be sorted. Addresses #637 --- chrome/content/zotero/xpcom/utilities | 2 +- chrome/content/zotero/zotero.mjs | 1 + test/tests/citeTest.js | 29 +++++++++++++++++++++++++++ test/tests/utilitiesSubmoduleTest.js | 18 +++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/chrome/content/zotero/xpcom/utilities b/chrome/content/zotero/xpcom/utilities index 1dd38e27ed..2b125a7a4b 160000 --- a/chrome/content/zotero/xpcom/utilities +++ b/chrome/content/zotero/xpcom/utilities @@ -1 +1 @@ -Subproject commit 1dd38e27edf81e9d9c4161c957b7efb7f5681ac3 +Subproject commit 2b125a7a4b3e38cb6dcf8612dd2d351bafd112a8 diff --git a/chrome/content/zotero/zotero.mjs b/chrome/content/zotero/zotero.mjs index 2af2386810..e7a8e15d0b 100644 --- a/chrome/content/zotero/zotero.mjs +++ b/chrome/content/zotero/zotero.mjs @@ -38,6 +38,7 @@ const xpcomFilesAll = [ 'dataDirectory', 'debug', 'error', + 'utilities/edtf', 'utilities/date', 'utilities/utilities', 'utilities/utilities_item', diff --git a/test/tests/citeTest.js b/test/tests/citeTest.js index a3c3abff17..7a10846e47 100644 --- a/test/tests/citeTest.js +++ b/test/tests/citeTest.js @@ -38,6 +38,35 @@ describe("Zotero.Cite", function () { }); }); + describe("EDTF dates", function () { + async function makeBibliography(fields) { + let item = new Zotero.Item; + item.fromJSON(Object.assign({ + itemType: "book", + title: "Test Book" + }, fields)); + await item.saveTx(); + + let style = Zotero.Styles.get('http://www.zotero.org/styles/chicago-notes-bibliography'); + let cslEngine = style.getCiteProc('en-US'); + return Zotero.Cite.makeFormattedBibliographyOrCitationList(cslEngine, [item], "text"); + } + + it("should render a date range", async function () { + assert.include(await makeBibliography({ date: '2021/2026' }), '2021–2026'); + }); + + it("should render a BCE date", async function () { + assert.match(await makeBibliography({ date: '-429' }), /429\s?BC/); + }); + + it("should render an EDTF range in Extra, overriding the Date field", async function () { + let output = await makeBibliography({ date: '1999', extra: 'issued: 2021/2026' }); + assert.include(output, '2021–2026'); + assert.notInclude(output, '1999'); + }); + }); + describe("#retrieveLocale()", function () { it("should handle locale with script code", async function () { var item = new Zotero.Item; diff --git a/test/tests/utilitiesSubmoduleTest.js b/test/tests/utilitiesSubmoduleTest.js index 95e2b029ad..17ace81a69 100644 --- a/test/tests/utilitiesSubmoduleTest.js +++ b/test/tests/utilitiesSubmoduleTest.js @@ -29,5 +29,23 @@ describe("Zotero.Utilities.Item", function () { assert.deepEqual(fromZoteroItem, fromExportItem, 'conversion from Zotero Item and from export item are the same'); }); + + it("should preserve EDTF ranges and circa dates from CSL JSON", function () { + let item = new Zotero.Item('book'); + Zotero.Utilities.Item.itemFromCSLJSON(item, { type: 'book', issued: { "date-parts": [[2021], [2026]] } }); + assert.equal(item.getField('date'), '2021/2026'); + + item = new Zotero.Item('book'); + Zotero.Utilities.Item.itemFromCSLJSON(item, { type: 'book', issued: { "date-parts": [[-429]], circa: true } }); + assert.equal(item.getField('date'), '-0429~'); + }); + + it("should convert an EDTF date stored on an item", async function () { + let item = new Zotero.Item('book'); + item.setField('date', '-429?'); + await item.saveTx(); + let cslItem = Zotero.Utilities.Item.itemToCSLJSON(item); + assert.deepEqual(cslItem.issued, { "date-parts": [[-429]], circa: true }); + }); }); });