@openmrs/esm-utils 3.1.15-pre.718 → 3.1.15-pre.730

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-utils",
3
- "version": "3.1.15-pre.718",
3
+ "version": "3.1.15-pre.730",
4
4
  "license": "MPL-2.0",
5
5
  "description": "Helper utilities for OpenMRS",
6
6
  "browser": "dist/openmrs-esm-utils.js",
@@ -43,10 +43,11 @@
43
43
  },
44
44
  "peerDependencies": {
45
45
  "dayjs": "1.x",
46
+ "i18next": "19.x",
46
47
  "rxjs": "6.x"
47
48
  },
48
49
  "dependencies": {
49
50
  "semver": "^7.3.2"
50
51
  },
51
- "gitHead": "4f30619d9e3db89532aafd059d9d12740782501b"
52
+ "gitHead": "e054367bca570e181a4cf718ffc6aba16a9adaf7"
52
53
  }
@@ -4,6 +4,11 @@ import {
4
4
  isOmrsDateStrict,
5
5
  } from "./omrs-dates";
6
6
  import dayjs from "dayjs";
7
+ import timezoneMock from "timezone-mock";
8
+ import { formatDate, formatDatetime, formatTime } from ".";
9
+ import { i18n } from "i18next";
10
+
11
+ window.i18next = { language: "en" } as i18n;
7
12
 
