From: Bill Erickson Date: Wed, 21 Aug 2019 18:06:03 +0000 (-0400) Subject: LP1840782 locale/timezone aware date display formatter X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=8b439bb322d68c3a412129a6a0987b17876cf285;p=working%2FEvergreen.git LP1840782 locale/timezone aware date display formatter Adds a method to the DateUtil class which can generate a human-friendly, timezone and locale-aware display value for a date. It supports canned date and time formats, following the options described here (see dateStyle and timeStyle): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat Adds a tool to the sandbox for generating date strings based on locale, time zone, date format, and time format. As noted in the sandbox page and within the utility code, Firefox support for canned dateStyle and timeStyle values is pending: resolution of https://bugzilla.mozilla.org/show_bug.cgi?id=1557718. In the meantime, the code generates locale-aware "short" date and time values in Firefox. Signed-off-by: Bill Erickson --- diff --git a/Open-ILS/src/eg2/src/app/share/util/date.spec.ts b/Open-ILS/src/eg2/src/app/share/util/date.spec.ts index 2ef5a42b8a..6b048a2422 100644 --- a/Open-ILS/src/eg2/src/app/share/util/date.spec.ts +++ b/Open-ILS/src/eg2/src/app/share/util/date.spec.ts @@ -35,4 +35,15 @@ describe('DateUtil', () => { it('Cross walk YMD and ISO', () => { expect(DateUtil.isoToLocalYmd(DateUtil.ymdToLocalIso(ymd))).toBe(ymd); }); + + /* Intl API not yet supported on PhantomJs + it('Creates an cs-CZ short date display string', () => { + const formatted = DateUtil.dateToLocaleString(now, + {locale: 'cs-CZ', dateStyle: 'short', timeStyle: 'short'}); + + // cs-CZ short date/time example 21.08.19 20:17 + expect(formatted.match(/^\d\d\.\d\d\.\d\d \d\d:\d\d$/)).not.toBe(null); + }); + */ + }); diff --git a/Open-ILS/src/eg2/src/app/share/util/date.ts b/Open-ILS/src/eg2/src/app/share/util/date.ts index 6f2516fb5e..19f90f8d3e 100644 --- a/Open-ILS/src/eg2/src/app/share/util/date.ts +++ b/Open-ILS/src/eg2/src/app/share/util/date.ts @@ -1,5 +1,19 @@ /* Collection of date utility functions */ +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat +interface DateFormatOptions { + locale?: string; // defaults to browser locale + timeZone?: string; // defaults to browser time zone + + // Note dateStyle and timeStyle support a value of 'default' in addition + // to the documented options. + dateStyle?: string; + timeStyle?: string; +} + +const DEFAULT_DATE_STYLE = 'short'; // en-US => 8/21/19 +const DEFAULT_TIME_STYLE = 'short'; // en-US => 12:01 PM + export class DateUtil { // Returns a YYYY-MM-DD string in the local time zone matching @@ -39,4 +53,61 @@ export class DateUtil { if (!d) { return null; } return d.toISOString(); } + + + // Returns a locale-friendly display string for a date. + // To include the date in the output, set a value for options.dateStyle + // To include the time in the output, set a value for options.timeStyle + // If dateStyle or timeStyle have the value 'default', the application + // default format will be used. + // If both dateStyle and timeStyle are null, the fallback mode is + // dateStyle === 'default' + // For browsers where dateStyle and timeStyle are not supported, + // 'short' date / time styles are always used. (see inline comments). + static dateToLocaleString( + date: Date, options: DateFormatOptions): string { + + if (options.dateStyle === 'default') { + options.dateStyle = DEFAULT_DATE_STYLE; + } + + if (options.timeStyle === 'default') { + options.timeStyle = DEFAULT_DATE_STYLE; + } + + if (!options.dateStyle && !options.timeStyle) { + options.dateStyle = DEFAULT_DATE_STYLE; + } + + const locale = options.locale || null; // defaults to browser locale + delete options.locale; + + let formatter = new Intl.DateTimeFormat(locale, options); + + // Firefox support for dateStyle / timeStyle pending. + // https://bugzilla.mozilla.org/show_bug.cgi?id=1557718 + // For now, use numeric date / style formats, which are + // equivalent to 'short' dates and times (at least for the 9 + // locales provided w/ Evergreen by default -- presumably this + // is universal). TODO: remove this code once the feature is + // generally available in FF. + const resolved: any = formatter.resolvedOptions(); + + if (!resolved.dateStyle && !resolved.timeStyle) { // supported? + + const optionsFf: any = {timeZone: options.timeZone}; + + if (options.dateStyle) { + optionsFf.year = optionsFf.month = optionsFf.day = 'numeric'; + } + + if (options.timeStyle) { + optionsFf.hour = optionsFf.minute = 'numeric'; + } + + formatter = new Intl.DateTimeFormat(locale, optionsFf); + } + + return formatter.format(date); + } } diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html index adb01d3ec3..1944656a7f 100644 --- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html +++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html @@ -143,6 +143,55 @@ +
+
Date Display Generator -- Browser Time is {{dateNow}}
+
+ Note we only support 'short' date and time styles for Firefox, pending + resolution of + https://bugzilla.mozilla.org/show_bug.cgi?id=1557718. +
+
+
+ + +
+
+ + + + + + + + + + + + +
+
+ + + + + + + +
+
+ + + + + + + +
+
{{dateString}}
+
+
+ @@ -366,3 +415,4 @@ + diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts index c452e158ab..3e9d7a7853 100644 --- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts @@ -1,4 +1,3 @@ - import {timer as observableTimer, Observable, of} from 'rxjs'; import {Component, OnInit, ViewChild, Input, TemplateRef} from '@angular/core'; import {ProgressDialogComponent} from '@eg/share/dialog/progress.component'; @@ -19,6 +18,7 @@ import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component'; import {FormatService} from '@eg/core/format.service'; import {StringComponent} from '@eg/share/string/string.component'; import {GridComponent} from '@eg/share/grid/grid.component'; +import {DateUtil} from '@eg/share/util/date'; @Component({ templateUrl: 'sandbox.component.html' @@ -45,6 +45,13 @@ export class SandboxComponent implements OnInit { @ViewChild('bresvEditor') private bresvEditor: FmRecordEditorComponent; + localeCodes: ComboboxEntry[] = []; + dateLocale: string; + dateTimeZone: string; + dateStyle: string; + timeStyle: string; + dateString: string; + dateNow = new Date(); // @ViewChild('helloStr') private helloStr: StringComponent; @@ -251,6 +258,13 @@ export class SandboxComponent implements OnInit { b.cancel_time('2019-03-25T11:07:59-0400'); this.bresvEditor.mode = 'create'; this.bresvEditor.record = b; + + this.pcrud.retrieveAll('i18n_l').subscribe(locale => { + if (!this.dateLocale) { + this.dateLocale = locale.code(); + } + this.localeCodes.push({id: locale.code(), label: locale.code()}); + }); } sbChannelHandler = msg => { @@ -381,6 +395,17 @@ export class SandboxComponent implements OnInit { ); }); } + + setDateString() { + const options: any = {}; + + if (this.dateLocale) { options.locale = this.dateLocale; } + if (this.dateTimeZone) { options.timeZone = this.dateTimeZone; } + if (this.dateStyle) { options.dateStyle = this.dateStyle; } + if (this.timeStyle) { options.timeStyle = this.timeStyle; } + + this.dateString = DateUtil.dateToLocaleString(new Date(), options); + } }