packages/@fbcmobile-ui/Utils/DateUtils.js (95 lines of code) (raw):

/** * Copyright (c) Facebook, Inc. and its affiliates. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. * * @flow strict-local * @format */ 'use strict'; import 'moment/min/locales'; import * as React from 'react'; import fbt from 'fbt'; import moment from 'moment/min/moment-with-locales.min.js'; /** * Checks if the device locale is available in moment and returns the locale * to be loaded * @param {string} locale locale to be checked * @return {string} locale to be loaded by moment */ export function chooseMomentLocale(locale: string): string { // make the locale lower case // will fix crashes caused by "en-GB" (instead of "en-gb") not being found const localeLowerCase = locale.toLowerCase(); if (moment.locales().includes(localeLowerCase)) { // check if the locale is included in the array returned by `locales()` // which (in this case) tells us which locales moment will support return localeLowerCase; } else if (moment.locales().includes(localeLowerCase.substring(0, 2))) { // check if the first two letters of the locale are included in the array // returned by `locales()` which (in this case) tells us which locales // moment will support // will fixes crashes caused by "en-US" not being found, as we'll tell // moment to load "en" instead return localeLowerCase.substring(0, 2); } // use "en" (the default language and locale for my app) as // a fallback if we can't find any other locale return 'en'; } export function getReadableDateString( date: moment$Moment, locale: string, ): React.Node { const today = moment(); const tomorrow = moment(today).add(1, 'd'); if (today.isSame(date, 'day')) { return <fbt desc="Date label shown as today">Today</fbt>; } else if (tomorrow.isSame(date, 'day')) { return <fbt desc="Date label shown as tomorrow">Tomorrow</fbt>; } return date.locale(locale).format('MMM DD'); } export function getClosestFutureDayToToday( dates: Array<moment$Moment>, jsLocale: string, ): ?moment$Moment { const todayMoment = moment() .locale(jsLocale) .startOf('day'); const closestMoment = dates .map<moment$Moment>(date => moment(date) .locale(jsLocale) .startOf('day'), ) .filter(moment => moment.isSameOrAfter(todayMoment, 'days')) .reduce((closestMoment, currentMoment) => { if (closestMoment == null) { return currentMoment; } if ( currentMoment.diff(todayMoment, 'days') < closestMoment.diff(todayMoment, 'days') ) { return currentMoment; } return closestMoment; }, null); return closestMoment; } export function getSimpleDateString(date: moment$Moment, locale: string) { return date.locale(locale).format('MM DD YYYY'); } export function getLocaleDateTimeString(date: moment$Moment, locale: string) { return date.locale(locale).format('L LT'); } // TODO: Support translations export function timeSince( date: moment$Moment, locale: string, currentTime?: moment$Moment, ) { const now = currentTime ? currentTime : moment().locale(locale); const intervals = { years: {diff: now.diff(date, 'years'), unit: 'y'}, weeks: {diff: now.diff(date, 'weeks'), unit: 'w'}, days: {diff: now.diff(date, 'days'), unit: 'd'}, hours: {diff: now.diff(date, 'hours'), unit: 'h'}, minutes: {diff: now.diff(date, 'minutes'), unit: 'm'}, seconds: {diff: now.diff(date, 'seconds'), unit: 's'}, }; let intervalType = null; if (intervals.years.diff >= 1) { intervalType = 'years'; } else if (intervals.weeks.diff >= 1) { intervalType = 'weeks'; } else if (intervals.days.diff >= 1) { intervalType = 'days'; } else if (intervals.hours.diff >= 1) { intervalType = 'hours'; } else if (intervals.minutes.diff >= 1) { intervalType = 'minutes'; } else if (intervals.seconds.diff >= 1) { intervalType = 'seconds'; } if (intervalType === null) { return 'now'; } const interval = intervals[intervalType]; return `${interval.diff}${interval.unit}`; }