Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
feat(duration-formatter): Adds formatting modes and improves output c…
Browse files Browse the repository at this point in the history
…onfiguration.

Fixes #444

BREAKING CHANGE: Removed experiementalFormatTime and
DtExperimentalFormatTime in favor of dtDuration pipe.
  • Loading branch information
rowa-audil committed Mar 12, 2020
1 parent fe2110d commit f034574
Show file tree
Hide file tree
Showing 20 changed files with 1,522 additions and 4 deletions.
6 changes: 4 additions & 2 deletions apps/dev/src/formatters/formatters-demo.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ <h2>Rate</h2>
<p>per second: {{ exampleValue | dtCount | dtRate: 's' }}</p>
<p>Chaining rate + bytes: {{ exampleValue | dtRate: 's' | dtBytes }}</p>
<p>Chaining bytes + rate: {{ exampleValue | dtBytes | dtRate: 's' }}</p>
<h2>Duration</h2>
<p>Default: {{ exampleValue | dtDuration: 'DEFAULT' }}</p>
<p>
InputUnit(Day -> Second): Time Formatter: {{ exampleValue | dtTime: 'd':'s' }}
Format: precise & chaining duration + rate:
{{ exampleValue | dtDuration: 'PRECISE' | dtRate: 'min' }}
</p>
<p>InputUnit(Ms): Time Formatter: {{ exampleValue | dtTime }}</p>
32 changes: 32 additions & 0 deletions libs/barista-components/formatters/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ The `dtTime` pipe provides a way to format a input time to a timestamp

<ba-live-example name="DtExampleFormattersTime"></ba-live-example>

### Duration

The `dtDuration` pipe provides a way to format an input time to a timestamp

<ba-live-example name="DtExampleFormattersDuration"></ba-live-example>

## Util functions

Since pipes are only used within templates we provide util functions that can be
Expand Down Expand Up @@ -149,6 +155,32 @@ the lower limit.
| `inputUnit` | `DtTimeUnit` | `undefined` | Which timeunit is used for the input |
| `toUnit` | `DtTimeUnit` | `undefined` | Which timeunit is the smallest possible output (Pipe disregards toUnit when unit is bigger than the inputUnit) |

### Duration

The `formatDuration` function converts a number to a duration string and
consumes a formatMethod which configures how the output is built.

- **'DEFAULT':** will look for the first unit that has a value and will only
print the next two descending units as long as they have values. The results
for each time unit will be rounded to a decimal number.
- **'PRECISE':** will only print the unit that it consumed or was set as the
outputUnit. The output value can be real numbers. (e.g. 1.54 s)
- **Custom/Number(1-n):** will tell the formatter to print a custom amount of
units.

You can specify the following properties on your options:

| Name | Type | Default | Description |
| -------------- | ------------------------------- | -------------- | ------------------------------------------------ |
| `input` | `number` | | Numeric value to be transformed |
| `formatMethod` | `'DEFAULT | 'PRECISE' | number` | `'DEFAULT'` | Formatting/Precision mode configuring the output |
| `outputUnit` | `DtTimeUnit` | `undefined` | Which unit to transform the input to |
| `inputUnit` | `DtTimeUnit` | `Milliseconds` | Which timeunit is used for the input |

#### Examples

<ba-live-example name="DtExampleFormattersDuration"></ba-live-example>

## Special uses (e.g. infographics, tiles)

