diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index f6fe874..5e2a77d 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -1,7 +1,7 @@ [package] org = "ballerina" name = "time" -version = "2.4.0" +version = "2.5.0" authors = ["Ballerina"] keywords = ["time", "utc", "epoch", "civil"] repository = "https://github.com/ballerina-platform/module-ballerina-time" @@ -15,5 +15,5 @@ graalvmCompatible = true [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" artifactId = "time-native" -version = "2.4.0" -path = "../native/build/libs/time-native-2.4.0.jar" +version = "2.5.0" +path = "../native/build/libs/time-native-2.5.0-SNAPSHOT.jar" diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 7900e51..bed40f3 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -40,7 +40,7 @@ modules = [ [[package]] org = "ballerina" name = "time" -version = "2.4.0" +version = "2.5.0" dependencies = [ {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "test"} diff --git a/ballerina/tests/time_test.bal b/ballerina/tests/time_test.bal index e27e6e8..33ad1ca 100644 --- a/ballerina/tests/time_test.bal +++ b/ballerina/tests/time_test.bal @@ -356,7 +356,7 @@ isolated function testCivilToString() returns Error? { utcOffset: zoneOffset }; string civilStr = check civilToString(civil); - string expectedStr = "2021-03-04T19:03:28.839564Z"; + string expectedStr = "2021-03-05T00:33:28.839564+05:30"; test:assertEquals(civilStr, expectedStr); } @@ -383,23 +383,23 @@ isolated function testCivilToStringWithTimeOfDay() returns Error? { utcOffset: zoneOffset }; string civilStr = check civilToString(civil); - string expectedStr = "2021-03-04T19:03:28.839564Z"; + string expectedStr = "2021-03-05T00:33:28.839564+05:30"; test:assertEquals(civilStr, expectedStr); } @test:Config {} -isolated function testCivilToStringWithoutOffset() { +isolated function testCivilToStringWithTimeAbbreviation() returns Error? { Civil civil = { year: 2021, month: 4, day: 13, hour: 4, - minute: 70, + minute: 33, timeAbbrev: "Asia/Colombo" }; - string|Error err = civilToString(civil); - test:assertTrue(err is Error); - test:assertEquals((err).message(), "civil.utcOffset must not be null"); + string civilStr = check civilToString(civil); + string expectedStr = "2021-04-13T04:33+05:30[Asia/Colombo]"; + test:assertEquals(civilStr, expectedStr); } @test:Config {} @@ -727,25 +727,6 @@ isolated function testUtcFromCivilWithEmptyTimeOffsetNegative() returns Error? { } } -@test:Config {enable: true} -isolated function testCivilToStringWithEmptyTimeOffsetNegative() returns Error? { - Civil civil = { - year: 2021, - month: 4, - day: 12, - hour: 23, - minute: 20, - second: 50.52, - timeAbbrev: "Asia/Colombo" - }; - string|error civilString = civilToString(civil); - if civilString is error { - test:assertEquals(civilString.message(), "civil.utcOffset must not be null"); - } else { - test:assertFail("civilString should be error"); - } -} - isolated function testUtcFromCivilWithEmptyTimeOffsetAndAbbreviation() returns Error? { Utc expectedUtc = check utcFromString("2021-04-12T23:20:50.520Z"); Civil civil = { @@ -776,7 +757,7 @@ isolated function testCivilToStringWithEmptyTimeOffsetAndAbbreviation() returns }; string|error civilString = civilToString(civil); if civilString is error { - test:assertEquals(civilString.message(), "civil.utcOffset must not be null"); + test:assertEquals(civilString.message(), "the civil value should have either `utcOffset` or `timeAbbrev`"); } else { test:assertFail("civilString should be error"); } diff --git a/ballerina/time_apis.bal b/ballerina/time_apis.bal index 35cd33b..689bc57 100644 --- a/ballerina/time_apis.bal +++ b/ballerina/time_apis.bal @@ -162,7 +162,7 @@ public isolated function civilFromString(string dateTimeString) returns Civil|Er return check externCivilFromString(dateTimeString); } -# Obtain a RFC 3339 timestamp (e.g., `2007-12-03T10:15:30.00Z`) from a given `time:Civil`. +# Obtain a RFC 3339 timestamp (e.g., `2021-03-05T00:33:28.839564+05:30`) from a given `time:Civil`. # ```ballerina # time:Civil civil = check time:civilFromString("2007-12-03T10:15:30.00Z"); # string|time:Error civilString = time:civilToString(civil); @@ -170,23 +170,23 @@ public isolated function civilFromString(string dateTimeString) returns Civil|Er # + civil - `time:Civil` that needs to be converted # + return - The corresponding string value or an error if the specified `time:Civil` contains invalid parameters (e.g., `month` > 12) public isolated function civilToString(Civil civil) returns string|Error { - ZoneOffset utcOffset; - if civil?.utcOffset is () { - if civil?.timeAbbrev !is () && string:toLowerAscii(civil?.timeAbbrev) == "z" { - utcOffset = {hours: 0, minutes: 0, seconds: 0}; - } else { - return error FormatError("civil.utcOffset must not be null"); - } - } else { - utcOffset = civil?.utcOffset; + ZoneOffset? utcOffset = civil?.utcOffset; + string? timeAbbrev = civil?.timeAbbrev; + + HeaderZoneHandling zoneHandling = PREFER_ZONE_OFFSET; + if utcOffset is () && timeAbbrev is () { + return error FormatError("the civil value should have either `utcOffset` or `timeAbbrev`"); + } else if utcOffset is () && timeAbbrev is string { + zoneHandling = PREFER_TIME_ABBREV; } - decimal? civilTimeSecField = civil?.second; - decimal? utcOffsetSecField = utcOffset?.seconds; - decimal civilTimeSeconds = (civilTimeSecField is Seconds) ? civilTimeSecField : 0.0; - decimal utcOffsetSeconds = (utcOffsetSecField is decimal) ? utcOffsetSecField : 0.0; - return externCivilToString(civil.year, civil.month, civil.day, civil.hour, civil.minute, civilTimeSeconds, utcOffset. - hours, utcOffset.minutes, utcOffsetSeconds); + int utcOffsetHours = utcOffset?.hours ?: 0; + int utcOffsetMinutes = utcOffset?.minutes ?: 0; + decimal utcOffsetSeconds = utcOffset?.seconds ?: 0.0; + decimal civilTimeSeconds = civil?.second ?: 0.0; + + return externCivilToString(civil.year, civil.month, civil.day, civil.hour, civil.minute, civilTimeSeconds, + utcOffsetHours, utcOffsetMinutes, utcOffsetSeconds, timeAbbrev ?: "", zoneHandling); } # Converts a given UTC to an email formatted string (e.g `Mon, 3 Dec 2007 10:15:30 GMT`). @@ -298,8 +298,9 @@ isolated function externCivilFromString(string dateTimeString) returns Civil|Err 'class: "io.ballerina.stdlib.time.nativeimpl.ExternMethods" } external; -isolated function externCivilToString(int year, int month, int day, int hour, int minute, decimal second, int zoneHour, - int zoneMinute, decimal zoneSecond) returns string|Error = @java:Method { +isolated function externCivilToString(int year, int month, int day, int hour, int minute, decimal second, + int zoneHour, int zoneMinute, decimal zoneSecond, + string timeAbber, HeaderZoneHandling zoneHandling) returns string|Error = @java:Method { name: "externCivilToString", 'class: "io.ballerina.stdlib.time.nativeimpl.ExternMethods" } external; diff --git a/changelog.md b/changelog.md index a3d423f..562981c 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [unreleased] + +### Fixed +- [When converting a `time:Civil` with time-zone information to a string using `time:civilToString` API error is thrown](https://github.com/ballerina-platform/ballerina-library/issues/6986) + +## [2.3.0] - 2023-06-30 ### changed - [Mark Standard Libraries as GraalVM Compatible](https://github.com/ballerina-platform/ballerina-standard-library/issues/4568) - [Add support for inferring utc offset for zulu time(Z) in utcFromCivil and civilToString APIs](https://github.com/ballerina-platform/module-ballerina-time/pull/459) diff --git a/docs/spec/spec.md b/docs/spec/spec.md index 868f1d0..29e89e9 100644 --- a/docs/spec/spec.md +++ b/docs/spec/spec.md @@ -115,7 +115,7 @@ The time library contains several conversion APIs to convert UTC to civil. The t ```ballerina public isolated function civilFromString(string dateTimeString) returns Civil|Error; ``` -4. Civil to an RFC timestamp string +4. Civil to an RFC 3339 timestamp string (e.g., `2021-03-05T00:33:28.839564+05:30`). ```ballerina public isolated function civilToString(Civil civil) returns string|Error; ``` diff --git a/gradle.properties b/gradle.properties index 5227da2..e6e7ed9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.caching=true group=io.ballerina.stdlib -version=2.4.1-SNAPSHOT +version=2.5.0-SNAPSHOT ballerinaLangVersion=2201.8.0 puppycrawlCheckstyleVersion=10.12.0 ballerinaGradlePluginVersion=2.0.1 diff --git a/native/src/main/java/io/ballerina/stdlib/time/nativeimpl/ExternMethods.java b/native/src/main/java/io/ballerina/stdlib/time/nativeimpl/ExternMethods.java index 1c0089b..8809ffd 100644 --- a/native/src/main/java/io/ballerina/stdlib/time/nativeimpl/ExternMethods.java +++ b/native/src/main/java/io/ballerina/stdlib/time/nativeimpl/ExternMethods.java @@ -157,13 +157,13 @@ public static Object externCivilFromEmailString(BString dateTimeString) { } public static Object externCivilToString(long year, long month, long day, long hour, long minute, BDecimal second, - long zoneHour, long zoneMinute, BDecimal zoneSecond) { + long zoneHour, long zoneMinute, BDecimal zoneSecond, BString zoneAbbr, + BString zoneHandling) { try { ZonedDateTime dateTime = TimeValueHandler.createZoneDateTimeFromCivilValues(year, month, day, hour, - minute, second, zoneHour, zoneMinute, zoneSecond, null, - Constants.HeaderZoneHandling.PREFER_ZONE_OFFSET.toString()); - return StringUtils.fromString(dateTime.toInstant().toString()); + minute, second, zoneHour, zoneMinute, zoneSecond, zoneAbbr, zoneHandling.getValue()); + return StringUtils.fromString(dateTime.toString()); } catch (DateTimeException e) { return Utils.createError(Errors.FormatError, e.getMessage()); }