1

I am developing a mobile app using React-Native, which uses JavaScript as the programming language. Queries are sent to Firestore to retrieve data.

Some queries include a where clause on dates (timestamps). The values of these date/timestamp attributes come from JavaScript Date objects.

The query results are wrong because the date values, which are sent to Firestore are in local time (rather than UTC) values. How can I convert the JavaScript local-time dates to UTC dates?

let queryDate = new Date(2022, 10, 31); 
console.log("queryDate:", queryDate.toDateSting()); // Date in local-time 

queryCode = queryCode.where(queryDate, ">=", "dbDateProperty"); // Wrong result is returned 
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Bilal Abdeen
  • 1,627
  • 1
  • 19
  • 41

1 Answers1

3

Firestore stores (and expects queries to use) timestamps in UTC.

In JavaScript, although Date objects store time internally in UTC, when a Date object is used, the platform's (computer, mobile device, etc.) local time zone is applied to the internal representation before the date value is used. For more details on how timezones are handled in JavaScript Date objects, please refer to: How to initialize a JavaScript Date to a particular time zone

So, before sending a date object to Firestore, we need to manipulate it by removing (subtracting) the internally stored "time zone" offset from the date value. Technically speaking, the manipulated Date value would represent a WRONG date and cannot be used for any purpose, other than being sent to Firestore (or probably some other systems!)

For example (for Sydney in the summertime with an offset of 11 hours), the internal representation of new Date(2022, 10, 31, 10, 0, 0) would be 2022-10-30T23:00:00.000Z. We need to change it to 2022-10-31T10:00:00.00Z before using it in a Firestore query. Note the "date/day" and "hour" values need to change.

Luckily, JavaScript dates have a method getTimezoneOffset(), which provides the time zone offset value stored internally (in minutes). Using this method, we need to manipulate dates before sending them to Firestore (or other systems.)

let queryDate = new Date(2022, 10, 31); 
console.log("queryDate:", queryDate.toDateSting()); // Date in local-time 
console.log("queryDate:", JSON.stringify(queryDate), ); // Date in internal representation

let wrongTimestamp = new Date(dateValue); 
wrongTimestamp.setTime(wrongTimestamp?.getTime() - (wrongTimestamp?.getTimezoneOffset() * 60 * 1000) ); 
console.log("wrongTimestamp :", JSON.stringify(wrongTimestamp ), ); // WRONG Date (from business point of view) 

queryCode = queryCode.where(wrongTimestamp, ">=", "dbDateProperty"); // correct query result

Bonus: If you need to send ONLY the date part with no time attributes, you can use the following function.

// The following function can be used to manipulate dates/timestamps before sending them to Firestore or any other system other than JavaScript. 
export const fWrongTimestamp = ({dateValue, endOfDayFlag = true}) => {
    // 1. make a copy of the date value 
    let wrongTimestamp = new Date(dateValue); 

    // 2. add the timezone offset to the date value. Technically speaking, using the timestamp in JavaScrips (after such manipulation) would be WRONG! 
    wrongTimestamp.setTime(wrongTimestamp?.getTime() - (wrongTimestamp?.getTimezoneOffset() * 60 * 1000) ); 
    // console.log("wrongTimestamp:", JSON.stringify(wrongTimestamp), "wrongTimestamp?.getTimezoneOffset():", wrongTimestamp?.getTimezoneOffset(), ); 
    
    // 3. remove the "time part" because db queries need only the "date part"
    // 3.a if "end of day" is required (used in UpperValue (<) queries): 
    if (endOfDayFlag) {
        wrongTimestamp.setHours(23);
        wrongTimestamp.setMinutes(59);
        wrongTimestamp.setSeconds(59);
        wrongTimestamp.setMilliseconds(999);
    }
    // 3.b if "start of day" is required (used in LowerValue (<) queries):
    else {
        wrongTimestamp.setHours(0);
        wrongTimestamp.setMinutes(0);
        wrongTimestamp.setSeconds(0);
        wrongTimestamp.setMilliseconds(0);
    }

    return wrongTimestamp; 
}; 

// example usage:
let queryDate = new Date(2022, 10, 31, 23, 43, 13); 
console.log("queryDate:", JSON.stringify(queryDate), ); // Date is in local-time 

queryDate = fWrongTimestamp({dateValue: queryDate, endOfDayFlag: false });
queryCode = queryCode.where(queryDate, ">=", "dbDateProperty"); 

Bilal Abdeen
  • 1,627
  • 1
  • 19
  • 41
  • [Collective Articles](https://stackoverflow.com/collectives/google-cloud?tab=articles) might be a nice place to post such articles too :) – Dharmaraj Oct 27 '22 at 08:02