8
13
  describe("Openmrs Dates", () => {
9
14
  it("converts js Date object to omrs date string version", () => {
@@ -41,4 +46,61 @@ describe("Openmrs Dates", () => {
41
46
  ).toDate();
42
47
  expect(toOmrsIsoString(date, true)).toEqual("2018-03-18T21:05:03.999+0000");
43
48
  });
49
+
50
+ it("formats 'Today' with respect to the locale", () => {
51
+ const testDate = new Date();
52
+ window.i18next.language = "en";
53
+ expect(formatDate(testDate)).toEqual("Today");
54
+ expect(formatDate(testDate, "no day")).toEqual("Today");
55
+ expect(formatDate(testDate, "no year")).toEqual("Today");
56
+ expect(formatDate(testDate, "wide")).toEqual("Today");
57
+ window.i18next.language = "sw";
58
+ expect(formatDate(testDate)).toEqual("Leo");
59
+ window.i18next.language = "ru";
60
+ expect(formatDate(testDate)).toEqual("Сегодня");
61
+ });
62
+
63
+ it("formats dates with respect to the locale", () => {
64
+ timezoneMock.register("UTC");
65
+ const testDate = new Date("2021-12-09");
66
+ window.i18next.language = "en";
67
+ expect(formatDate(testDate)).toEqual("09-Dec-2021");
68
+ expect(formatDate(testDate, "no day")).toEqual("Dec 2021");
69
+ expect(formatDate(testDate, "no year")).toEqual("09 Dec");
70
+ expect(formatDate(testDate, "wide")).toEqual("09 — Dec — 2021");
71
+ window.i18next.language = "fr";
72
+ expect(formatDate(testDate)).toEqual("09 déc. 2021");
73
+ expect(formatDate(testDate, "no day")).toEqual("déc. 2021");
74
+ expect(formatDate(testDate, "no year")).toEqual("09 déc.");
75
+ expect(formatDate(testDate, "wide")).toEqual("09 — déc. — 2021");
76
+ window.i18next.language = "sw";
77
+ expect(formatDate(testDate)).toEqual("09 Des 2021");
78
+ window.i18next.language = "ru";
79
+ expect(formatDate(testDate, "wide")).toEqual("09 — дек. — 2021 г.");
80
+ });
81
+
82
+ it("formats times with respect to the locale", () => {
83
+ timezoneMock.register("Australia/Adelaide");
84
+ const testDate = new Date("2021-12-09T13:15:33");
85
+ window.i18next.language = "en";
86
+ expect(formatTime(testDate)).toEqual("01:15 PM");
87
+ window.i18next.language = "es-CO";
88
+ expect(formatTime(testDate)).toMatch(/1:15 p.\sm./); // it's not a normal space between the 'p.' and 'm.'
89
+ window.i18next.language = "es-MX";
90
+ expect(formatTime(testDate)).toEqual("13:15");
91
+ });
92
+
93
+ it("formats datetimes with respect to the locale", () => {
94
+ timezoneMock.register("US/Pacific");
95
+ const testDate = new Date("2022-02-09T13:15:33");
96
+ const todayDate = new Date();
97
+ todayDate.setHours(15);
98
+ todayDate.setMinutes(20);
99
+ window.i18next.language = "en";
100
+ expect(formatDatetime(testDate)).toEqual("09-Feb-2022, 01:15 PM");
101
+ expect(formatDatetime(todayDate)).toEqual("Today, 03:20 PM");
102
+ window.i18next.language = "ht";
103
+ expect(formatDatetime(testDate)).toEqual("09 févr. 2022, 13:15");
104
+ expect(formatDatetime(todayDate)).toEqual("Aujourd’hui, 15:20");
105
+ });
44
106
  });
package/src/omrs-dates.ts CHANGED
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @module
3
+ * @category Date and time
4
+ */
5
+ import { i18n } from "i18next";
1
6
  import dayjs from "dayjs";
2
7
  import utc from "dayjs/plugin/utc";
3
8
  import isToday from "dayjs/plugin/isToday";
@@ -5,6 +10,12 @@ import isToday from "dayjs/plugin/isToday";
5
10
  dayjs.extend(utc);
6
11
  dayjs.extend(isToday);
7
12
 
13
+ declare global {
14
+ interface Window {
15
+ i18next: i18n;
16
+ }
17
+ }
18
+
8
19
  export type DateInput = string | number | Date;
9
20
 
10
21
  const isoFormat = "YYYY-MM-DDTHH:mm:ss.SSSZZ";
@@ -79,6 +90,7 @@ export function toOmrsIsoString(date: DateInput, toUTC = false): string {
79
90
  }
80
91
 
81
92
  /**
93
+ * @deprecated use `formatTime`
82
94
  * Formats the input as a time string using the format "HH:mm".
83
95
  */
84
96
  export function toOmrsTimeString24(date: DateInput) {
@@ -86,6 +98,7 @@ export function toOmrsTimeString24(date: DateInput) {
86
98
  }
87
99
 
88
100
  /**
101
+ * @deprecated use `formatTime`
89
102
  * Formats the input as a time string using the format "HH:mm A".
90
103
  */
91
104
  export function toOmrsTimeString(date: DateInput) {
@@ -93,6 +106,7 @@ export function toOmrsTimeString(date: DateInput) {
93
106
  }
94
107
 
95
108
  /**
109
+ * @deprecated use `formatDate(date, "wide")`
96
110
  * Formats the input as a date string using the format "DD - MMM - YYYY".
97
111
  */
98
112
  export function toOmrsDayDateFormat(date: DateInput) {
@@ -100,6 +114,7 @@ export function toOmrsDayDateFormat(date: DateInput) {
100
114
  }
101
115
 
102
116
  /**
117
+ * @deprecated use `formatDate(date, "no year")`
103
118
  * Formats the input as a date string using the format "DD-MMM".
104
119
  */
105
120
  export function toOmrsYearlessDateFormat(date: DateInput) {
@@ -107,8 +122,112 @@ export function toOmrsYearlessDateFormat(date: DateInput) {
107
122
  }
108
123
 
109
124
  /**
125
+ * @deprecated use `formatDate(date)`
110
126
  * Formats the input as a date string. By default the format "YYYY-MMM-DD" is used.
111
127
  */
112
128
  export function toOmrsDateFormat(date: DateInput, format = "YYYY-MMM-DD") {
113
129
  return dayjs(date).format(format);
114
130
  }
131
+
132
+ const DATE_FORMAT_YYYY_MMM_DD = {
133
+ year: "numeric",
134
+ month: "short",
135
+ day: "2-digit",
136
+ };
137
+ const DATE_FORMAT_YYYY_MMM = {
138
+ year: "numeric",
139
+ month: "short",
140
+ };
141
+ const DATE_FORMAT_MMM_DD = {
142
+ month: "short",
143
+ day: "2-digit",
144
+ };
145
+
146
+ export type FormatDateMode = "standard" | "no year" | "no day" | "wide";
147
+
148
+ /**
149
+ * Formats the input date according to the current locale and the
150
+ * given format mode.
151
+ *
152
+ * `standard`: "13 Dec 2021"
153
+ * `no year`: "13 Dec"
154
+ * `no day`: "Dec 2021"
155
+ * `wide`: "13 — Dec — 2021"
156
+ */
157
+ export function formatDate(date: Date, mode: FormatDateMode = "standard") {
158
+ const options = (
159
+ {
160
+ standard: DATE_FORMAT_YYYY_MMM_DD,
161
+ wide: DATE_FORMAT_YYYY_MMM_DD,
162
+ "no year": DATE_FORMAT_MMM_DD,
163
+ "no day": DATE_FORMAT_YYYY_MMM,
164
+ } as Record<string, Intl.DateTimeFormatOptions>
165
+ )[mode];
166
+ let locale = getLocale();
167
+ if (dayjs(date).isToday()) {
168
+ // This produces the word "Today" in the language of `locale`
169
+ const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
170
+ let localeString = rtf.format(0, "day");
171
+ localeString =
172
+ localeString[0].toLocaleUpperCase(locale) + localeString.slice(1);
173
+ return localeString;
174
+ } else {
175
+ if (locale == "en") {
176
+ // This locale override is here rather than in `getLocale`
177
+ // because Americans should see AM/PM for times.
178
+ locale = "en-GB";
179
+ }
180
+ let localeString = date.toLocaleDateString(locale, options);
181
+ if (locale == "en-GB" && mode == "standard") {
182
+ // Custom formatting for English. Use hyphens instead of spaces.
183
+ localeString = localeString.replace(/ /g, "-");
184
+ }
185
+ if (mode == "wide") {
186
+ localeString = localeString.replace(/ /g, " — "); // space-emdash-space
187
+ if (/ru.*/.test(locale)) {
188
+ // Remove the extra em-dash that gets added between the year and the suffix 'r.'
189
+ const len = localeString.length;
190
+ localeString =
191
+ localeString.slice(0, len - 5) +
192
+ localeString.slice(len - 5).replace(" — ", " ");
193
+ }
194
+ }
195
+ return localeString;
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Formats the input as a time, according to the current locale.
201
+ * 12-hour or 24-hour clock depends on locale.
202
+ */
203
+ export function formatTime(date: Date) {
204
+ return date.toLocaleTimeString(getLocale(), {
205
+ hour: "2-digit",
206
+ minute: "2-digit",
207
+ });
208
+ }
209
+
210
+ /**
211
+ * Formats the input into a string showing the date and time, according
212
+ * to the current locale. The `mode` parameter is as described for
213
+ * `formatDate`.
214
+ *
215
+ * This is created by concatenating the results of `formatDate`
216
+ * and `formatTime` with a comma and space. This agrees with the
217
+ * output of `Date.prototype.toLocaleString` for *most* locales.
218
+ */
219
+ export function formatDatetime(date: Date, mode: FormatDateMode = "standard") {
220
+ const dateString = formatDate(date, mode);
221
+ const timeString = formatTime(date);
222
+ return `${dateString}, ${timeString}`;
223
+ }
224
+
225
+ function getLocale() {
226
+ let language = window.i18next.language;
227
+ language = language.replace("_", "-"); // just in case
228
+ // hack for `ht` until https://unicode-org.atlassian.net/browse/CLDR-14956 is fixed
229
+ if (language === "ht") {
230
+ language = "fr-HT";
231
+ }
232
+ return language;
233
+ }