Skip to content

Commit

Permalink
Make Timestamp a part of the public Firestore API. (#544)
Browse files Browse the repository at this point in the history
* Make Timestamp a part of the public Firestore API.

* Accept Timestamps as a parameter where Dates are currently accepted.

* Add an option to settings to control how timestamp fields are returned.

This is a transitional state; eventually, Timestamp will live in a new
separate package (common-types/).

The behavior to return timestamp fields as dates is now legacy. It's on
by default to allow for a transitional period. The new behavior to
return Timestamp objects instead is opt-in, but will become default in
the future. The advantage Timestamps have is higher precision than
native browser Date. If the new behavior is not enabled, a warning is
logged on startup urging users to upgrade.
  • Loading branch information
var-const authored Mar 30, 2018
1 parent b5aa5b8 commit 1608edb
Show file tree
Hide file tree
Showing 38 changed files with 831 additions and 196 deletions.
100 changes: 100 additions & 0 deletions packages/firebase/app/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,26 @@ declare namespace firebase.firestore {
host?: string;
/** Whether to use SSL when connecting. */
ssl?: boolean;

/**
* Enables the use of `Timestamp`s for timestamp fields in
* `DocumentSnapshot`s.
*
* Currently, Firestore returns timestamp fields as `Date` but `Date` only
* supports millisecond precision, which leads to truncation and causes
* unexpected behavior when using a timestamp from a snapshot as a part
* of a subsequent query.
*
* Setting `timestampsInSnapshots` to true will cause Firestore to return
* `Timestamp` values instead of `Date` avoiding this kind of problem. To make
* this work you must also change any code that uses `Date` to use `Timestamp`
* instead.
*
* NOTE: in the future `timestampsInSnapshots: true` will become the
* default and this option will be removed so you should change your code to
* use Timestamp now and opt-in to this new behavior as soon as you can.
*/
timestampsInSnapshots?: boolean;
}

export type LogLevel = 'debug' | 'error' | 'silent';
Expand Down Expand Up @@ -780,6 +800,86 @@ declare namespace firebase.firestore {
isEqual(other: GeoPoint): boolean;
}

/**
* A Timestamp represents a point in time independent of any time zone or
* calendar, represented as seconds and fractions of seconds at nanosecond
* resolution in UTC Epoch time. It is encoded using the Proleptic Gregorian
* Calendar which extends the Gregorian calendar backwards to year one. It is
* encoded assuming all minutes are 60 seconds long, i.e. leap seconds are
* "smeared" so that no leap second table is needed for interpretation. Range is
* from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
*
* @see https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto
*/
export class Timestamp {
/**
* Creates a new timestamp with the current date, with millisecond precision.
*
* @return a new timestamp representing the current date.
*/
static now(): Timestamp;

/**
* Creates a new timestamp from the given date.
*
* @param date The date to initialize the `Timestamp` from.
* @return A new `Timestamp` representing the same point in time as the given
* date.
*/
static fromDate(date: Date): Timestamp;

/**
* Creates a new timestamp from the given number of milliseconds.
*
* @param milliseconds Number of milliseconds since Unix epoch
* 1970-01-01T00:00:00Z.
* @return A new `Timestamp` representing the same point in time as the given
* number of milliseconds.
*/
static fromMillis(milliseconds: number): Timestamp;

/**
* Creates a new timestamp.
*
* @param seconds The number of seconds of UTC time since Unix epoch
* 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
* 9999-12-31T23:59:59Z inclusive.
* @param nanoseconds The non-negative fractions of a second at nanosecond
* resolution. Negative second values with fractions must still have
* non-negative nanoseconds values that count forward in time. Must be
* from 0 to 999,999,999 inclusive.
*/
constructor(seconds: number, nanoseconds: number);

readonly seconds: number;
readonly nanoseconds: number;

/**
* Returns a new `Date` corresponding to this timestamp. This may lose
* precision.
*
* @return JavaScript `Date` object representing the same point in time as
* this `Timestamp`, with millisecond precision.
*/
toDate(): Date;

/**
* Returns the number of milliseconds since Unix epoch 1970-01-01T00:00:00Z.
*
* @return The point in time corresponding to this timestamp, represented as
* the number of milliseconds since Unix epoch 1970-01-01T00:00:00Z.
*/
toMillis(): number;

/**
* Returns true if this `Timestamp` is equal to the provided one.
*
* @param other The `Timestamp` to compare against.
* @return true if this `Timestamp` is equal to the provided one.
*/
isEqual(other: Timestamp): boolean;
}

/**
* An immutable object representing an array of bytes.
*/
Expand Down
100 changes: 100 additions & 0 deletions packages/firebase/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,26 @@ declare namespace firebase.firestore {
host?: string;
/** Whether to use SSL when connecting. */
ssl?: boolean;

/**
* Enables the use of `Timestamp`s for timestamp fields in
* `DocumentSnapshot`s.
*
* Currently, Firestore returns timestamp fields as `Date` but `Date` only
* supports millisecond precision, which leads to truncation and causes
* unexpected behavior when using a timestamp from a snapshot as a part
* of a subsequent query.
*
* Setting `timestampsInSnapshots` to true will cause Firestore to return
* `Timestamp` values instead of `Date` avoiding this kind of problem. To make
* this work you must also change any code that uses `Date` to use `Timestamp`
* instead.
*
* NOTE: in the future `timestampsInSnapshots: true` will become the
* default and this option will be removed so you should change your code to
* use Timestamp now and opt-in to this new behavior as soon as you can.
*/
timestampsInSnapshots?: boolean;
}

export type LogLevel = 'debug' | 'error' | 'silent';
Expand Down Expand Up @@ -780,6 +800,86 @@ declare namespace firebase.firestore {
isEqual(other: GeoPoint): boolean;
}

/**
* A Timestamp represents a point in time independent of any time zone or
* calendar, represented as seconds and fractions of seconds at nanosecond
* resolution in UTC Epoch time. It is encoded using the Proleptic Gregorian
* Calendar which extends the Gregorian calendar backwards to year one. It is
* encoded assuming all minutes are 60 seconds long, i.e. leap seconds are
* "smeared" so that no leap second table is needed for interpretation. Range is
* from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
*
* @see https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto
*/
export class Timestamp {
/**
* Creates a new timestamp with the current date, with millisecond precision.
*
* @return a new timestamp representing the current date.
*/
static now(): Timestamp;

/**
* Creates a new timestamp from the given date.
*
* @param date The date to initialize the `Timestamp` from.
* @return A new `Timestamp` representing the same point in time as the given
* date.
*/
static fromDate(date: Date): Timestamp;

/**
* Creates a new timestamp from the given number of milliseconds.
*
* @param milliseconds Number of milliseconds since Unix epoch
* 1970-01-01T00:00:00Z.
* @return A new `Timestamp` representing the same point in time as the given
* number of milliseconds.
*/
static fromMillis(milliseconds: number): Timestamp;

/**
* Creates a new timestamp.
*
* @param seconds The number of seconds of UTC time since Unix epoch
* 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
* 9999-12-31T23:59:59Z inclusive.
* @param nanoseconds The non-negative fractions of a second at nanosecond
* resolution. Negative second values with fractions must still have
* non-negative nanoseconds values that count forward in time. Must be
* from 0 to 999,999,999 inclusive.
*/
constructor(seconds: number, nanoseconds: number);

readonly seconds: number;
readonly nanoseconds: number;

/**
* Returns a new `Date` corresponding to this timestamp. This may lose
* precision.
*
* @return JavaScript `Date` object representing the same point in time as
* this `Timestamp`, with millisecond precision.
*/
toDate(): Date;

/**
* Returns the number of milliseconds since Unix epoch 1970-01-01T00:00:00Z.
*
* @return The point in time corresponding to this timestamp, represented as
* the number of milliseconds since Unix epoch 1970-01-01T00:00:00Z.
*/
toMillis(): number;

/**
* Returns true if this `Timestamp` is equal to the provided one.
*
* @param other The `Timestamp` to compare against.
* @return true if this `Timestamp` is equal to the provided one.
*/
isEqual(other: Timestamp): boolean;
}

/**
* An immutable object representing an array of bytes.
*/
Expand Down
104 changes: 102 additions & 2 deletions packages/firestore-types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@ export interface Settings {
host?: string;
/** Whether to use SSL when connecting. */
ssl?: boolean;

/**
* Enables the use of `Timestamp`s for timestamp fields in
* `DocumentSnapshot`s.
*
* Currently, Firestore returns timestamp fields as `Date` but `Date` only
* supports millisecond precision, which leads to truncation and causes
* unexpected behavior when using a timestamp from a snapshot as a part
* of a subsequent query.
*
* Setting `timestampsInSnapshots` to true will cause Firestore to return
* `Timestamp` values instead of `Date` avoiding this kind of problem. To make
* this work you must also change any code that uses `Date` to use `Timestamp`
* instead.
*
* NOTE: in the future `timestampsInSnapshots: true` will become the
* default and this option will be removed so you should change your code to
* use Timestamp now and opt-in to this new behavior as soon as you can.
*/
timestampsInSnapshots?: boolean;
}

export type LogLevel = 'debug' | 'error' | 'silent';
Expand Down Expand Up @@ -172,6 +192,86 @@ export class GeoPoint {
isEqual(other: GeoPoint): boolean;
}

/**
* A Timestamp represents a point in time independent of any time zone or
* calendar, represented as seconds and fractions of seconds at nanosecond
* resolution in UTC Epoch time. It is encoded using the Proleptic Gregorian
* Calendar which extends the Gregorian calendar backwards to year one. It is
* encoded assuming all minutes are 60 seconds long, i.e. leap seconds are
* "smeared" so that no leap second table is needed for interpretation. Range is
* from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
*
* @see https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto
*/
export class Timestamp {
/**
* Creates a new timestamp with the current date, with millisecond precision.
*
* @return a new timestamp representing the current date.
*/
static now(): Timestamp;

/**
* Creates a new timestamp from the given date.
*
* @param date The date to initialize the `Timestamp` from.
* @return A new `Timestamp` representing the same point in time as the given
* date.
*/
static fromDate(date: Date): Timestamp;

/**
* Creates a new timestamp from the given number of milliseconds.
*
* @param milliseconds Number of milliseconds since Unix epoch
* 1970-01-01T00:00:00Z.
* @return A new `Timestamp` representing the same point in time as the given
* number of milliseconds.
*/
static fromMillis(milliseconds: number): Timestamp;

/**
* Creates a new timestamp.
*
* @param seconds The number of seconds of UTC time since Unix epoch
* 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
* 9999-12-31T23:59:59Z inclusive.
* @param nanoseconds The non-negative fractions of a second at nanosecond
* resolution. Negative second values with fractions must still have
* non-negative nanoseconds values that count forward in time. Must be
* from 0 to 999,999,999 inclusive.
*/
constructor(seconds: number, nanoseconds: number);

readonly seconds: number;
readonly nanoseconds: number;

/**
* Returns a new `Date` corresponding to this timestamp. This may lose
* precision.
*
* @return JavaScript `Date` object representing the same point in time as
* this `Timestamp`, with millisecond precision.
*/
toDate(): Date;

/**
* Returns the number of milliseconds since Unix epoch 1970-01-01T00:00:00Z.
*
* @return The point in time corresponding to this timestamp, represented as
* the number of milliseconds since Unix epoch 1970-01-01T00:00:00Z.
*/
toMillis(): number;

/**
* Returns true if this `Timestamp` is equal to the provided one.
*
* @param other The `Timestamp` to compare against.
* @return true if this `Timestamp` is equal to the provided one.
*/
isEqual(other: Timestamp): boolean;
}

/**
* An immutable object representing an array of bytes.
*/
Expand Down Expand Up @@ -652,8 +752,8 @@ export class DocumentSnapshot {
*
* @param fieldPath The path (e.g. 'foo' or 'foo.bar') to a specific field.
* @param options An options object to configure how the field is retrieved
* from the snapshot (e.g. the desired behavior for server timestamps that have
* not yet been set to their final value).
* from the snapshot (e.g. the desired behavior for server timestamps that
* have not yet been set to their final value).
* @return The data at the specified field location or undefined if no such
* field exists in the document.
*/
Expand Down
8 changes: 8 additions & 0 deletions packages/firestore/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
- [fixed] Fixed a regression in the Firebase JS release 4.11.0 that could
cause get() requests made while offline to be delayed by up to 10
seconds (rather than returning from cache immediately).
- [feature] Added a new `Timestamp` class to represent timestamp fields,
currently supporting up to microsecond precision. It can be passed to API
methods anywhere a JS Date object is currently accepted. To make
`DocumentSnapshot`s read timestamp fields back as `Timestamp`s instead of
Dates, you can set the newly added flag `timestampsInSnapshots` in
`FirestoreSettings` to `true`. Note that the current behavior
(`DocumentSnapshot`s returning JS Date objects) will be removed in a future
release. `Timestamp` supports higher precision than JS Date.

# 0.3.6
- [fixed] Fixed a regression in the Firebase JS release 4.11.0 that could
Expand Down
1 change: 1 addition & 0 deletions packages/firestore/index.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ declare module '@firebase/app-types' {
GeoPoint: typeof types.GeoPoint;
Query: typeof types.Query;
QuerySnapshot: typeof types.QuerySnapshot;
Timestamp: typeof types.Timestamp;
Transaction: typeof types.Transaction;
WriteBatch: typeof types.WriteBatch;
setLogLevel: typeof types.setLogLevel;
Expand Down
1 change: 1 addition & 0 deletions packages/firestore/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ declare module '@firebase/app-types' {
GeoPoint: typeof types.GeoPoint;
Query: typeof types.Query;
QuerySnapshot: typeof types.QuerySnapshot;
Timestamp: typeof types.Timestamp;
Transaction: typeof types.Transaction;
WriteBatch: typeof types.WriteBatch;
setLogLevel: typeof types.setLogLevel;
Expand Down
Loading

0 comments on commit 1608edb

Please sign in to comment.