Is there a specific pipe in Angular that displays dates in a relative time ago

My current project involves creating a website for sharing posts. One of the features I want to include is a date ago pipe in Angular.

    import {Pipe, PipeTransform} from 'angular2/core';

@Pipe({
  name: 'messageTime',
  pure: false
})
export class MessageTimePipe implements PipeTransform {
  transform(value: Date, []): string {
    var result: string;

    // current time
    let now = new Date().getTime();

    // calculate time difference in seconds
    let delta = (now - value.getTime()) / 1000;

    // format the output based on the time difference
    if (delta < 10) {
      result = 'just now';
    } else if (delta < 60) { 
      result = Math.floor(delta) + ' seconds ago';
    } else if (delta < 3600) { 
      result = Math.floor(delta / 60) + ' minutes ago';
    } else if (delta < 86400) { 
      result = Math.floor(delta / 3600) + ' hours ago';
    } else { 
      result = Math.floor(delta / 86400) + ' days ago';
    }

    return result;
  }enter code here
}

The above code snippet is what I have been working with in my project. However, it seems to be causing some issues and not functioning correctly.

Answer №1

To begin, let's start by creating a class for our pipe.

ng g p pipes/DateAgo

Next, we can insert the following code:

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
    name: 'dateAgo',
    pure: true
})
export class DateAgoPipe implements PipeTransform {

    transform(value: any, args?: any): any {
        if (value) {
            const seconds = Math.floor((+new Date() - +new Date(value)) / 1000);
            if (seconds < 29) // Show as 'Just now' if less than 30 seconds ago
                return 'Just now';
            const intervals: { [key: string]: number } = {
                'year': 31536000,
                'month': 2592000,
                'week': 604800,
                'day': 86400,
                'hour': 3600,
                'minute': 60,
                'second': 1
            };
            let counter;
            for (const i in intervals) {
                counter = Math.floor(seconds / intervals[i]);
                if (counter > 0)
                    if (counter === 1) {
                        return counter + ' ' + i + ' ago'; // singular (1 day ago)
                    } else {
                        return counter + ' ' + i + 's ago'; // plural (2 days ago)
                    }
            }
        }
        return value;
    }

}

Answer №2

In my opinion, it is more beneficial to utilize one of the available packages for implementing a "time ago" functionality rather than developing your own solution. This is due to various reasons such as quick implementation, ease of maintenance, addition of new features and fixes, among others.

  • ngx-timeago (my preferred choice)

    This package offers unique features that set it apart from others, including internationalization support and live updates to the time ago text.

  • time-ago-pipe

    This is a lightweight and straightforward 'pipe only' package, making it user-friendly.

  • ngx-moment

    I recommend using this package if you are already utilizing moment.js in your application. However, be cautious as it may contribute significantly to your bundle size.

  • Angular Intl

    This newer package seems promising with its internationalization support and the ability to customize the time ago description.

Answer №3

To create a custom pipe in Angular, use the following command: "ng g p customePipe/timeAgo"

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'timeAgo'
})
export class TimeAgoPipe implements PipeTransform {
  transform(value: string): string {
    const time = new Date(value);
    const now = new Date();
    const seconds = Math.floor((now.getTime() - time.getTime()) / 1000);

    if (seconds < 60) {
      return 'just now';
    } else if (seconds < 120) {
      return 'a minute ago';
    } else if (seconds < 3600) {
      return Math.floor(seconds / 60) + ' minutes ago';
    } else if (seconds < 7200) {
      return 'an hour ago';
    } else if (seconds < 86400) {
      return Math.floor(seconds / 3600) + ' hours ago';
    } else {
      return time.toLocaleString();
    }
  }
}

Answer №4

It's advisable to utilize moment in order to create a pipe. After extensive research and experimentation, I discovered this method.

To create a pipe:

ng g pipe pipes/timeAgo

Simply enter the following code. Only one line needs to be edited, the rest will be generated automatically.

