Skip to content

Commit

Permalink
re-use public code in server, add test
Browse files Browse the repository at this point in the history
  • Loading branch information
tsullivan committed Jul 10, 2020
1 parent 1e1d3c5 commit 1bebcc8
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ export class DateNanosFormat extends FieldFormat {
});
static fieldType = KBN_FIELD_TYPES.DATE;

private memoizedConverter: Function = noop;
private memoizedPattern: string = '';
private timeZone: string = '';
protected memoizedConverter: Function = noop;
protected memoizedPattern: string = '';
protected timeZone: string = '';

getParamDefaults() {
return {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { DateNanosFormat } from './date_nanos_server';
import { FieldFormatsGetConfigFn } from 'src/plugins/data/common';

describe('Date Nanos Format: Server side edition', () => {
let convert: Function;
let mockConfig: Record<string, any>;
let getConfig: FieldFormatsGetConfigFn;

const dateTime = '2019-05-05T14:04:56.201900001Z';

beforeEach(() => {
mockConfig = {};
mockConfig.dateNanosFormat = 'MMMM Do YYYY, HH:mm:ss.SSSSSSSSS';
mockConfig['dateFormat:tz'] = 'Browser';

getConfig = (key: string) => mockConfig[key];
});

test('should format according to the given timezone parameter', () => {
const dateNy = new DateNanosFormat({ timezone: 'America/New_York' }, getConfig);
convert = dateNy.convert.bind(dateNy);
expect(convert(dateTime)).toMatchInlineSnapshot(`"May 5th 2019, 10:04:56.201900001"`);

const datePhx = new DateNanosFormat({ timezone: 'America/Phoenix' }, getConfig);
convert = datePhx.convert.bind(datePhx);
expect(convert(dateTime)).toMatchInlineSnapshot(`"May 5th 2019, 07:04:56.201900001"`);
});

test('should format according to UTC if no timezone parameter is given or exists in settings', () => {
const utcFormat = 'May 5th 2019, 14:04:56.201900001';
const dateUtc = new DateNanosFormat({ timezone: 'UTC' }, getConfig);
convert = dateUtc.convert.bind(dateUtc);
expect(convert(dateTime)).toBe(utcFormat);

const dateDefault = new DateNanosFormat({}, getConfig);
convert = dateDefault.convert.bind(dateDefault);
expect(convert(dateTime)).toBe(utcFormat);
});

test('should format according to dateFormat:tz if the setting is not "Browser"', () => {
mockConfig['dateFormat:tz'] = 'America/Phoenix';

const date = new DateNanosFormat({}, getConfig);
convert = date.convert.bind(date);
expect(convert(dateTime)).toMatchInlineSnapshot(`"May 5th 2019, 07:04:56.201900001"`);
});

test('should defer to meta params for timezone, not the UI config', () => {
mockConfig['dateFormat:tz'] = 'America/Phoenix';

const date = new DateNanosFormat({ timezone: 'America/New_York' }, getConfig);
convert = date.convert.bind(date);
expect(convert(dateTime)).toMatchInlineSnapshot(`"May 5th 2019, 10:04:56.201900001"`);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,80 +17,17 @@
* under the License.
*/

import { i18n } from '@kbn/i18n';
import { memoize, noop } from 'lodash';
import moment, { Moment } from 'moment-timezone';
import { memoize } from 'lodash';
import moment from 'moment-timezone';
import {
FieldFormat,
FIELD_FORMAT_IDS,
KBN_FIELD_TYPES,
TextContextTypeConvert,
} from '../../../common';

/**
* Analyse the given moment.js format pattern for the fractional sec part (S,SS,SSS...)
* returning length, match, pattern and an escaped pattern, that excludes the fractional
* part when formatting with moment.js -> e.g. [SSS]
*/
export function analysePatternForFract(pattern: string) {
const fracSecMatch = pattern.match('S+') as any; // extract fractional seconds sub-pattern
const fracSecMatchStr = fracSecMatch ? fracSecMatch[0] : '';

return {
length: fracSecMatchStr.length,
patternNanos: fracSecMatchStr,
pattern,
patternEscaped: fracSecMatchStr ? pattern.replace(fracSecMatch, `[${fracSecMatch}]`) : '',
};
}

/**
* Format a given moment.js date object
* Since momentjs would loose the exact value for fractional seconds with a higher resolution than
* milliseconds, the fractional pattern is replaced by the fractional value of the raw timestamp
*/
export function formatWithNanos(
dateMomentObj: Moment,
valRaw: string,
fracPatternObj: Record<string, any>
) {
if (fracPatternObj.length <= 3) {
// S,SS,SSS is formatted correctly by moment.js
return dateMomentObj.format(fracPatternObj.pattern);
} else {
// Beyond SSS the precise value of the raw datetime string is used
const valFormatted = dateMomentObj.format(fracPatternObj.patternEscaped);
// Extract fractional value of ES formatted timestamp, zero pad if necessary:
// 2020-05-18T20:45:05.957Z -> 957000000
// 2020-05-18T20:45:05.957000123Z -> 957000123
// we do not need to take care of the year 10000 bug since max year of date_nanos is 2262
const valNanos = valRaw
.substr(20, valRaw.length - 21) // remove timezone(Z)
.padEnd(9, '0') // pad shorter fractionals
.substr(0, fracPatternObj.patternNanos.length);
return valFormatted.replace(fracPatternObj.patternNanos, valNanos);
}
}

export class DateNanosFormat extends FieldFormat {
static id = FIELD_FORMAT_IDS.DATE_NANOS;
static title = i18n.translate('data.fieldFormats.date_nanos.title', {
defaultMessage: 'Date nanos',
});
static fieldType = KBN_FIELD_TYPES.DATE;

private memoizedConverter: Function = noop;
private memoizedPattern: string = '';
private timeZone: string = '';

getParamDefaults() {
return {
pattern: this.getConfig!('dateNanosFormat'),
fallbackPattern: this.getConfig!('dateFormat'),
timezone: this.getConfig!('dateFormat:tz'),
};
}
analysePatternForFract,
DateNanosFormat,
formatWithNanos,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from 'src/plugins/data/public/field_formats/converters/date_nanos';
import { TextContextTypeConvert } from '../../../common';

class DateNanosFormatServer extends DateNanosFormat {
textConvert: TextContextTypeConvert = (val) => {
// don't give away our ref to converter so
// we can hot-swap when config changes
Expand Down Expand Up @@ -139,3 +76,5 @@ export class DateNanosFormat extends FieldFormat {
return this.memoizedConverter(val);
};
}

export { DateNanosFormatServer as DateNanosFormat };

0 comments on commit 1bebcc8

Please sign in to comment.