It is possible to display (and style) value and unit separately - just use
Expand Down
2 changes: 2 additions & 0 deletions libs/barista-components/formatters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export { formatRate } from './src/rate/rate-formatter';
export * from './src/rate/rate';
export * from './src/bits/bits-formatter';
export * from './src/bits/bits';
export * from './src/duration/duration';
export * from './src/duration/duration-formatter';
export {
formatTime as experimentalFormatTime,
DtTime as DtExperimentalFormatTime,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* @license
* Copyright 2020 Dynatrace LLC
* Licensed 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 { DtTimeUnit } from '../unit';

export type DurationMode = 'DEFAULT' | 'PRECISE' | number;

export function toDurationMode(
formatMethod: string | number,
): DurationMode | undefined {
if (formatMethod === 'DEFAULT') {
return 'DEFAULT';
} else if (formatMethod === 'PRECISE') {
return 'PRECISE';
} else if (typeof formatMethod === 'number') {
return formatMethod;
}
}

// tslint:disable: no-magic-numbers
/** Factorials needed for converting milliseconds to other time units */
export const CONVERSION_FACTORS_TO_MS = new Map<DtTimeUnit, number>([
[DtTimeUnit.YEAR, 12 * 30.41666 * 24 * 60 * 60 * 1000],
[DtTimeUnit.MONTH, 30.41666 * 24 * 60 * 60 * 1000],
[DtTimeUnit.DAY, 24 * 60 * 60 * 1000],
[DtTimeUnit.HOUR, 60 * 60 * 1000],
[DtTimeUnit.MINUTE, 60 * 1000],
[DtTimeUnit.SECOND, 1000],
[DtTimeUnit.MILLISECOND, 1],
[DtTimeUnit.MICROSECOND, 1000], // Has to be handled differently because IEEE can't handle floating point numbers very well
[DtTimeUnit.NANOSECOND, 1], // Has to be handled differently because IEEE can't handle floating point numbers very well
]);

/** Default for the conversionunit when no formatmethod is passed as a number. */
export const CONVERSIONUNITS = 3;

/** Use when converting a duration to micro- or nanoseconds */
export const MOVE_COMMA = 1000000;
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* @license
* Copyright 2020 Dynatrace LLC
* Licensed 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 { DtTimeUnit } from '../../unit';
import {
CONVERSION_FACTORS_TO_MS,
MOVE_COMMA,
} from '../duration-formatter-constants';

/**
* Converts any duration to milliseconds
* @param duration numeric time value
* @param inputUnit dtTimeUnit value describing which unit the duration is in
*/
export function dtConvertToMilliseconds(
duration: number,
inputUnit: DtTimeUnit,
): number | undefined {
if (duration >= 0) {
return inputUnit === DtTimeUnit.MICROSECOND ||
inputUnit === DtTimeUnit.NANOSECOND
? (duration * CONVERSION_FACTORS_TO_MS.get(inputUnit)!) / MOVE_COMMA
: duration * CONVERSION_FACTORS_TO_MS.get(inputUnit)!;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* @license
* Copyright 2020 Dynatrace LLC
* Licensed 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.
*/

export * from './convert-to-milliseconds';
export * from './transform-result';
export * from './transform-result-precise';
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* @license
* Copyright 2020 Dynatrace LLC
* Licensed 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 { DtTimeUnit } from '../../unit';
import {
CONVERSION_FACTORS_TO_MS,
DurationMode,
MOVE_COMMA,
} from '../duration-formatter-constants';
import { dtConvertToMilliseconds } from './convert-to-milliseconds';

/**
* Calculates the duration precisely. Will convert duration to the inputUnit or to the outputUnit if set. (floating point number for its corelated unit)
* @param duration numeric time value
* @param inputUnit dtTimeUnit value describing which unit the duration is in
* @param outputUnit dtTimeUnit | undefined value describing the unit to which it should format
* @param formatMethod the formatting method
*/

export function dtTransformResultPrecise(
duration: number,
inputUnit: DtTimeUnit,
outputUnit: DtTimeUnit | undefined,
formatMethod: DurationMode,
): Map<DtTimeUnit, string> | undefined {
const amount =
inputUnit === DtTimeUnit.MILLISECOND
? duration
: dtConvertToMilliseconds(duration, inputUnit);
return outputUnit !== undefined
? calcResult(amount!, formatMethod, outputUnit)
: calcResult(amount!, formatMethod, inputUnit);
}

function calcResult(
amount: number,
formatMethod: DurationMode,
unit: DtTimeUnit,
): Map<DtTimeUnit, string> {
let result = new Map<DtTimeUnit, string>();
if (formatMethod === 'PRECISE') {
amount = amount / CONVERSION_FACTORS_TO_MS.get(unit)!;
// Need to move the comma since IEEE can't handle floating point numbers very well.
if (unit === DtTimeUnit.MICROSECOND || unit === DtTimeUnit.NANOSECOND) {
amount *= MOVE_COMMA;
}
result.set(unit, amount.toString());
} else {
amount = Math.trunc(amount / CONVERSION_FACTORS_TO_MS.get(unit)!);
amount < 1 ? result.set(unit, '< 1') : result.set(unit, amount.toString());
}
return result;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* @license
* Copyright 2020 Dynatrace LLC
* Licensed 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 { DtTimeUnit } from '../../unit';
import {
CONVERSION_FACTORS_TO_MS,
CONVERSIONUNITS,
DurationMode,
MOVE_COMMA,
} from '../duration-formatter-constants';

/**
* Calculates output duration in either "DEFAULT" or "CUSTOM" mode.
* If precision is DEFAULT then displays a maximum of three units, but
* if precision is a number, then displays that amount of units.
* @param duration numeric time value
* @param inputUnit dtTimeUnit value describing which unit the duration is in
* @param formatMethod the formatting method
*/
export function dtTransformResult(
duration: number,
inputUnit: DtTimeUnit,
formatMethod: DurationMode,
): Map<DtTimeUnit, string> | undefined {
const result = new Map<DtTimeUnit, string>();
const unitsToDisplay =
typeof formatMethod === 'number' ? formatMethod : CONVERSIONUNITS;
let rest = duration * CONVERSION_FACTORS_TO_MS.get(inputUnit)!;
let displayedUnits = 0;

for (const key of Array.from(CONVERSION_FACTORS_TO_MS.keys())) {
if (key === DtTimeUnit.MICROSECOND) {
rest = Math.round(rest * MOVE_COMMA); // handles IEEE floating point number problem
}
const amount = Math.trunc(rest / CONVERSION_FACTORS_TO_MS.get(key)!);
if (displayedUnits < unitsToDisplay) {
if (amount > 0) {
result.set(key, amount.toString());
// Only increase when a unit with a value bigger than 0 exists
displayedUnits++;
} else if (displayedUnits > 0) {
// Only increase when a unit with a value is already set
displayedUnits++;
}
}
rest = rest - amount * CONVERSION_FACTORS_TO_MS.get(key)!;
}
return result;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* @license
* Copyright 2020 Dynatrace LLC
* Licensed 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 { DtFormattedValue, NO_DATA, SourceData } from '../formatted-value';
import { DtTimeUnit } from '../unit';
import {
DurationMode,
CONVERSION_FACTORS_TO_MS,
} from './duration-formatter-constants';
import {
dtTransformResultPrecise,
dtTransformResult,
} from './duration-formatter-utils';

/**
* Formats a numeric value to a duration string
* @param duration numeric time value
* @param formatMethod the formatting method
* @param outputUnit dtTimeUnit | undefined value describing the unit to which it should format e.g to seconds
* @param inputUnit dtTimeUnit value describing which unit the duration is in (default: milliseconds)
*/
export function formatDuration(
duration: number,
formatMethod: DurationMode = 'DEFAULT',
outputUnit?: DtTimeUnit,
inputUnit: DtTimeUnit = DtTimeUnit.MILLISECOND,
): DtFormattedValue | string {
const inputData: SourceData = {
input: duration,
unit: inputUnit,
};
if (duration <= 0 && formatMethod === 'DEFAULT') {
return new DtFormattedValue(inputData, {
transformedValue: duration,
displayValue: '< 1',
displayUnit: inputUnit,
displayWhiteSpace: false,
});
}
const result =
outputUnit || formatMethod === 'PRECISE'
? dtTransformResultPrecise(duration, inputUnit, outputUnit, formatMethod)
: dtTransformResult(duration, inputUnit, formatMethod);

// Return NO_DATA when inputUnit is invalid
if (CONVERSION_FACTORS_TO_MS.get(inputUnit) === undefined) {
return NO_DATA;
}
if (result === undefined) {
return NO_DATA;
}
let resultString = '';
result.forEach((value, key) => {
resultString = `${resultString}${value} ${key} `;
});
return new DtFormattedValue(inputData, {
transformedValue: inputData.input,
displayValue: resultString.trim(),
displayWhiteSpace: false,
});
}
Loading

0 comments on commit f034574

Please sign in to comment.