import { Pipe, PipeTransform } from '@angular/core';
import moment from 'moment';

@Pipe({
  name: 'timeAgo'
})
export class TimeAgoPipe implements PipeTransform {

  transform(value: Date, ...args: unknown[]): unknown {
    return moment(value).fromNow(); // modify this line as needed
  }

}
<span class="timeago-label">{{'2023-07-10T06:00:00Z' | timeAgo}}</span>

Don't forget to install moment before proceeding:

npm i moment --save

Answer №5

Unfortunately, as of 2024, Moment is no longer supported and Angular Date pipes still do not have built-in support for this feature.

Here is the finalized version of my solution:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'timeAgo',
  standalone: true,
})
export class TimeAgoPipe implements PipeTransform {
  transform(value: string | Date): string {
    if (!value) {
      return '';
    }

    // handling both Date objects and string dates
    const date = typeof value === 'string' ? new Date(value) : value;

    if (!(date instanceof Date) || isNaN(date.getTime())) {
      return '';
    }

    const now = new Date();
    const diff = now.getTime() - date.getTime();

    const minute = 60 * 1000;
    const hour = 60 * minute;
    const day = 24 * hour;

    if (diff < minute) {
      return 'just now';
    } else if (diff < hour) {
      const minutes = Math.floor(diff / minute);
      return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;
    } else if (diff < day) {
      const hours = Math.floor(diff / hour);
      return `${hours} hour${hours > 1 ? 's' : ''} ago`;
    } else {
      const days = Math.floor(diff / day);
      return `${days} day${days > 1 ? 's' : ''} ago`;
    }
  }
}

Answer №6

If you're looking to dynamically update the time in real-time at specified intervals, I have integrated a solution on my website. You can incorporate it into any of the previous answers - just remember to set pure: false in the pipe's @Pipe decorator.

import {Directive, Input, OnDestroy} from '@angular/core';
import {interval, Subject, takeUntil} from 'rxjs';

@Directive({
  selector: '[dirUpdater]',
  standalone: true
})
export class UpdaterDirective implements OnDestroy {
  private destroy$ = new Subject<void>();
  private currentInterval: number = 60000; //ms

  @Input() set updateInterval(value: number) {
    this.currentInterval = value;
    this.resetInterval();
  }

  constructor() {
    this.resetInterval();
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }

  private resetInterval() {
    this.destroy$.next();
    interval(this.currentInterval)
      .pipe(takeUntil(this.destroy$))
      .subscribe(() => {});
  }
}

To use it, simply:

<span appTimeAgoUpdater updateInterval="1000">{{time | timeAgo}}</span>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issue with loading JSON dependencies in ionic version 2 webpack loader

We're currently in the process of transitioning an app prototype from ionic to ionic2, accomplished by duplicating the functionality of the ionic-conference-app, which is working smoothly on our local environment. Our next task involves creating a wr ...

Axios simulation: Total calls received: 0

