Best approach to express duration?
LukasUrban opened this issue · 18 comments
Lets say I want to return time taken by walking from point A to point B in minutes
1 minute
5 minutes
Is simple pluralization best practice, or is there any better approach?
react-intl supports relative time, more information here: http://formatjs.io/react/#formatted-relative
Currently the relative time formatting always evaluates the date against Date.now()
. I'm curious to hear more about the data structure you have and the values you have for A
and B
as it will help us determine how we might add support for specifying a reference date other than "now" to compare to.
Current structure is based on start and stop (target) locations. Duration property is integer representing time between these two points in minutes.
route = {
start:{station:'Kačerov', type:'metro metro-c'},
stop:{station:'Tempo', type:'bus'},
duration:7
}
Link to the case: http://fluid.cz/clients/libere/kontakt/react/production.html#/pobocky/praha_4 ("JAK SE K NÁM DOSTANETE" section)
Is there a unit
property to accompany the duration
value?
Nope, I just want to hardcode it to show minutes
Okay, so in this case we don't currently have a convenient way to access the term "minute(s)" in all the locales, even though we have the data and it backs our relative time formatting feature.
If you only need to support a few languages for this, then I would recommend treating as a translated message and using the <FormattedMessage>
component:
<FormattedMessage
message="{duration, plural, one{# minute} other{# minutes}}"
duration={route.duration} />
This will give you the pluralization of minute/minutes based on the duration value. If you're supporting more than one language, extract this message out of your React components into a messages
collection that's passed into the root React component (using the Intl Mixin).
And if unit
property would be present?
@ericf Have you had to deal with displaying hour/minute duration between two datetimes?
Something like 1h 30m
or 1 hour and 30 minutes
.
I'm also trying to solve this. Trying to display the duration of two dates. My implementation is really ugly at the moment something like this would be nice:
<FormattedRelative value={endDate} valueFrom={startDate} units="years" precision="2" />
1 year and 2 months ago
My current implementation:
renderDuration() {
var startDate = this.props.experience.get('start_date');
var endDate = this.props.experience.get('end_date') || moment();
var duration = moment.duration(endDate.diff(startDate));
var self = this;
var renderAnd = function() {
if (duration.years() < 1) { return false; }
return (
<span>
<FormattedMessage message={self.getIntlMessage('and')} />
</span>
);
};
return (
<span>
<FormattedMessage
message={this.getIntlMessage('duration_years')}
years={duration.years()} />
{renderAnd()}
<FormattedMessage
message={this.getIntlMessage('duration_months')}
months={duration.months()} />
</span>
);
}
{
"and":"'and",
"duration_years": "{years, plural, =0 {} =1 {1 year} other {# years}}",
"duration_months": "{months, plural, =0 {} =1 {1 month} other {# months}}"
}
PR #94 adds a now
prop to the <FormattedRelative>
component. While this is not exactly what this thread is about, it is related.
Hi everyone,
I'm trying to understand what i should include and what is the right component signature to turn an integer into a HH:mm:ss representation.
I.e.:
42 --> 42
120 --> 2:00
3695 --> 1:00:59
@nicolabortignon This is not currently supported and there's no built-in duration/unit formatting API in ECMAScript. There is some discussion around creating Intl.DurationFormat
: tc39/ecma402#32
Once this feature is officially proposed, work could begin on a polyfill, and I'd be happy to support adding formatDuration()
and <FormattedDuration>
to React Intl.
Today, as stated above in this thread, relative time formatting is the closest feature to durations; but it doesn't sound like the thing you're looking for.
Intl.DurationFormat
is the right solution. Closing this…
Here's the correct link for Intl.DurationFormat
: tc39/ecma402#47
I know this is quite old, but I stumbled upon it recently... I've created this https://github.com/en-japan-air/react-intl-formatted-duration it'll help until the ECMAScript API is implemented
I had the same issue today, get minutes as props and need to render minutes and hours.
export interface FormattedDurationProps {
minutes: number;
}
export const FormattedDuration = (props: FormattedDurationProps) => {
const durationHours = Math.floor(props.minutes / 60);
const durationMinutes = props.minutes % 60;
const renderHours = (hours: number) => (
<FormattedMessage
id="shift.durationHours"
defaultMessage="{value,plural,=0{}one{1 Hour}other{# Hours}}"
values={{ value: hours }}
/>
);
const renderMinutes = (minutes: number) => (
<FormattedMessage
id="shift.durationMinutes"
defaultMessage="{value,plural,=0{}one{1 Minute}other{# Minutes}}"
values={{ value: minutes }}
/>
);
if (durationHours && !durationMinutes) {
return renderHours(durationHours);
}
if (!durationHours && durationMinutes) {
return renderMinutes(durationMinutes);
}
return (
<Fragment>
{renderHours(durationHours)}
<span> </span>
{renderMinutes(durationMinutes)}
</Fragment>
);
};
Leave it here just in case anyone need simple FormattedDuration component
If you're still looking for a workaround, I created this gist for you. There's no magic there! just a wrapper around Intl
. Hope this helps.
import React from 'react';
import { useIntl, defineMessages } from 'react-intl';
import PropTypes from 'prop-types';
const FormattedDuration = ({ value, children, unitDisplay, ...props }) => {
const durationHours = Math.floor(value / 60);
const durationMinutes = value % 60;
const Intl = useIntl();
const renderHours = (hours) =>
Intl.formatNumber(hours, {
style: 'unit',
unit: 'hour',
unitDisplay,
});
const renderMinutes = (minutes) =>
Intl.formatNumber(minutes, {
style: 'unit',
unit: 'minute',
unitDisplay,
});
if (durationHours <= 0 && durationMinutes <= 0) {
return children(renderMinutes(durationMinutes));
}
if (durationHours <= 0 && durationMinutes > 0) {
return children(renderMinutes(durationMinutes));
}
if (durationHours > 0 && durationMinutes <= 0) {
return children(renderHours(durationHours));
}
const messageAnd = defineMessages({
and: {
id: 'and',
defaultMessage: 'and',
description: 'Used between durations',
},
});
return children(
`${renderHours(durationHours)} ${Intl.formatMessage(
messageAnd.and
)} ${renderMinutes(durationMinutes)}`
);
};
FormattedDuration.propTypes = {
value: PropTypes.number.isRequired,
unitDisplay: PropTypes.oneOf(['long', 'narrow', 'short']),
};
FormattedDuration.defaultProps = {
unitDisplay: 'long',
};
export default FormattedDuration;