Skip to content

Commit 4eb402e

Browse files
committed
change API of timestampTruncate
1 parent 109a2f8 commit 4eb402e

File tree

4 files changed

+112
-13
lines changed

4 files changed

+112
-13
lines changed

Firestore/Swift/Source/ExpressionImplementation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,7 @@ public extension Expression {
935935
return FunctionExpression(functionName: "timestamp_to_unix_seconds", args: [self])
936936
}
937937

938-
func timestampTruncate(granularity: TimeUnit) -> FunctionExpression {
938+
func timestampTruncate(granularity: TimeGranularity) -> FunctionExpression {
939939
return FunctionExpression(
940940
functionName: "timestamp_trunc",
941941
args: [self, Helper.sendableToExpr(granularity.rawValue)]

Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/Expression.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,19 +1452,23 @@ public protocol Expression: Sendable {
14521452
/// Field("timestamp").timestampTruncate(granularity: .day)
14531453
/// ```
14541454
///
1455-
/// - Parameter granularity: A `TimeUnit` enum representing the truncation unit.
1455+
/// - Parameter granularity: A `TimeGranularity` representing the truncation unit.
14561456
/// - Returns: A new `FunctionExpression` representing the truncated timestamp.
1457-
func timestampTruncate(granularity: TimeUnit) -> FunctionExpression
1457+
func timestampTruncate(granularity: TimeGranularity) -> FunctionExpression
14581458

14591459
/// Creates an expression that truncates a timestamp to a specified granularity.
1460-
/// Assumes `self` evaluates to a Timestamp, and `granularity` is a literal string.
1460+
/// Assumes `self` evaluates to a Timestamp.
14611461
///
14621462
/// ```swift
14631463
/// // Truncate "timestamp" field to the nearest day using a literal string.
14641464
/// Field("timestamp").timestampTruncate(granularity: "day")
1465+
///
1466+
/// // Truncate "timestamp" field to the nearest day using an expression.
1467+
/// Field("timestamp").timestampTruncate(granularity: Field("granularity_field"))
14651468
/// ```
14661469
///
1467-
/// - Parameter granularity: A `Sendable` literal string specifying the truncation unit.
1470+
/// - Parameter granularity: A `Sendable` literal string or an `Expression` that evaluates to a
1471+
/// string, specifying the truncation unit.
14681472
/// - Returns: A new `FunctionExpression` representing the truncated timestamp.
14691473
func timestampTruncate(granularity: Sendable) -> FunctionExpression
14701474

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
public struct TimeGranularity: Sendable, Equatable, Hashable {
16+
enum Kind: String {
17+
case microsecond
18+
case millisecond
19+
case second
20+
case minute
21+
case hour
22+
case day
23+
case week
24+
case weekMonday = "week(monday)"
25+
case weekTuesday = "week(tuesday)"
26+
case weekWednesday = "week(wednesday)"
27+
case weekThursday = "week(thursday)"
28+
case weekFriday = "week(friday)"
29+
case weekSaturday = "week(saturday)"
30+
case weekSunday = "week(sunday)"
31+
case isoweek
32+
case month
33+
case quarter
34+
case year
35+
case isoyear
36+
}
37+
38+
public static let microsecond = TimeGranularity(kind: .microsecond)
39+
public static let millisecond = TimeGranularity(kind: .millisecond)
40+
public static let second = TimeGranularity(kind: .second)
41+
public static let minute = TimeGranularity(kind: .minute)
42+
public static let hour = TimeGranularity(kind: .hour)
43+
/// The day in the Gregorian calendar year that contains the value to truncate.
44+
public static let day = TimeGranularity(kind: .day)
45+
/// The first day in the week that contains the value to truncate. Weeks begin on Sundays. WEEK is
46+
/// equivalent to WEEK(SUNDAY).
47+
public static let week = TimeGranularity(kind: .week)
48+
/// The first day in the week that contains the value to truncate. Weeks begin on Monday.
49+
public static let weekMonday = TimeGranularity(kind: .weekMonday)
50+
/// The first day in the week that contains the value to truncate. Weeks begin on Tuesday.
51+
public static let weekTuesday = TimeGranularity(kind: .weekTuesday)
52+
/// The first day in the week that contains the value to truncate. Weeks begin on Wednesday.
53+
public static let weekWednesday = TimeGranularity(kind: .weekWednesday)
54+
/// The first day in the week that contains the value to truncate. Weeks begin on Thursday.
55+
public static let weekThursday = TimeGranularity(kind: .weekThursday)
56+
/// The first day in the week that contains the value to truncate. Weeks begin on Friday.
57+
public static let weekFriday = TimeGranularity(kind: .weekFriday)
58+
/// The first day in the week that contains the value to truncate. Weeks begin on Saturday.
59+
public static let weekSaturday = TimeGranularity(kind: .weekSaturday)
60+
/// The first day in the week that contains the value to truncate. Weeks begin on Sunday.
61+
public static let weekSunday = TimeGranularity(kind: .weekSunday)
62+
/// The first day in the ISO 8601 week that contains the value to truncate. The ISO week begins on
63+
/// Monday. The first ISO week of each ISO year contains the first Thursday of the corresponding
64+
/// Gregorian calendar year.
65+
public static let isoweek = TimeGranularity(kind: .isoweek)
66+
/// The first day in the month that contains the value to truncate.
67+
public static let month = TimeGranularity(kind: .month)
68+
/// The first day in the quarter that contains the value to truncate.
69+
public static let quarter = TimeGranularity(kind: .quarter)
70+
/// The first day in the year that contains the value to truncate.
71+
public static let year = TimeGranularity(kind: .year)
72+
/// The first day in the ISO 8601 week-numbering year that contains the value to truncate. The ISO
73+
/// year is the Monday of the first week where Thursday belongs to the corresponding Gregorian
74+
/// calendar year.
75+
public static let isoyear = TimeGranularity(kind: .isoyear)
76+
77+
public let rawValue: String
78+
79+
init(kind: Kind) {
80+
rawValue = kind.rawValue
81+
}
82+
}

Firestore/Swift/Tests/Integration/PipelineTests.swift

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3365,15 +3365,22 @@ class PipelineIntegrationTests: FSTIntegrationTestCase {
33653365
.limit(1)
33663366
.select(
33673367
[
3368-
Constant(baseTimestamp).timestampTruncate(granularity: "nanosecond").as("truncNano"),
33693368
Constant(baseTimestamp).timestampTruncate(granularity: .microsecond).as("truncMicro"),
33703369
Constant(baseTimestamp).timestampTruncate(granularity: .millisecond).as("truncMilli"),
33713370
Constant(baseTimestamp).timestampTruncate(granularity: .second).as("truncSecond"),
33723371
Constant(baseTimestamp).timestampTruncate(granularity: .minute).as("truncMinute"),
33733372
Constant(baseTimestamp).timestampTruncate(granularity: .hour).as("truncHour"),
33743373
Constant(baseTimestamp).timestampTruncate(granularity: .day).as("truncDay"),
3375-
Constant(baseTimestamp).timestampTruncate(granularity: "month").as("truncMonth"),
3376-
Constant(baseTimestamp).timestampTruncate(granularity: "year").as("truncYear"),
3374+
Constant(baseTimestamp).timestampTruncate(granularity: .week).as("truncWeek"),
3375+
Constant(baseTimestamp).timestampTruncate(granularity: .weekMonday).as("truncWeekMonday"),
3376+
Constant(baseTimestamp).timestampTruncate(granularity: .weekTuesday)
3377+
.as("truncWeekTuesday"),
3378+
Constant(baseTimestamp).timestampTruncate(granularity: .isoweek).as("truncIsoWeek"),
3379+
Constant(baseTimestamp).timestampTruncate(granularity: .month).as("truncMonth"),
3380+
Constant(baseTimestamp).timestampTruncate(granularity: .quarter).as("truncQuarter"),
3381+
Constant(baseTimestamp).timestampTruncate(granularity: .year).as("truncYear"),
3382+
Constant(baseTimestamp).timestampTruncate(granularity: .isoyear).as("truncIsoYear"),
3383+
Constant(baseTimestamp).timestampTruncate(granularity: "day").as("truncDayString"),
33773384
Constant(baseTimestamp).timestampTruncate(granularity: Constant("day"))
33783385
.as("truncDayExpr"),
33793386
]
@@ -3384,16 +3391,22 @@ class PipelineIntegrationTests: FSTIntegrationTestCase {
33843391
XCTAssertEqual(snapshot.results.count, 1, "Should retrieve one document")
33853392

33863393
let expectedResults: [String: Timestamp] = [
3387-
"truncNano": Timestamp(seconds: 1_741_380_235, nanoseconds: 123_456_000),
33883394
"truncMicro": Timestamp(seconds: 1_741_380_235, nanoseconds: 123_456_000),
33893395
"truncMilli": Timestamp(seconds: 1_741_380_235, nanoseconds: 123_000_000),
33903396
"truncSecond": Timestamp(seconds: 1_741_380_235, nanoseconds: 0),
33913397
"truncMinute": Timestamp(seconds: 1_741_380_180, nanoseconds: 0),
33923398
"truncHour": Timestamp(seconds: 1_741_377_600, nanoseconds: 0),
3393-
"truncDay": Timestamp(seconds: 1_741_305_600, nanoseconds: 0), // Assuming UTC day start
3394-
"truncMonth": Timestamp(seconds: 1_740_787_200, nanoseconds: 0), // Assuming UTC month start
3395-
"truncYear": Timestamp(seconds: 1_735_689_600, nanoseconds: 0), // Assuming UTC year start
3396-
"truncDayExpr": Timestamp(seconds: 1_741_305_600, nanoseconds: 0), // Assuming UTC day start
3399+
"truncDay": Timestamp(seconds: 1_741_305_600, nanoseconds: 0),
3400+
"truncWeek": Timestamp(seconds: 1_740_873_600, nanoseconds: 0),
3401+
"truncWeekMonday": Timestamp(seconds: 1_740_960_000, nanoseconds: 0),
3402+
"truncWeekTuesday": Timestamp(seconds: 1_741_046_400, nanoseconds: 0),
3403+
"truncIsoWeek": Timestamp(seconds: 1_740_960_000, nanoseconds: 0),
3404+
"truncMonth": Timestamp(seconds: 1_740_787_200, nanoseconds: 0),
3405+
"truncQuarter": Timestamp(seconds: 1_735_689_600, nanoseconds: 0),
3406+
"truncYear": Timestamp(seconds: 1_735_689_600, nanoseconds: 0),
3407+
"truncIsoYear": Timestamp(seconds: 1_735_516_800, nanoseconds: 0),
3408+
"truncDayString": Timestamp(seconds: 1_741_305_600, nanoseconds: 0),
3409+
"truncDayExpr": Timestamp(seconds: 1_741_305_600, nanoseconds: 0),
33973410
]
33983411

33993412
if let resultDoc = snapshot.results.first {

0 commit comments

Comments
 (0)