diff --git a/i18n/en.pot b/i18n/en.pot
index ec19704ca..e348cad85 100644
--- a/i18n/en.pot
+++ b/i18n/en.pot
@@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
-"POT-Creation-Date: 2024-01-15T14:35:08.966Z\n"
-"PO-Revision-Date: 2024-01-15T14:35:08.966Z\n"
+"POT-Creation-Date: 2024-10-10T14:28:50.013Z\n"
+"PO-Revision-Date: 2024-10-10T14:28:50.014Z\n"
msgid "Not authorized"
msgstr "Not authorized"
@@ -525,6 +525,12 @@ msgstr "No"
msgid "Clear"
msgstr "Clear"
+msgid "Pick a date"
+msgstr "Pick a date"
+
+msgid "Pick a time"
+msgstr "Pick a time"
+
msgid "Delete"
msgstr "Delete"
diff --git a/package.json b/package.json
index 6e9bcfeec..de560a3ce 100644
--- a/package.json
+++ b/package.json
@@ -81,7 +81,7 @@
"node": ">=14.0.0"
},
"resolutions": {
- "@dhis2/multi-calendar-dates": "^1.1.0",
+ "@dhis2/multi-calendar-dates": "^1.3.2",
"@dhis2/ui": "^9.2.0",
"@dhis2/app-runtime": "^3.10.2"
}
diff --git a/src/data-workspace/inputs/datetime-input.js b/src/data-workspace/inputs/datetime-input.js
index 5f1ecd520..312fa2534 100644
--- a/src/data-workspace/inputs/datetime-input.js
+++ b/src/data-workspace/inputs/datetime-input.js
@@ -3,7 +3,12 @@ import i18n from '@dhis2/d2-i18n'
import { Button, CalendarInput } from '@dhis2/ui'
import React, { useState } from 'react'
import { useField } from 'react-final-form'
-import { useSetDataValueMutation, useUserInfo } from '../../shared/index.js'
+import {
+ convertFromIso8601ToString,
+ convertToIso8601ToString,
+ useSetDataValueMutation,
+ useUserInfo,
+} from '../../shared/index.js'
import styles from './inputs.module.css'
import { InputPropTypes } from './utils.js'
@@ -94,10 +99,19 @@ export const DateTimeInput = ({
onKeyDown={onKeyDown}
disabled={disabled}
readOnly={locked}
- date={date}
+ date={
+ date === ''
+ ? ''
+ : convertFromIso8601ToString(date, calendar)
+ }
calendar={calendar}
onDateSelect={(date) => {
- const selectedDate = date?.calendarDateString ?? ''
+ const selectedDate = date?.calendarDateString
+ ? convertToIso8601ToString(
+ date?.calendarDateString,
+ calendar
+ )
+ : ''
setDate(selectedDate)
handleChange({ date: selectedDate, time })
}}
diff --git a/src/data-workspace/inputs/datetime-input.test.js b/src/data-workspace/inputs/datetime-input.test.js
index 6b21abc47..0ca7b3b19 100644
--- a/src/data-workspace/inputs/datetime-input.test.js
+++ b/src/data-workspace/inputs/datetime-input.test.js
@@ -232,7 +232,7 @@ describe('date input field', () => {
expect(mutate.mock.calls[0][0]).toHaveProperty('value', '')
})
- it('works with ethiopian calendar', async () => {
+ it('posts ISO date to backend with ethiopian calendar', async () => {
// this is 2016-02-30 Ethopian
jest.setSystemTime(new Date('2023-11-10T09:05:00.000Z'))
@@ -281,13 +281,38 @@ describe('date input field', () => {
fireEvent.change(timepicker, { target: { value: '12:34' } })
expect(mutate.mock.calls).toHaveLength(1)
+
+ // date is converted back to ISO equivalent before being sent to backend
expect(mutate.mock.calls[0][0]).toHaveProperty(
'value',
- '2016-02-30T12:34'
+ '2023-11-10T12:34'
)
})
- it('works with nepali calendar', async () => {
+ it('populates the ethiopian equivalent of the persisted ISO date', async () => {
+ jest.setSystemTime(new Date('2024-07-25T09:05:00.000Z'))
+
+ useConfig.mockReturnValue({
+ systemInfo: { calendar: 'ethiopian' },
+ })
+
+ // 2021-04-22 ISO = 2013-08-14 ethiopian
+ const { getByRole, getByTestId } = render(
+
+
+
+ )
+
+ const calendarInput = getByRole('textbox')
+ expect(calendarInput.value).toBe('2013-08-14')
+
+ const timepicker = getByTestId('time-input')
+ expect(timepicker.value).toBe('13:17')
+ })
+
+ it('posts ISO date to backend with nepali calendar', async () => {
// this is 2080-02-32 Nepali
jest.setSystemTime(new Date('2023-06-15T09:05:00.000Z'))
@@ -330,9 +355,34 @@ describe('date input field', () => {
fireEvent.change(timepicker, { target: { value: '12:34' } })
expect(mutate.mock.calls).toHaveLength(1)
+
+ // date is converted back to ISO equivalent before being sent to backend
expect(mutate.mock.calls[0][0]).toHaveProperty(
'value',
- '2080-02-32T12:34'
+ '2023-06-15T12:34'
)
})
+
+ it('populates the nepali equivalent of the persisted ISO date', async () => {
+ jest.setSystemTime(new Date('2024-07-25T09:05:00.000Z'))
+
+ useConfig.mockReturnValue({
+ systemInfo: { calendar: 'nepali' },
+ })
+
+ // 2021-04-22 ISO = 2078-01-09 ethiopian
+ const { getByRole, getByTestId } = render(
+
+
+
+ )
+
+ const calendarInput = getByRole('textbox')
+ expect(calendarInput.value).toBe('2078-01-09')
+
+ const timepicker = getByTestId('time-input')
+ expect(timepicker.value).toBe('13:17')
+ })
})
diff --git a/src/shared/date/conversion-functions.js b/src/shared/date/conversion-functions.js
new file mode 100644
index 000000000..3c68c569d
--- /dev/null
+++ b/src/shared/date/conversion-functions.js
@@ -0,0 +1,23 @@
+import {
+ convertFromIso8601,
+ convertToIso8601,
+} from '@dhis2/multi-calendar-dates'
+
+const padWithZeros = (val, count) => String(val).padStart(count, '0')
+
+export const convertFromIso8601ToString = (date, calendar) => {
+ const { year, eraYear, month, day } = convertFromIso8601(date, calendar)
+ const ISOyear = calendar === 'ethiopian' ? eraYear : year
+ return `${padWithZeros(ISOyear, 4)}-${padWithZeros(
+ month,
+ 2
+ )}-${padWithZeros(day, 2)}`
+}
+
+export const convertToIso8601ToString = (date, calendar) => {
+ const { year, month, day } = convertToIso8601(date, calendar)
+ return `${padWithZeros(year, 4)}-${padWithZeros(month, 2)}-${padWithZeros(
+ day,
+ 2
+ )}`
+}
diff --git a/src/shared/date/index.js b/src/shared/date/index.js
index 9b099e8d7..ff074d508 100644
--- a/src/shared/date/index.js
+++ b/src/shared/date/index.js
@@ -2,3 +2,7 @@ export { default as formatJsDateToDateString } from './format-js-date-to-date-st
export { default as useClientServerDate } from './use-client-server-date.js'
export { default as useClientServerDateUtils } from './use-client-server-date-utils.js'
export { default as useServerTimeOffset } from './use-server-time-offset.js'
+export {
+ convertFromIso8601ToString,
+ convertToIso8601ToString,
+} from './conversion-functions.js'
diff --git a/yarn.lock b/yarn.lock
index 9f71bafe6..f5c24ed69 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2617,12 +2617,22 @@
i18next "^10.3"
moment "^2.24.0"
+"@dhis2/d2-i18n@^1.1.3":
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/@dhis2/d2-i18n/-/d2-i18n-1.1.3.tgz#ad73030f7cfceeed1b5bcaad86a9b336130bdfb1"
+ integrity sha512-vOu6RDNumOJM396mHt35bETk9ai9b6XJyAwlUy1HstUZNvfET61F8rjCmMuXZU6zJ8ELux8kMFqlH8IG0vDJmA==
+ dependencies:
+ "@types/i18next" "^11.9.0"
+ i18next "^10.3"
+ moment "^2.24.0"
+
"@dhis2/multi-calendar-dates@1.0.2", "@dhis2/multi-calendar-dates@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@dhis2/multi-calendar-dates/-/multi-calendar-dates-1.1.0.tgz#897d27eefa70eb60d3e8e72348f09ee739755ebb"
- integrity sha512-VAuq3dIcI42mt1pCL3Xp9pebAOpUEz2ngShTIPD1ll7oMeanRAXkweBT1gburc5W3SfD6jbS8GuwemJ4zg4d3Q==
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/@dhis2/multi-calendar-dates/-/multi-calendar-dates-1.3.2.tgz#34e5896f7fdfb761a2ee6035d848cba00446cde5"
+ integrity sha512-H37EptumkqZeHUpbR4wl3y2NZjeipxUNUI2VaHX28z2fbVD7O9H+k1InSskeP5nNWzTLMdPAM4lt/zQP8oRbrg==
dependencies:
- "@js-temporal/polyfill" "^0.4.2"
+ "@dhis2/d2-i18n" "^1.1.3"
+ "@js-temporal/polyfill" "0.4.3"
classnames "^2.3.2"
"@dhis2/prop-types@^3.0.0-beta.1", "@dhis2/prop-types@^3.1.2":
@@ -3257,7 +3267,7 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"
-"@js-temporal/polyfill@^0.4.2":
+"@js-temporal/polyfill@0.4.3":
version "0.4.3"
resolved "https://registry.yarnpkg.com/@js-temporal/polyfill/-/polyfill-0.4.3.tgz#e8f8cf86745eb5050679c46a5ebedb9a9cc1f09b"
integrity sha512-6Fmjo/HlkyVCmJzAPnvtEWlcbQUSRhi8qlN9EtJA/wP7FqXsevLLrlojR44kzNzrRkpf7eDJ+z7b4xQD/Ycypw==
@@ -3850,6 +3860,11 @@
dependencies:
"@types/node" "*"
+"@types/i18next@^11.9.0":
+ version "11.9.3"
+ resolved "https://registry.yarnpkg.com/@types/i18next/-/i18next-11.9.3.tgz#04d84c6539908ad69665d26d8967f942d1638550"
+ integrity sha512-snM7bMKy6gt7UYdpjsxycqSCAy0fr2JVPY0B8tJ2vp9bN58cE7C880k20PWFM4KXxQ3KsstKM8DLCawGCIH0tg==
+
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44"
@@ -5632,11 +5647,16 @@ class-utils@^0.3.5:
isobject "^3.0.0"
static-extend "^0.1.1"
-classnames@2.3.2, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2:
+classnames@2.3.2, classnames@^2.2.6, classnames@^2.3.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
+classnames@^2.3.2:
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b"
+ integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
+
clean-css@^5.2.2:
version "5.3.2"
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.2.tgz#70ecc7d4d4114921f5d298349ff86a31a9975224"
@@ -14983,11 +15003,16 @@ tslib@^1.8.1, tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
-tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1:
+tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
+tslib@^2.3.1:
+ version "2.7.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01"
+ integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==
+
tsutils@^3.21.0:
version "3.21.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"