Having trouble with my mock for the getAll method. The error I'm seeing is: Expected number of calls: 1 Received number of calls: 0 Here are the mock configurations I've set up: jest.mock("axios", () => { return { create: jes ...

What is the best way to transfer information from the window method to the data function in a Vue.js application?

Is there a way to transfer information from the window method to the data function in a vuejs component? Take a look at my window method: window.authenticate = function(pid, receiptKey) { console.log("Authentication"); console.log(this) localStorag ...

Using keyof to access static properties within TypeScript classes

While experimenting with this TypeScript playground code sample, I encountered an issue with defining the greeterBuilderName variable. How can I specify that I want properties of the Greeter function itself (such as prototype, warm_greeter, etc.) when keyo ...

Issue with Navigating Angular Buttons

In my Angular application, I have a mat-card component containing a list of buttons. Below is the code snippet: <mat-card class="side-card"> <mat-card-title>Images</mat-card-title> <mat-card-subtitle>Choose to star ...

Using TypeScript will result in the return type of a combined generic type always being unknown

I am designing a settings store system where users can add settings with values of any type and retrieve them with correct typing. To achieve this, I thought about utilizing connected generic type arguments. The idea is that the return type of the value sh ...

Typescript error: The property "Authorization" is not found in the type HeadersInit

As I utilize the npm module node-fetch, I have a helper function specifically designed to facilitate authorized requests to a third-party service. This function essentially acts as middleware by incorporating the Authorization header. async function makeAu ...

Enhance the appearance of JSON data in Angular 2 with the Prettify

I'm encountering an issue where my JSON is not being printed in a pretty formatted way and is returning with unnecessary \ characters. The solution I found on Plnkr works well there, but strangely doesn't work in my actual application. The ...

Working with base URL and relative paths in Angular 2 on GitHub Pages

While attempting to deploy my project site on gh-pages, I encountered an issue with relative paths in the templates of my Angular2 app that uses webpack. Despite setting the base URL to match my repository name, everything loads fine except for the paths w ...

Issues arising from TypeScript error regarding the absence of a property on an object

Having a STEPS_CONFIG object that contains various steps with different properties, including defaultValues, I encountered an issue while trying to access the defaultValues property from the currentStep object in TypeScript. The error message indicated tha ...

What is the best way to include the file name and size as query parameters in Node.js?

To retrieve an image from the folder, a query needs to be passed containing the filename and dimensions like this: localhost:3000/images?filename=myImage&width=100&height=100 The initial objective is to fetch images from the designated folder, res ...

What is the process for exporting a plugin from dayjs() in JavaScript?

Currently, I have incorporated the plugin isToday() to enhance the capabilities of dayjs(). Nevertheless, I am uncertain about how to export isToday() in order to utilize it across other files. import isToday from "dayjs/plugin/isToday"; expor ...

Tips for adjusting the dimensions of a child element to match its parent in Angular 12 with Typescript

I have included the child component in the parent component and I am displaying that child component within a col-md-8. What I want to achieve is to highlight a specific div in the child component with additional text, making it equal in size to the parent ...

Importing a third-party JavaScript library in Angular 2 RC5: Utilizing Showdown Library

Struggling with the integration of Showdown as a vendor in my project. Whenever I compile, the browser console throws an error saying showdown is not defined. Since it's a vendor package, importing it directly into app.module.ts doesn't seem to b ...

Using TypeScript - Implementing a generic constraint to allow passing a Zod schema result as an argument to a function

I'm in the process of creating a custom controller function to streamline my application. The repetitive task of wrapping try-catch, parsing a zod schema, and merging the request zod schema into a single object is present in all handler functions. The ...

Child routes in Angular tabs are failing to display in the currently active tab

Hey there! I'm currently working on navigating my Angular application using tabs, and I've run into a bit of an issue with the child routes. Specifically, when I switch to the second child route within the second tab, the status of the tab change ...

I'm curious, which ref tag should I utilize for draft.js?

I'm currently working on a form using Draft.js with Next.js and TS, but I've encountered some errors in my code. Here is what I have so far: import {Editor, EditorState} from 'draft-js'; const [editorState, setEditorState] = useState( ...

Encountering an issue in the tmp directory while deploying an Angular App with Gitlab CI

Hello there, I am currently facing a challenge while attempting to deploy my Angular application on Heroku through Gitlab CI. The issue revolves around an API key that is stored in the Angular's environment file. Despite creating the necessary folder ...

Eliminate the AM and PM options from the input time selection dropdown menu in HTML

https://i.sstatic.net/wKeQk.png Is it possible to eliminate the AM / PM option from the input time format dropdown? The form builder currently uses a 24-hour format that includes the AM / PM field. ...

Exploring the Google Drive API with Node

Currently, I am utilizing the Google Drive API in NodeJS. Below is the snippet of code I am using to download a file: try { const auth = await authenticate(); const drive = google.drive({ version: 'v3', auth }); drive.files.get( ...