mirror of https://github.com/grafana/grafana
System: Date formating options (#27216)
* Add support for local time formats in graph panel * Enfore 24h format for backward compatibility * Use existing Intl.DateTimeFormatOptions * Pre-generate time scale, add tests * Move localTimeFormat, add local format to units * updated default fallback * #25602, use navigator.languages to enforce locale in formatting * Making options * Worked new system settings * things are working * Local browser time formats working * Support parsing dates in different formats * settings updated * Settings starting to work * Fixed graph issue * Logs fix * refactored settings a bit * Updated and name change * Progress * Changed config names * Updated * Updated * Updated test * Synced description * fixed ts issue * Added version notice * Ts fix * Updated heatmap and test * Updated snapshot * Updated * fixed ts issue * Fixes Co-authored-by: Alex Shpak <alex-shpak@users.noreply.github.com>pull/27431/head
parent
783391a861
commit
61bd33c241
@ -0,0 +1,19 @@ |
||||
import { localTimeFormat } from './formats'; |
||||
|
||||
describe('Date Formats', () => { |
||||
it('localTimeFormat', () => { |
||||
const format = localTimeFormat( |
||||
{ |
||||
year: '2-digit', |
||||
month: '2-digit', |
||||
day: '2-digit', |
||||
hour: '2-digit', |
||||
minute: '2-digit', |
||||
second: '2-digit', |
||||
}, |
||||
'' |
||||
); |
||||
|
||||
expect(format).toBe('MM/DD/YYYY, HH:mm:ss A'); |
||||
}); |
||||
}); |
||||
@ -1,2 +1,117 @@ |
||||
export const DEFAULT_DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; |
||||
export const MS_DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss.SSS'; |
||||
export interface SystemDateFormatSettings { |
||||
fullDate: string; |
||||
interval: { |
||||
second: string; |
||||
minute: string; |
||||
hour: string; |
||||
day: string; |
||||
month: string; |
||||
year: string; |
||||
}; |
||||
useBrowserLocale: boolean; |
||||
} |
||||
|
||||
const DEFAULT_SYSTEM_DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss'; |
||||
|
||||
export class SystemDateFormatsState { |
||||
fullDate = DEFAULT_SYSTEM_DATE_FORMAT; |
||||
interval = { |
||||
second: 'HH:mm:ss', |
||||
minute: 'HH:mm', |
||||
hour: 'MM/DD HH:mm', |
||||
day: 'MM/DD', |
||||
month: 'YYYY-MM', |
||||
year: 'YYYY', |
||||
}; |
||||
|
||||
update(settings: SystemDateFormatSettings) { |
||||
this.fullDate = settings.fullDate; |
||||
this.interval = settings.interval; |
||||
|
||||
if (settings.useBrowserLocale) { |
||||
this.useBrowserLocale(); |
||||
} |
||||
} |
||||
|
||||
get fullDateMS() { |
||||
// Add millisecond to seconds part
|
||||
return this.fullDate.replace('ss', 'ss.SSS'); |
||||
} |
||||
|
||||
useBrowserLocale() { |
||||
this.fullDate = localTimeFormat({ |
||||
year: 'numeric', |
||||
month: '2-digit', |
||||
day: '2-digit', |
||||
hour: '2-digit', |
||||
minute: '2-digit', |
||||
second: '2-digit', |
||||
}); |
||||
|
||||
this.interval.second = localTimeFormat( |
||||
{ hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }, |
||||
null, |
||||
this.interval.second |
||||
); |
||||
this.interval.minute = localTimeFormat( |
||||
{ hour: '2-digit', minute: '2-digit', hour12: false }, |
||||
null, |
||||
this.interval.minute |
||||
); |
||||
this.interval.hour = localTimeFormat( |
||||
{ month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hour12: false }, |
||||
null, |
||||
this.interval.hour |
||||
); |
||||
this.interval.day = localTimeFormat({ month: '2-digit', day: '2-digit', hour12: false }, null, this.interval.day); |
||||
this.interval.month = localTimeFormat( |
||||
{ year: 'numeric', month: '2-digit', hour12: false }, |
||||
null, |
||||
this.interval.month |
||||
); |
||||
} |
||||
|
||||
getTimeFieldUnit(useMsResolution?: boolean) { |
||||
return `time:${useMsResolution ? this.fullDateMS : this.fullDate}`; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* localTimeFormat helps to generate date formats for momentjs based on browser's locale |
||||
* |
||||
* @param locale browser locale, or default |
||||
* @param options DateTimeFormatOptions to format date |
||||
* @param fallback default format if Intl API is not present |
||||
*/ |
||||
export function localTimeFormat( |
||||
options: Intl.DateTimeFormatOptions, |
||||
locale?: string | string[] | null, |
||||
fallback?: string |
||||
): string { |
||||
if (!window.Intl) { |
||||
return fallback ?? DEFAULT_SYSTEM_DATE_FORMAT; |
||||
} |
||||
|
||||
if (!locale) { |
||||
locale = [...navigator.languages]; |
||||
} |
||||
|
||||
// https://momentjs.com/docs/#/displaying/format/
|
||||
const parts = new Intl.DateTimeFormat(locale, options).formatToParts(new Date()); |
||||
const mapping: { [key: string]: string } = { |
||||
year: 'YYYY', |
||||
month: 'MM', |
||||
day: 'DD', |
||||
hour: 'HH', |
||||
minute: 'mm', |
||||
second: 'ss', |
||||
weekday: 'ddd', |
||||
era: 'N', |
||||
dayPeriod: 'A', |
||||
timeZoneName: 'Z', |
||||
}; |
||||
|
||||
return parts.map(part => mapping[part.type] || part.value).join(''); |
||||
} |
||||
|
||||
export const systemDateFormats = new SystemDateFormatsState(); |
||||
|
||||
@ -0,0 +1,25 @@ |
||||
import { dateTimeParse } from './parser'; |
||||
import { systemDateFormats } from './formats'; |
||||
|
||||
describe('dateTimeParse', () => { |
||||
it('should be able to parse using default format', () => { |
||||
const date = dateTimeParse('2020-03-02 15:00:22', { timeZone: 'utc' }); |
||||
expect(date.format()).toEqual('2020-03-02T15:00:22Z'); |
||||
}); |
||||
|
||||
it('should be able to parse using default format', () => { |
||||
systemDateFormats.update({ |
||||
fullDate: 'MMMM D, YYYY, h:mm:ss a', |
||||
interval: {} as any, |
||||
useBrowserLocale: false, |
||||
}); |
||||
|
||||
const date = dateTimeParse('Aug 20, 2020 10:30:20 am', { timeZone: 'utc' }); |
||||
expect(date.format()).toEqual('2020-08-20T10:30:20Z'); |
||||
}); |
||||
|
||||
it('should be able to parse array formats used by calendar', () => { |
||||
const date = dateTimeParse([2020, 5, 10, 10, 30, 20], { timeZone: 'utc' }); |
||||
expect(date.format()).toEqual('2020-06-10T10:30:20Z'); |
||||
}); |
||||
}); |
||||
@ -0,0 +1,28 @@ |
||||
package setting |
||||
|
||||
type DateFormats struct { |
||||
FullDate string `json:"fullDate"` |
||||
UseBrowserLocale bool `json:"useBrowserLocale"` |
||||
Interval DateFormatIntervals `json:"interval"` |
||||
} |
||||
|
||||
type DateFormatIntervals struct { |
||||
Second string `json:"second"` |
||||
Minute string `json:"minute"` |
||||
Hour string `json:"hour"` |
||||
Day string `json:"day"` |
||||
Month string `json:"month"` |
||||
Year string `json:"year"` |
||||
} |
||||
|
||||
func (cfg *Cfg) readDateFormats() { |
||||
dateFormats := cfg.Raw.Section("date_formats") |
||||
cfg.DateFormats.FullDate, _ = valueAsString(dateFormats, "full_date", "YYYY-MM-DD HH:mm:ss") |
||||
cfg.DateFormats.Interval.Second, _ = valueAsString(dateFormats, "interval_second", "HH:mm:ss") |
||||
cfg.DateFormats.Interval.Minute, _ = valueAsString(dateFormats, "interval_minute", "HH:mm") |
||||
cfg.DateFormats.Interval.Hour, _ = valueAsString(dateFormats, "interval_hour", "MM-DD HH:mm") |
||||
cfg.DateFormats.Interval.Day, _ = valueAsString(dateFormats, "interval_day", "YYYY-MM-DD") |
||||
cfg.DateFormats.Interval.Month, _ = valueAsString(dateFormats, "interval_month", "YYYY-MM") |
||||
cfg.DateFormats.Interval.Year = "YYYY" |
||||
cfg.DateFormats.UseBrowserLocale = dateFormats.Key("date_format_use_browser_locale").MustBool(false) |
||||
} |
||||
Loading…
Reference in new issue