skip to Main Content

I am working on a project in react native with a firestore database. I have stored articles with a timestamp that I want to filter when i fetch the data from the database.

const getLoads = async (e) => {
    const today = getCurrentSeconds();
    console.log(today);
    await getDocs(collection(db, 'load'))
    .then((QuerySnapshot) => {
        const newData = QuerySnapshot.docs
            .filter((item) => item.date >= today)
            .map((doc) => ({...doc.data(), id:doc.id }));
            setLoad(newData);
            console.log(load, newData);
    });
    setIsLoading(false);
}

As getCurrentSeconds() returns seconds and the firestore timestamp is in the format of seconds and nanoseconds.
I’m guessing that the comparison won’t do anything as they’re in nanoseconds as well and I haven’t found a good way to acquire this. What I want to do is just compare the seconds.

How do I make the comparison correctly?

This is what the date from the firebase looks like:

picture from console

date: it {seconds: 1684748618, nanoseconds: 863000000}

I tried calling a function that returns todays date and then compare that to the timestamp I get from firestore:

The function that is supposed to return todays date:

/*
*   Returns todays date 
*/
export const getCurrentDate=() => {
    var date = new Date().getDate();
    var month = new Date().getMonth() + 1;
    var year = new Date().getFullYear();

    return date + '/' + month;
}

export const getCurrentSeconds=() => {
    var now = Date.now();
    return now;
}

the getCurrentSeconds() returns 1684829176108.

the timestamp from the firestore is: 1684748618 which is three digits shorter than the getCurrentSeconds().

2

Answers


  1. You probably could try this method…assuming that the "date" field in your Firestore documents is also a timestamp in seconds, you can modify your filter condition to correctly compare the values. Here’s an updated version of your code:

    const getLoads = async (e) => {
      const today = getCurrentSeconds();
      console.log(today);
      await getDocs(collection(db, 'load'))
        .then((querySnapshot) => {
          const newData = querySnapshot.docs
            .filter((doc) => doc.data().date.seconds >= today)
            .map((doc) => ({ ...doc.data(), id: doc.id }));
          setLoad(newData);
          console.log(load, newData);
        });
      setIsLoading(false);
    };
    

    In the updated code, the filter() function is used to check if the "date.seconds" property of each document is greater than or equal to the current timestamp in seconds (today). By accessing the "seconds" property of the Firestore timestamp, you ensure that you are comparing the correct values.

    Login or Signup to reply.
  2. To begin with, Date.now() returns the number of milliseconds since the epoch. This means that your current getCurrentSeconds() method is returning milliseconds instead of seconds.

    export const getCurrentSeconds=() => {
        return Math.floor(Date.now() / 1000);
    }
    

    Next, QuerySnapshot.docs is an array of QueryDocumentSnapshot objects. These objects do not have a date property, so item.date in your callback will always return undefined. This effectively makes your callback .filter(item => undefined > today) which will return false in all cases. To correctly extract the date field from your document, you need to use item.get("date") or item.data().date in your callback.

    const getLoads = async (e) => {
      const todaySeconds = getCurrentSeconds();
      console.log(today);
    
      const querySnapshot = await getDocs(collection(db, 'load'));
      const newData = querySnapshot.docs
        .filter((doc) => doc.get("date").seconds >= todaySeconds) // use doc/docSnap instead of item
        .map((doc) => ({ ...doc.data(), id: doc.id }));
    
      setLoad(newData);
      setIsLoading(false);
      console.log(load, newData);
    };
    

    Next we need to address the logic error in your function where you set today to the current number of seconds since the epoch. This will mean that unless your database contains data for dates in the future, it will always return no results. If you instead want to get all the data since midnight today, you need to introduce a getTodayMidnightSeconds() function.

    // NOTE: Uses user's local timezone
    export const getTodayMidnightSeconds=() => {
        const dt = new Date();
        const ms = dt.setHours(0, 0, 0, 0); // setHours returns the new timestamp in milliseconds
        return Math.floor(ms / 1000);
    }
    
    const getLoads = async (e) => {
      const todaySeconds = getTodayMidnightSeconds();
      console.log(today);
    
      const querySnapshot = await getDocs(collection(db, 'load'));
      const newData = querySnapshot.docs
        .filter((doc) => doc.get("date").seconds >= todaySeconds)
        .map((doc) => ({ ...doc.data(), id: doc.id }));
    
      setLoad(newData);
      setIsLoading(false);
      console.log(load, newData);
    };
    

    However, the above steps can be simplified by querying using the desired Date object instead of applying a filter client-side. This will also improve speed and reduce costs.

    // NOTE: Uses user's local timezone
    export const getTodayMidnightDate = () => {
      const dt = new Date();
      dt.setHours(0, 0, 0, 0);
      return dt;
    }
    
    const getLoads = async (e) => {
      const todayDate = getTodayMidnightDate(); // or just `new Date()` if you want the current time
      console.log(today);
    
      const querySnapshot = await getDocs(query(
        collection(db, 'load'),
        where('date', '>', todayDate)
      ));
    
      const newData = querySnapshot.docs
        .map((doc) => ({ ...doc.data(), id: doc.id }));
    
      setLoad(newData);
      setIsLoading(false);
      console.log(load, newData);
    };
    

    Lastly, you should move the setLoad and setIsLoading calls outside of the getLoads call and move the getLoads function out of your component if you are calling it from inside a useEffect call.

    const getLoads = async (db) => {
      const todayDate = getTodayMidnightDate();
    
      const querySnapshot = await getDocs(query(
        collection(db, 'load'),
        where('date', '>', todayDate)
      ));
    
      return querySnapshot.docs
        .map((doc) => ({ ...doc.data(), id: doc.id }));
    };
    
    useEffect(() => {
      let detached = false;
    
      getLoads(db)
        .then(
          (newData) => {
            if (detached) return; // do nothing if detached
            setLoad(newData);
            setIsLoading(false);
          },
          (err) => {
            if (detached) return; // do nothing if detached
            setLoad(null);
            setIsLoading(false);
          }
        );
    
      return () => detached = true;
    }, []);
    

    You can also reduce some of this boilerplate using useAsyncEffect from @react-hook/async or use-async-effect depending on your style. The former will handle the loading state for you.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search