Skip to content

Commit d879fcb

Browse files
make format optional and create testing case about $dateToString aggregation with timezone outputs invalid ISO8601 string
1 parent 5f2c539 commit d879fcb

File tree

3 files changed

+51
-9
lines changed

3 files changed

+51
-9
lines changed

driver-core/src/main/com/mongodb/client/model/mql/MqlDate.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.mongodb.annotations.Beta;
2020
import com.mongodb.annotations.Reason;
2121
import com.mongodb.annotations.Sealed;
22+
import com.mongodb.lang.Nullable;
2223

2324
import java.util.function.Function;
2425

@@ -132,11 +133,19 @@ public interface MqlDate extends MqlValue {
132133
* The string representation of {@code this} date as determined by the
133134
* provided {@code timezone}, and formatted according to the {@code format}.
134135
*
136+
* <p>
137+
* If {@code format} is unspecified and the {@code timezone} is set to a non UTC timezone, then
138+
* {@code "%Y-%m-%dT%H:%M:%S.%L"} will be the default format.
139+
*
140+
* <p>
141+
* If {@code format} is unspecified and the {@code timezone} is UTC, then
142+
* {@code "%Y-%m-%dT%H:%M:%S.%LZ"} will be the default format.
143+
*
135144
* @param timezone the UTC Offset or Olson Timezone Identifier.
136-
* @param format the format specifier.
145+
* @param format the optional format specifier.
137146
* @return the resulting value.
138147
*/
139-
MqlString asString(MqlString timezone, MqlString format);
148+
MqlString asString(MqlString timezone, @Nullable MqlString format);
140149

141150
/**
142151
* The result of passing {@code this} value to the provided function.

driver-core/src/main/com/mongodb/client/model/mql/MqlExpression.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.mongodb.client.model.mql;
1818

1919
import com.mongodb.assertions.Assertions;
20+
import com.mongodb.lang.Nullable;
2021
import org.bson.BsonArray;
2122
import org.bson.BsonDocument;
2223
import org.bson.BsonInt32;
@@ -944,13 +945,15 @@ public MqlInteger millisecond(final MqlString timezone) {
944945
}
945946

946947
@Override
947-
public MqlString asString(final MqlString timezone, final MqlString format) {
948-
Assertions.notNull("timezone", timezone);
949-
Assertions.notNull("format", format);
950-
return newMqlExpression((cr) -> astDoc("$dateToString", new BsonDocument()
951-
.append("date", this.toBsonValue(cr))
952-
.append("format", toBsonValue(cr, format))
953-
.append("timezone", toBsonValue(cr, timezone))));
948+
public MqlString asString(final MqlString timezone, @Nullable final MqlString format) {
949+
return newMqlExpression((cr) -> {
950+
BsonDocument document = new BsonDocument("date", this.toBsonValue(cr));
951+
if (format != null) {
952+
document.append("format", toBsonValue(cr, format));
953+
}
954+
document.append("timezone", toBsonValue(cr, timezone));
955+
return astDoc("$dateToString", document);
956+
});
954957
}
955958

956959
@Override

driver-core/src/test/functional/com/mongodb/client/model/mql/TypeMqlValuesFunctionalTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.time.ZoneId;
2727
import java.time.ZoneOffset;
2828
import java.time.ZonedDateTime;
29+
import java.time.format.DateTimeFormatter;
2930
import java.util.Arrays;
3031

3132
import static com.mongodb.ClusterFixture.serverVersionAtLeast;
@@ -225,6 +226,35 @@ public void dateAsStringTest() {
225226
of(instant).asString(of("America/New_York"), of("%Y-%m-%dT%H:%M:%S.%L%z")));
226227
}
227228

229+
/**
230+
* Tests that with default format and non-UTC timezone, {@code $dateToString} won't output invalid ISO 8601 string ending with 'Z'.
231+
* Ticket: JAVA-5044
232+
*/
233+
@Test
234+
void dateAsStringWithDefaultFormat() {
235+
// the bug was reported on v6.0.3, but was fixed in v7.1 without backporting
236+
// due to the concern that it is a breaking change.
237+
assumeTrue(serverVersionAtLeast(7, 1));
238+
239+
final Instant instant = Instant.parse("2025-07-17T18:00:36.052Z");
240+
MqlDate date = of(instant);
241+
ZonedDateTime utcDateTime = ZonedDateTime.ofInstant(instant, ZoneId.of(ZoneOffset.UTC.getId()));
242+
243+
// UTC timezone's default format should be "%Y-%m-%dT%H:%M:%S.%LZ"
244+
assertExpression(
245+
utcDateTime.format(DateTimeFormatter.ISO_INSTANT),
246+
date.asString(of("UTC"), null),
247+
"{'$dateToString': {'date': {'$date': '2025-07-17T18:00:36.052Z'}, "
248+
+ "'timezone': 'UTC'}}");
249+
250+
// non-UTC timezone's default format should be "%Y-%m-%dT%H:%M:%S.%L"
251+
assertExpression(
252+
utcDateTime.withZoneSameInstant(ZoneId.of("America/New_York")).format(ISO_LOCAL_DATE_TIME),
253+
date.asString(of("America/New_York"), null),
254+
"{'$dateToString': {'date': {'$date': '2025-07-17T18:00:36.052Z'}, "
255+
+ "'timezone': 'America/New_York'}}");
256+
}
257+
228258
// parse string
229259

230260
@Test

0 commit comments

Comments
 (0)