Date and time formatting

In this guide we will learn how date and time formatting can be customized within SDK’s components.

SDK components displaying date & time

The following components provided by the SDK display datetime:

  • DateSeparator- component separating groups of messages in message lists
  • EventComponent - component that renders system messages (message.type === 'system')
  • Timestamp - component to display non-system message timestamp

Format customization

The datetime format customization can be done on multiple levels:

  1. Component prop values
  2. Supply custom formatting function
  3. Format date via i18n

Override the component props defaults

All the mentioned components accept timestamp formatter props:

export type TimestampFormatterOptions = {
  /* If true, call the `Day.js` calendar function to get the date string to display (e.g. "Yesterday at 3:58 PM"). */
  calendar?: boolean;
  /* Object specifying date display formats for dates formatted with calendar extension. Active only if calendar prop enabled. */
  calendarFormats?: Record<string, string>;
  /* Overrides the default timestamp format if calendar is disabled. */
  format?: string;
};

If calendar formatting is enabled, the dates are formatted with time-relative words (“yesterday at …”, “last …”). The calendar strings can be further customized with calendarFormats object. The calendarFormats object has to cover all the formatting cases as shows the example below:

{
  "lastDay": "[gestern um] LT",
  "lastWeek": "[letzten] dddd [um] LT",
  "nextDay": "[morgen um] LT",
  "nextWeek": "dddd [um] LT",
  "sameDay": "[heute um] LT",
  "sameElse": "L"
}

If any of the calendarFormats keys are missing, then the underlying library will fall back to hard-coded english equivalents

If calendar formatting is enabled, the format prop would be ignored. So to apply the format string, the calendar has to be disabled (applies to DateSeparator and MessageTimestamp).

All the components can be overridden through Channel component context:

import {
  Channel,
  DateSeparatorProps,
  DateSeparator,
  EventComponentProps,
  EventComponent,
  MessageTimestampProps,
  MessageTimestamp,
} from "stream-chat-react";

const CustomDateSeparator = (props: DateSeparatorProps) => (
  <DateSeparator {...props} calendar={false} format={"YYYY"} /> // calendar is enabled by default
);

const CustomSystemMessage = (props: EventComponentProps) => (
  <EventComponent {...props} format={"YYYY"} /> // calendar is disabled by default
);

const CustomMessageTimestamp = (props: MessageTimestampProps) => (
  <MessageTimestamp
    {...props}
    calendar={false}
    format={"YYYY-MM-DDTHH:mm:ss"}
  /> // calendar is enabled by default
);

<Channel
  DateSeparator={CustomDateSeparator}
  MessageSystem={SystemMessage}
  MessageTimestamp={CustomMessageTimestamp}
>
  ...
</Channel>;

Custom formatting function

Custom formatting function can be passed to MessageList or VirtualizedMessageList via prop formatDate ((date: Date) => string;). The Message component passes down the function to be consumed by the children via MessageComponentContext:

import { useMessageContext } from "stream-chat-react";
const CustomComponent = () => {
  const { formatDate } = useMessageContext();
};

By default, the function is consumed by the MessageTimestamp component. This means the formatting via formatDate is reduced only to timestamp shown by a message in the message list. Components DateSeparator, EventComponent would ignore the custom formatting.

Date & time formatting with i18n service

Until now, the datetime values could be customized within the Channel component at best. Formatting via i18n service allows for SDK wide configuration. The configuration is stored with other translations in JSON files. Formatting with i18n service has the following advantages:

  • it is centralized
  • it takes into consideration the locale out of the box
  • allows for high granularity - formatting per string, not component (opposed to props approach)
  • allows for high re-usability - apply the same configuration in multiple places via the same translation key
  • allows for custom formatting logic

Change the default configuration

The default datetime formatting configuration is stored in the JSON translation files. The default translation keys are namespaced with prefix timestamp/ followed by the component name. For example, the message date formatting can be targeted via timestamp/MessageTimestamp, because the underlying component is called MessageTimestamp.

You can change the default configuration by passing an object to translationsForLanguage Streami18n option with all or some of the relevant translation keys:

import { Chat, Streami18n } from "stream-chat-react";

const i18n = new Streami18n({
  language: "de",
  translationsForLanguage: {
    "timestamp/DateSeparator":
      "{{ timestamp | timestampFormatter(calendar: false) }}",
    "timestamp/MessageTimestamp":
      '{{ timestamp | timestampFormatter(calendar: true; calendarFormats: {"lastDay": "[gestern um] LT", "lastWeek": "[letzten] dddd [um] LT", "nextDay": "[morgen um] LT", "nextWeek": "dddd [um] LT", "sameDay": "[heute um] LT", "sameElse": "L"}) }}',
  },
});

const ChatApp = ({ chatClient, children }) => {
  return (
    <Chat client={chatClient} i18nInstance={i18n}>
      {children}
    </Chat>
  );
};
Understanding the formatting syntax

Once the default prop values are nullified, we override the default formatting rules. We can take a look at an example of German translation for SystemMessage (below a JSON example - note the escaped quotes):

"timestamp/SystemMessage": "{{ timestamp | timestampFormatter(calendar: true; calendarFormats: {\"lastDay\": \"[gestern um] LT\", \"lastWeek\": \"[letzten] dddd [um] LT\", \"nextDay\": \"[morgen um] LT\", \"nextWeek\": \"dddd [um] LT\", \"sameDay\": \"[heute um] LT\", \"sameElse\": \"L\"}) }}",

Let’s dissect the example:

  • The curly brackets ({{, }}) indicate the place where a value will be interpolated (inserted) into the string.
  • variable timestamp is the name of variable which value will be inserted into the string
  • value separator | signals the separation between the interpolated value and the formatting function name
  • timestampFormatter is the name of the formatting function that is used to convert the timestamp value into desired format
  • the timestampFormatter can be passed the same parameters as the React components (calendar, calendarFormats, format) as if the function was called with these values. The values can be simple scalar values as well as objects (note calendarFormats should be an object). The params should be separated by semicolon ;.

The described rules follow the formatting rules required by the i18n library used under the hood - i18next. You can learn more about the rules in the formatting section of the i18next documentation.

Custom datetime formatter functions

Besides overriding the configuration parameters, we can override the default timestampFormatter function by providing custom Streami18n instance:

import { Chat, Streami18n, useCreateChatClient } from "stream-chat-react";

const i18n = new Streami18n({
  formatters: {
    timestampFormatter: () => (val: string | Date) => {
      return new Date(val).getTime() + "";
    },
  },
});

export const ChatApp = ({ apiKey, userId, userToken }) => {
  const chatClient = useCreateChatClient({
    apiKey,
    tokenOrProvider: userToken,
    userData: { id: userId },
  });
  return <Chat client={chatClient} i18nInstance={i18n}></Chat>;
};
© Getstream.io, Inc. All Rights Reserved.