diff --git a/package.json b/package.json
index 13726e500..76a566ad7 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,7 @@
"build": "lerna exec --stream --ignore @scaleway/eslint-* --ignore @scaleway/countries -- rollup -c ../../rollup.config.mjs",
"build:profile": "cross-env PROFILE=true yarn run build",
"commit": "npx git-cz -a",
- "test": "jest",
+ "test": "TZ=UTC jest",
"test:watch": "yarn run test --watch",
"test:coverage": "yarn run test --coverage",
"prepare": "husky install"
diff --git a/packages/use-i18n/README.md b/packages/use-i18n/README.md
index 3e3c0f19a..52f8b756c 100644
--- a/packages/use-i18n/README.md
+++ b/packages/use-i18n/README.md
@@ -160,6 +160,55 @@ const App = () => {
}
```
+### formatDate
+
+This hook exposes a `formatDate` function which can be used to format JS dates
+
+The first parameter is anything that can be accepted as a valid JS Date (Date, number, string)
+
+It accepts an `options` as second parameter which can eiter be one of predefined shorthand formats (see below) or an [Intl.DateTimeFormat `options` object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat)
+
+Shorthand formats:
+```
+"long" => "February 13, 2020"
+"short" => (default) "Feb 13, 2020"
+"hour" => "February 13, 2020, 4:28 PM"
+"hourOnly" => "4:28 PM"
+"shortWithoutDay" => "Feb 2020"
+"numeric" => "2020-02-13"
+"numericHour" => "2020-02-13 4:28 PM"
+```
+
+```js
+import { useI18n } from '@scaleway/use-i18n'
+
+const App = () => {
+ const { formatDate } = useI18n()
+
+ const units = [
+ formatDate(new Date(2020, 1, 13, 16, 28)) // "Feb 13, 2020"
+ formatDate(1581607680000, 'long') // "February 13, 2020"
+ formatDate('2020-02-13T15:28:00.000Z', {
+ day: "numeric",
+ era: "short",
+ hour: "2-digit",
+ minute: "numeric",
+ month: "narrow",
+ second: "2-digit",
+ timeZoneName: "long",
+ weekday: "long",
+ year: "2-digit",
+ }) // "Thursday, F 13, 20 AD, 04:28:00 PM Central European Standard Time""
+ ]
+
+ return (
+
+ {units}
+
+ )
+}
+```
+
### formatUnit
This hook also exposes a `formatUnit` function which can be used to format bits/bytes until [ECMA-402 Unit Preferences](https://github.com/tc39/proposal-smart-unit-preferences) is standardised
@@ -175,15 +224,14 @@ It accepts an `options` as second parameter:
```js
import { useI18n } from '@scaleway/use-i18n'
-import { DateInput } from '@scaleway/ui'
const App = () => {
const { formatUnit } = useI18n()
const units = [
- formatUnit(12 { unit: 'kilobyte' }) // "12 KB" or "12 Ko" in fr an ro
- formatUnit(10 ** 8 { unit: 'bytes-humanized' }) // "100 MB" or "100 Mo" in fr an ro
- formatUnit(10 ** 8 { unit: 'bits-per-second-humanized' }) // "100Mbs"
+ formatUnit(12, { unit: 'kilobyte' }) // "12 KB" or "12 Ko" in fr an ro
+ formatUnit(10 ** 8, { unit: 'bytes-humanized' }) // "100 MB" or "100 Mo" in fr an ro
+ formatUnit(10 ** 8, { unit: 'bits-per-second-humanized' }) // "100Mbs"
]
return (
diff --git a/packages/use-i18n/src/__tests__/__snapshots__/formatDate.ts.snap b/packages/use-i18n/src/__tests__/__snapshots__/formatDate.ts.snap
new file mode 100644
index 000000000..506ac38db
--- /dev/null
+++ b/packages/use-i18n/src/__tests__/__snapshots__/formatDate.ts.snap
@@ -0,0 +1,247 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`formatDate should return passed object if not valid date 1`] = `
+Object {
+ "not": "a valid date",
+}
+`;
+
+exports[`formatDate should work with custom format and locale de 1`] = `"Donnerstag, 13. F 20 n. Chr., 16:28:00 Koordinierte Weltzeit"`;
+
+exports[`formatDate should work with custom format and locale de 2`] = `"Donnerstag, 13. F 20 n. Chr., 15:28:00 Koordinierte Weltzeit"`;
+
+exports[`formatDate should work with custom format and locale de 3`] = `"Donnerstag, 13. F 20 n. Chr., 15:28:00 Koordinierte Weltzeit"`;
+
+exports[`formatDate should work with custom format and locale en 1`] = `"Thursday, F 13, 20 AD, 04:28:00 PM Coordinated Universal Time"`;
+
+exports[`formatDate should work with custom format and locale en 2`] = `"Thursday, F 13, 20 AD, 03:28:00 PM Coordinated Universal Time"`;
+
+exports[`formatDate should work with custom format and locale en 3`] = `"Thursday, F 13, 20 AD, 03:28:00 PM Coordinated Universal Time"`;
+
+exports[`formatDate should work with custom format and locale es 1`] = `"jueves, 13 F 20 d. C. 16:28:00 (tiempo universal coordinado)"`;
+
+exports[`formatDate should work with custom format and locale es 2`] = `"jueves, 13 F 20 d. C. 15:28:00 (tiempo universal coordinado)"`;
+
+exports[`formatDate should work with custom format and locale es 3`] = `"jueves, 13 F 20 d. C. 15:28:00 (tiempo universal coordinado)"`;
+
+exports[`formatDate should work with custom format and locale fr 1`] = `"jeudi 13 F 20 ap. J.-C., 16:28:00 Temps universel coordonné"`;
+
+exports[`formatDate should work with custom format and locale fr 2`] = `"jeudi 13 F 20 ap. J.-C., 15:28:00 Temps universel coordonné"`;
+
+exports[`formatDate should work with custom format and locale fr 3`] = `"jeudi 13 F 20 ap. J.-C., 15:28:00 Temps universel coordonné"`;
+
+exports[`formatDate should work with custom format and locale ro 1`] = `"joi, 13 F 20 d.Hr., 16:28:00 Timpul universal coordonat"`;
+
+exports[`formatDate should work with custom format and locale ro 2`] = `"joi, 13 F 20 d.Hr., 15:28:00 Timpul universal coordonat"`;
+
+exports[`formatDate should work with custom format and locale ro 3`] = `"joi, 13 F 20 d.Hr., 15:28:00 Timpul universal coordonat"`;
+
+exports[`formatDate should work with format "hour", for date = "2020-02-13T15:28:00.000Z" and locale "de" 1`] = `"13. Februar 2020, 15:28"`;
+
+exports[`formatDate should work with format "hour", for date = "2020-02-13T15:28:00.000Z" and locale "en" 1`] = `"February 13, 2020, 3:28 PM"`;
+
+exports[`formatDate should work with format "hour", for date = "2020-02-13T15:28:00.000Z" and locale "es" 1`] = `"13 de febrero de 2020 15:28"`;
+
+exports[`formatDate should work with format "hour", for date = "2020-02-13T15:28:00.000Z" and locale "fr" 1`] = `"13 février 2020, 15:28"`;
+
+exports[`formatDate should work with format "hour", for date = "2020-02-13T15:28:00.000Z" and locale "ro" 1`] = `"13 februarie 2020, 15:28"`;
+
+exports[`formatDate should work with format "hour", for date = "1581607680000" and locale "de" 1`] = `"13. Februar 2020, 15:28"`;
+
+exports[`formatDate should work with format "hour", for date = "1581607680000" and locale "en" 1`] = `"February 13, 2020, 3:28 PM"`;
+
+exports[`formatDate should work with format "hour", for date = "1581607680000" and locale "es" 1`] = `"13 de febrero de 2020 15:28"`;
+
+exports[`formatDate should work with format "hour", for date = "1581607680000" and locale "fr" 1`] = `"13 février 2020, 15:28"`;
+
+exports[`formatDate should work with format "hour", for date = "1581607680000" and locale "ro" 1`] = `"13 februarie 2020, 15:28"`;
+
+exports[`formatDate should work with format "hour", for date = "new Date(2020, 1, 13, 16, 28)" and locale "de" 1`] = `"13. Februar 2020, 16:28"`;
+
+exports[`formatDate should work with format "hour", for date = "new Date(2020, 1, 13, 16, 28)" and locale "en" 1`] = `"February 13, 2020, 4:28 PM"`;
+
+exports[`formatDate should work with format "hour", for date = "new Date(2020, 1, 13, 16, 28)" and locale "es" 1`] = `"13 de febrero de 2020 16:28"`;
+
+exports[`formatDate should work with format "hour", for date = "new Date(2020, 1, 13, 16, 28)" and locale "fr" 1`] = `"13 février 2020, 16:28"`;
+
+exports[`formatDate should work with format "hour", for date = "new Date(2020, 1, 13, 16, 28)" and locale "ro" 1`] = `"13 februarie 2020, 16:28"`;
+
+exports[`formatDate should work with format "hourOnly", for date = "2020-02-13T15:28:00.000Z" and locale "de" 1`] = `"15:28"`;
+
+exports[`formatDate should work with format "hourOnly", for date = "2020-02-13T15:28:00.000Z" and locale "en" 1`] = `"3:28 PM"`;
+
+exports[`formatDate should work with format "hourOnly", for date = "2020-02-13T15:28:00.000Z" and locale "es" 1`] = `"15:28"`;
+
+exports[`formatDate should work with format "hourOnly", for date = "2020-02-13T15:28:00.000Z" and locale "fr" 1`] = `"15:28"`;
+
+exports[`formatDate should work with format "hourOnly", for date = "2020-02-13T15:28:00.000Z" and locale "ro" 1`] = `"15:28"`;
+
+exports[`formatDate should work with format "hourOnly", for date = "1581607680000" and locale "de" 1`] = `"15:28"`;
+
+exports[`formatDate should work with format "hourOnly", for date = "1581607680000" and locale "en" 1`] = `"3:28 PM"`;
+
+exports[`formatDate should work with format "hourOnly", for date = "1581607680000" and locale "es" 1`] = `"15:28"`;
+
+exports[`formatDate should work with format "hourOnly", for date = "1581607680000" and locale "fr" 1`] = `"15:28"`;
+
+exports[`formatDate should work with format "hourOnly", for date = "1581607680000" and locale "ro" 1`] = `"15:28"`;
+
+exports[`formatDate should work with format "hourOnly", for date = "new Date(2020, 1, 13, 16, 28)" and locale "de" 1`] = `"16:28"`;
+
+exports[`formatDate should work with format "hourOnly", for date = "new Date(2020, 1, 13, 16, 28)" and locale "en" 1`] = `"4:28 PM"`;
+
+exports[`formatDate should work with format "hourOnly", for date = "new Date(2020, 1, 13, 16, 28)" and locale "es" 1`] = `"16:28"`;
+
+exports[`formatDate should work with format "hourOnly", for date = "new Date(2020, 1, 13, 16, 28)" and locale "fr" 1`] = `"16:28"`;
+
+exports[`formatDate should work with format "hourOnly", for date = "new Date(2020, 1, 13, 16, 28)" and locale "ro" 1`] = `"16:28"`;
+
+exports[`formatDate should work with format "long", for date = "2020-02-13T15:28:00.000Z" and locale "de" 1`] = `"13. Februar 2020"`;
+
+exports[`formatDate should work with format "long", for date = "2020-02-13T15:28:00.000Z" and locale "en" 1`] = `"February 13, 2020"`;
+
+exports[`formatDate should work with format "long", for date = "2020-02-13T15:28:00.000Z" and locale "es" 1`] = `"13 de febrero de 2020"`;
+
+exports[`formatDate should work with format "long", for date = "2020-02-13T15:28:00.000Z" and locale "fr" 1`] = `"13 février 2020"`;
+
+exports[`formatDate should work with format "long", for date = "2020-02-13T15:28:00.000Z" and locale "ro" 1`] = `"13 februarie 2020"`;
+
+exports[`formatDate should work with format "long", for date = "1581607680000" and locale "de" 1`] = `"13. Februar 2020"`;
+
+exports[`formatDate should work with format "long", for date = "1581607680000" and locale "en" 1`] = `"February 13, 2020"`;
+
+exports[`formatDate should work with format "long", for date = "1581607680000" and locale "es" 1`] = `"13 de febrero de 2020"`;
+
+exports[`formatDate should work with format "long", for date = "1581607680000" and locale "fr" 1`] = `"13 février 2020"`;
+
+exports[`formatDate should work with format "long", for date = "1581607680000" and locale "ro" 1`] = `"13 februarie 2020"`;
+
+exports[`formatDate should work with format "long", for date = "new Date(2020, 1, 13, 16, 28)" and locale "de" 1`] = `"13. Februar 2020"`;
+
+exports[`formatDate should work with format "long", for date = "new Date(2020, 1, 13, 16, 28)" and locale "en" 1`] = `"February 13, 2020"`;
+
+exports[`formatDate should work with format "long", for date = "new Date(2020, 1, 13, 16, 28)" and locale "es" 1`] = `"13 de febrero de 2020"`;
+
+exports[`formatDate should work with format "long", for date = "new Date(2020, 1, 13, 16, 28)" and locale "fr" 1`] = `"13 février 2020"`;
+
+exports[`formatDate should work with format "long", for date = "new Date(2020, 1, 13, 16, 28)" and locale "ro" 1`] = `"13 februarie 2020"`;
+
+exports[`formatDate should work with format "numeric", for date = "2020-02-13T15:28:00.000Z" and locale "de" 1`] = `"2020-02-13"`;
+
+exports[`formatDate should work with format "numeric", for date = "2020-02-13T15:28:00.000Z" and locale "en" 1`] = `"2020-02-13"`;
+
+exports[`formatDate should work with format "numeric", for date = "2020-02-13T15:28:00.000Z" and locale "es" 1`] = `"2020-02-13"`;
+
+exports[`formatDate should work with format "numeric", for date = "2020-02-13T15:28:00.000Z" and locale "fr" 1`] = `"2020-02-13"`;
+
+exports[`formatDate should work with format "numeric", for date = "2020-02-13T15:28:00.000Z" and locale "ro" 1`] = `"2020-02-13"`;
+
+exports[`formatDate should work with format "numeric", for date = "1581607680000" and locale "de" 1`] = `"2020-02-13"`;
+
+exports[`formatDate should work with format "numeric", for date = "1581607680000" and locale "en" 1`] = `"2020-02-13"`;
+
+exports[`formatDate should work with format "numeric", for date = "1581607680000" and locale "es" 1`] = `"2020-02-13"`;
+
+exports[`formatDate should work with format "numeric", for date = "1581607680000" and locale "fr" 1`] = `"2020-02-13"`;
+
+exports[`formatDate should work with format "numeric", for date = "1581607680000" and locale "ro" 1`] = `"2020-02-13"`;
+
+exports[`formatDate should work with format "numeric", for date = "new Date(2020, 1, 13, 16, 28)" and locale "de" 1`] = `"2020-02-13"`;
+
+exports[`formatDate should work with format "numeric", for date = "new Date(2020, 1, 13, 16, 28)" and locale "en" 1`] = `"2020-02-13"`;
+
+exports[`formatDate should work with format "numeric", for date = "new Date(2020, 1, 13, 16, 28)" and locale "es" 1`] = `"2020-02-13"`;
+
+exports[`formatDate should work with format "numeric", for date = "new Date(2020, 1, 13, 16, 28)" and locale "fr" 1`] = `"2020-02-13"`;
+
+exports[`formatDate should work with format "numeric", for date = "new Date(2020, 1, 13, 16, 28)" and locale "ro" 1`] = `"2020-02-13"`;
+
+exports[`formatDate should work with format "numericHour", for date = "2020-02-13T15:28:00.000Z" and locale "de" 1`] = `"2020-02-13 15:28"`;
+
+exports[`formatDate should work with format "numericHour", for date = "2020-02-13T15:28:00.000Z" and locale "en" 1`] = `"2020-02-13 3:28 PM"`;
+
+exports[`formatDate should work with format "numericHour", for date = "2020-02-13T15:28:00.000Z" and locale "es" 1`] = `"2020-02-13 15:28"`;
+
+exports[`formatDate should work with format "numericHour", for date = "2020-02-13T15:28:00.000Z" and locale "fr" 1`] = `"2020-02-13 15:28"`;
+
+exports[`formatDate should work with format "numericHour", for date = "2020-02-13T15:28:00.000Z" and locale "ro" 1`] = `"2020-02-13 15:28"`;
+
+exports[`formatDate should work with format "numericHour", for date = "1581607680000" and locale "de" 1`] = `"2020-02-13 15:28"`;
+
+exports[`formatDate should work with format "numericHour", for date = "1581607680000" and locale "en" 1`] = `"2020-02-13 3:28 PM"`;
+
+exports[`formatDate should work with format "numericHour", for date = "1581607680000" and locale "es" 1`] = `"2020-02-13 15:28"`;
+
+exports[`formatDate should work with format "numericHour", for date = "1581607680000" and locale "fr" 1`] = `"2020-02-13 15:28"`;
+
+exports[`formatDate should work with format "numericHour", for date = "1581607680000" and locale "ro" 1`] = `"2020-02-13 15:28"`;
+
+exports[`formatDate should work with format "numericHour", for date = "new Date(2020, 1, 13, 16, 28)" and locale "de" 1`] = `"2020-02-13 16:28"`;
+
+exports[`formatDate should work with format "numericHour", for date = "new Date(2020, 1, 13, 16, 28)" and locale "en" 1`] = `"2020-02-13 4:28 PM"`;
+
+exports[`formatDate should work with format "numericHour", for date = "new Date(2020, 1, 13, 16, 28)" and locale "es" 1`] = `"2020-02-13 16:28"`;
+
+exports[`formatDate should work with format "numericHour", for date = "new Date(2020, 1, 13, 16, 28)" and locale "fr" 1`] = `"2020-02-13 16:28"`;
+
+exports[`formatDate should work with format "numericHour", for date = "new Date(2020, 1, 13, 16, 28)" and locale "ro" 1`] = `"2020-02-13 16:28"`;
+
+exports[`formatDate should work with format "short", for date = "2020-02-13T15:28:00.000Z" and locale "de" 1`] = `"13. Feb. 2020"`;
+
+exports[`formatDate should work with format "short", for date = "2020-02-13T15:28:00.000Z" and locale "en" 1`] = `"Feb 13, 2020"`;
+
+exports[`formatDate should work with format "short", for date = "2020-02-13T15:28:00.000Z" and locale "es" 1`] = `"13 feb 2020"`;
+
+exports[`formatDate should work with format "short", for date = "2020-02-13T15:28:00.000Z" and locale "fr" 1`] = `"13 févr. 2020"`;
+
+exports[`formatDate should work with format "short", for date = "2020-02-13T15:28:00.000Z" and locale "ro" 1`] = `"13 feb. 2020"`;
+
+exports[`formatDate should work with format "short", for date = "1581607680000" and locale "de" 1`] = `"13. Feb. 2020"`;
+
+exports[`formatDate should work with format "short", for date = "1581607680000" and locale "en" 1`] = `"Feb 13, 2020"`;
+
+exports[`formatDate should work with format "short", for date = "1581607680000" and locale "es" 1`] = `"13 feb 2020"`;
+
+exports[`formatDate should work with format "short", for date = "1581607680000" and locale "fr" 1`] = `"13 févr. 2020"`;
+
+exports[`formatDate should work with format "short", for date = "1581607680000" and locale "ro" 1`] = `"13 feb. 2020"`;
+
+exports[`formatDate should work with format "short", for date = "new Date(2020, 1, 13, 16, 28)" and locale "de" 1`] = `"13. Feb. 2020"`;
+
+exports[`formatDate should work with format "short", for date = "new Date(2020, 1, 13, 16, 28)" and locale "en" 1`] = `"Feb 13, 2020"`;
+
+exports[`formatDate should work with format "short", for date = "new Date(2020, 1, 13, 16, 28)" and locale "es" 1`] = `"13 feb 2020"`;
+
+exports[`formatDate should work with format "short", for date = "new Date(2020, 1, 13, 16, 28)" and locale "fr" 1`] = `"13 févr. 2020"`;
+
+exports[`formatDate should work with format "short", for date = "new Date(2020, 1, 13, 16, 28)" and locale "ro" 1`] = `"13 feb. 2020"`;
+
+exports[`formatDate should work with format "shortWithoutDay", for date = "2020-02-13T15:28:00.000Z" and locale "de" 1`] = `"Feb. 2020"`;
+
+exports[`formatDate should work with format "shortWithoutDay", for date = "2020-02-13T15:28:00.000Z" and locale "en" 1`] = `"Feb 2020"`;
+
+exports[`formatDate should work with format "shortWithoutDay", for date = "2020-02-13T15:28:00.000Z" and locale "es" 1`] = `"feb 2020"`;
+
+exports[`formatDate should work with format "shortWithoutDay", for date = "2020-02-13T15:28:00.000Z" and locale "fr" 1`] = `"févr. 2020"`;
+
+exports[`formatDate should work with format "shortWithoutDay", for date = "2020-02-13T15:28:00.000Z" and locale "ro" 1`] = `"feb. 2020"`;
+
+exports[`formatDate should work with format "shortWithoutDay", for date = "1581607680000" and locale "de" 1`] = `"Feb. 2020"`;
+
+exports[`formatDate should work with format "shortWithoutDay", for date = "1581607680000" and locale "en" 1`] = `"Feb 2020"`;
+
+exports[`formatDate should work with format "shortWithoutDay", for date = "1581607680000" and locale "es" 1`] = `"feb 2020"`;
+
+exports[`formatDate should work with format "shortWithoutDay", for date = "1581607680000" and locale "fr" 1`] = `"févr. 2020"`;
+
+exports[`formatDate should work with format "shortWithoutDay", for date = "1581607680000" and locale "ro" 1`] = `"feb. 2020"`;
+
+exports[`formatDate should work with format "shortWithoutDay", for date = "new Date(2020, 1, 13, 16, 28)" and locale "de" 1`] = `"Feb. 2020"`;
+
+exports[`formatDate should work with format "shortWithoutDay", for date = "new Date(2020, 1, 13, 16, 28)" and locale "en" 1`] = `"Feb 2020"`;
+
+exports[`formatDate should work with format "shortWithoutDay", for date = "new Date(2020, 1, 13, 16, 28)" and locale "es" 1`] = `"feb 2020"`;
+
+exports[`formatDate should work with format "shortWithoutDay", for date = "new Date(2020, 1, 13, 16, 28)" and locale "fr" 1`] = `"févr. 2020"`;
+
+exports[`formatDate should work with format "shortWithoutDay", for date = "new Date(2020, 1, 13, 16, 28)" and locale "ro" 1`] = `"feb. 2020"`;
diff --git a/packages/use-i18n/src/__tests__/formatDate.ts b/packages/use-i18n/src/__tests__/formatDate.ts
new file mode 100644
index 000000000..8610ef1f1
--- /dev/null
+++ b/packages/use-i18n/src/__tests__/formatDate.ts
@@ -0,0 +1,76 @@
+import formatDate, { FormatDateOptions, supportedFormats } from '../formatDate'
+
+const locales = ['en', 'fr', 'de', 'ro', 'es']
+
+const tests = [
+ ...locales.map(locale => [
+ ...supportedFormats.map(format => [
+ format,
+ 'new Date(2020, 1, 13, 16, 28)',
+ locale,
+ new Date(2020, 1, 13, 16, 28),
+ ])
+ ]),
+ ...locales.map(locale => [
+ ...supportedFormats.map(format => [
+ format,
+ '1581607680000',
+ locale,
+ 1581607680000,
+ ])
+ ]),
+ ...locales.map(locale => [
+ ...supportedFormats.map(format => [
+ format,
+ '2020-02-13T15:28:00.000Z',
+ locale,
+ '2020-02-13T15:28:00.000Z',
+ ])
+ ]),
+].flat()
+
+describe('formatDate', () => {
+ test.each(tests)('should work with format "%s", for date = "%s" and locale "%s"', (format, _, locale, date) => {
+ expect(
+ formatDate(locale as string, date, format as FormatDateOptions),
+ ).toMatchSnapshot()
+ })
+
+ test.each(locales)('should work with custom format and locale %s', (locale) => {
+ const format = {
+ day: "numeric",
+ era: "short",
+ hour: "2-digit",
+ minute: "numeric",
+ month: "narrow",
+ second: "2-digit",
+ timeZoneName: "long",
+ weekday: "long",
+ year: "2-digit",
+ }
+
+ expect(
+ formatDate(locale, new Date(2020, 1, 13, 16, 28), format),
+ ).toMatchSnapshot()
+ expect(
+ formatDate(locale, 1581607680000, format),
+ ).toMatchSnapshot()
+ expect(
+ formatDate(locale, '2020-02-13T15:28:00.000Z', format),
+ ).toMatchSnapshot()
+ })
+
+ test('should return passed object if not valid date', () => {
+ expect(
+ // @ts-expect-error we check a failing case
+ formatDate('fr', { not: 'a valid date' }),
+ ).toMatchSnapshot()
+ })
+
+ test('should throw if shorthand format is invalid', () => {
+ expect(() =>
+ // @ts-expect-error we check a failing case
+ formatDate('fr', 1581607680000, 'not a valid format'),
+ ).toThrowError('format "not a valid format" should be one of hour, hourOnly, long, short, shortWithoutDay, numeric, numericHour or a valid Intl.DateTimeFormat options object')
+ })
+})
diff --git a/packages/use-i18n/src/__tests__/usei18n.tsx b/packages/use-i18n/src/__tests__/usei18n.tsx
index cb7487d07..4b56e868c 100644
--- a/packages/use-i18n/src/__tests__/usei18n.tsx
+++ b/packages/use-i18n/src/__tests__/usei18n.tsx
@@ -522,6 +522,26 @@ describe('i18n hook', () => {
).toEqual('12 octets')
})
+ it('should formatDate', async () => {
+ const { result, waitForNextUpdate } = renderHook(() => useI18n(), {
+ wrapper: wrapper({
+ defaultLocale: 'en',
+ }),
+ })
+
+ expect(
+ result.current.formatDate(new Date(2020, 1, 13, 16, 28), 'numericHour'),
+ ).toEqual('2020-02-13 4:28 PM')
+ act(() => {
+ result.current.switchLocale('fr')
+ })
+ await waitForNextUpdate()
+
+ expect(
+ result.current.formatDate(new Date(2020, 1, 13, 16, 28), 'numericHour'),
+ ).toEqual('2020-02-13 16:28')
+ })
+
it('should load default datefns locales', async () => {
const { result, waitForNextUpdate } = renderHook(() => useI18n(), {
wrapper: wrapper({
diff --git a/packages/use-i18n/src/formatDate.ts b/packages/use-i18n/src/formatDate.ts
new file mode 100644
index 000000000..032c6df6b
--- /dev/null
+++ b/packages/use-i18n/src/formatDate.ts
@@ -0,0 +1,71 @@
+import { formatISO, intlFormat } from 'date-fns'
+
+const formatOptions = {
+ hour: {
+ // Expected output format: February 13, 2020, 4:28 PM
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ month: 'long',
+ year: 'numeric',
+ },
+ hourOnly: {
+ // Expected output format: 4:28 PM
+ hour: 'numeric',
+ minute: 'numeric',
+ },
+ long: {
+ // Expected output format: February 13, 2020
+ day: 'numeric',
+ month: 'long',
+ year: 'numeric',
+ },
+ short: {
+ // Expected output format: Feb 13, 2020
+ day: 'numeric',
+ month: 'short',
+ year: 'numeric',
+ },
+ shortWithoutDay: {
+ // Expected output format: Feb 2020
+ month: 'short',
+ year: 'numeric',
+ },
+}
+
+const complexFormatOptions = {
+ // Expected output format: 2020-02-13
+ numeric: (date: Date): string => formatISO(date, { representation: 'date' }),
+ // Expected output format: 2020-02-13 4:28 PM
+ numericHour: (date: Date, locale: string): string => `${formatISO(date, { representation: 'date' })} ${intlFormat(date, formatOptions.hourOnly as Intl.DateTimeFormatOptions, { locale })}`,
+}
+
+export const supportedFormats = [...Object.keys(formatOptions), ...Object.keys(complexFormatOptions)]
+
+export type FormatDateOptions = keyof typeof formatOptions | keyof typeof complexFormatOptions | Intl.DateTimeFormatOptions
+
+const formatDate = (
+ locale: string,
+ date: Date | number | string,
+ format: FormatDateOptions = 'short',
+): string => {
+ if (typeof format === 'string' && !((`${format}` in formatOptions) || (`${format}` in complexFormatOptions))) {
+ throw new Error(`format "${format}" should be one of ${supportedFormats.join(', ')} or a valid Intl.DateTimeFormat options object`)
+ }
+
+ const properDate: Date = typeof date === 'string' || typeof date === 'number' ? new Date(date) : date
+
+ if (typeof format === 'string' && `${format}` in complexFormatOptions) {
+ return complexFormatOptions[format as keyof typeof complexFormatOptions](properDate, locale)
+ }
+
+ const options = typeof format === 'string' ? formatOptions[format as keyof typeof formatOptions] : format
+
+ if (properDate instanceof Date) {
+ return intlFormat(properDate, options as Intl.DateTimeFormatOptions, { locale })
+ }
+
+ return properDate
+}
+
+export default formatDate
diff --git a/packages/use-i18n/src/usei18n.tsx b/packages/use-i18n/src/usei18n.tsx
index 6e68623e1..1d7dec2f8 100644
--- a/packages/use-i18n/src/usei18n.tsx
+++ b/packages/use-i18n/src/usei18n.tsx
@@ -14,9 +14,9 @@ import React, {
} from 'react'
import ReactDOM from 'react-dom'
import 'intl-pluralrules'
+import dateFormat, { FormatDateOptions } from './formatDate'
import unitFormat, { FormatUnitOptions } from './formatUnit'
-
const LOCALE_ITEM_STORAGE = 'locale'
type Translations = Record & { prefix?: string }
@@ -59,6 +59,7 @@ interface Context {
currentLocale: string
dateFnsLocale?: Locale,
datetime?: (date: Date | number, options?: Intl.DateTimeFormatOptions) => string,
+ formatDate?: (value: Date | number | string, options: FormatDateOptions) => string,
formatList?: (listFormat: string[], options?: Intl.ListFormatOptions) => string,
formatNumber?: (numb: number, options?: Intl.NumberFormatOptions) => string,
formatUnit?: (value: number, options: FormatUnitOptions) => string,
@@ -238,6 +239,12 @@ const I18nContextProvider = ({
[currentLocale],
)
+ const formatDate = useCallback(
+ (value: Date | number | string, options: FormatDateOptions) =>
+ dateFormat(currentLocale, value, options),
+ [currentLocale],
+ )
+
const datetime = useCallback(
// intl-format-chache does not forwrad return types
// eslint-disable-next-line
@@ -309,6 +316,7 @@ const I18nContextProvider = ({
currentLocale,
dateFnsLocale,
datetime,
+ formatDate,
formatList,
formatNumber,
formatUnit,
@@ -327,6 +335,7 @@ const I18nContextProvider = ({
currentLocale,
dateFnsLocale,
datetime,
+ formatDate,
formatList,
formatNumber,
formatUnit,