ballerina-platform / ballerina-library

The Ballerina Library
https://ballerina.io/learn/api-docs/ballerina/
Apache License 2.0
136 stars 64 forks source link

[Time] Add support add/subtract specified amount of year, month, date from the given time #6840

Open daneshk opened 3 months ago

daneshk commented 3 months ago

Description: In the current time module, we only support adding/subtracting seconds from the given time in UTC. This is because we don't support advanced cases like leap seconds, daylight saving, etc in the module. If we need to support adding/subtracting specific no of year, month, and date, we need to consider those scenarios as well.

This task is to check the possibility of supporting it in the time module.

Refer: https://github.com/wso2-enterprise/internal-support-ballerina/issues/743

daneshk commented 2 months ago

Extracted from the Ballerina time module specification,

Nominal duration

A specification of a duration in terms of a variety of units (e.g. months, years) which can be resolved to a definite duration only relative to a particular timestamp.

Apart from seconds, the units are

Note that all of these other than seconds are affected by leap seconds.

If you ignore leap seconds, all except months and years can be resolved into a definite number of seconds independent of a timestamp.

We can represent this as a record with optional fields (or with fields defaulting to 0).

Arithmetic using nominal durations and broken-down date-time has several cases that are poorly defined e.g.

There's an algorithm in XML Schema Part 2 we can use https://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes

daneshk commented 2 months ago

In practice, handling leap seconds is often done by relying on systems or services (like NTP servers) that are designed to manage leap seconds. Most applications simply ignore leap seconds because their occurrence is so infrequent and they typically have a negligible impact on most time calculations.

Leap Seconds in Java:

Leap Seconds in Golang:

Python's Time Libraries:

daneshk commented 2 months ago

When adding a duration to a specific timestamp while considering Daylight Saving Time (DST), you need to account for potential DST transitions that may cause the local time to shift forward or backward. Here we need timezone-aware timestamps

Using timezone-aware objects (like ZonedDateTime in Java or datetime with pytz/zoneinfo in Python) ensures that any DST-related shifts are accurately reflected in the final timestamp.

daneshk commented 2 months ago

The proposed design contains APIs to cater to the following scenarios,

Sample code:

time:Civil civil = check time:civilFromString("2021-04-12T23:20:50.520Z");
time:Duration duration = {years: 1, months: 2, days: 3, hours: 4, minutes: 5, seconds: 6};
time:Civil newCivil = time:civilAddDuration(civil, duration);
public type Zone readonly & object {

    # If always at a fixed offset from Utc, then this function returns it; otherwise nil.
    #
    # + return - The fixed zone offset or nil
    public isolated function fixedOffset() returns ZoneOffset?;

    # Converts a given `Civil` value to an `Utc` timestamp based on the time zone value.
    #
    # + civil - `Civil` time
    # + return - The corresponding `Utc` value or an error if `civil.timeAbbrev` is missing
    public isolated function utcFromCivil(Civil civil) returns Utc|Error;

    # Converts a given `Utc` timestamp to a `Civil` value based on the time zone value.
    #
    # + utc - `Utc` timestamp
    # + return - The corresponding `Civil` value
    public isolated function utcToCivil(Utc utc) returns Civil;

    # Adds the given datetime duration to the given civil time.  This is a time zone-aware calculation
    # + civil - The civil time to which the duration should be added
    # + deltaTime - The datetime duration to be added
    public isolated function civilAddDuration(Civil civil, Duration dateTime) returns Civil;
};

Sample code

time:TimeZone timeZone = check new("Asia/Colombo");
time:Civil localTime = check timeZone.utcToCivil(time:utcNow());
time:Duration duration = {years: 1, months: 2, days: 3, hours: 4, minutes: 5, seconds: 6};
time:Civil newCivil = timeZone.civilAddDuration(localTime, duration);
daneshk commented 2 months ago

@jclark @sameerajayasoma, could you please review the proposed API to add duration for the civil record? The requirement is to add/subtract a DateTime (provided by the user) from the current date and get the modified date. We only provide the utcAddSeconds[1] API that works with seconds.

  1. https://central.ballerina.io/ballerina/time/latest#utcAddSeconds
malay5 commented 4 weeks ago

Please assign this issue to me

Atharva1723 commented 4 weeks ago

Can I work on this issue ?

daneshk commented 3 weeks ago

👋 Welcome, @malay5 🚀

We're thrilled to have you join the Ballerina Hacktoberfest community! You have been assigned the issue, and we’re excited to see your contributions.

No contribution is too small, and your feedback is always welcome! Don’t hesitate to ask questions, propose new ideas, or report issues.

Happy coding and welcome aboard! 🎉

daneshk commented 3 weeks ago

Can I work on this issue ?

Thank you, @Atharva1723, for your interest. However, we need to assign @malay5 as she made the request first.

daneshk commented 3 weeks ago

Hi @malay5,

Were you able to work on this issue?

PentesterPriyanshu commented 2 weeks ago

@Danesh Kuruppu please assign issue to me

daneshk commented 2 weeks ago

It seems that @malay5 is unavailable to work on the issue. @Atharva1723, are you available to work on this issue? I appreciate your prompt response so that we can proceed.

daneshk commented 2 weeks ago

@Danesh Kuruppu please assign issue to me

@PentesterPriyanshu Thank you for your interest. If @Atharva1723 is unavailable, we can proceed with you. I appreciate your patience.

Atharva1723 commented 2 weeks ago

Yes I can surely work

Atharva1723 commented 2 weeks ago

Yes I can surely work

daneshk commented 2 weeks ago

Yes I can surely work

Thank you, @Atharva1723. I assigned the issue to you. Please check

jclark commented 2 weeks ago

I've been meaning to comment on this. One of the design points of the Ballerina time APIs is that they handle leap seconds properly. Although they are an immense pain in the neck, they are a fact of life.

You can handle them properly with the Duration type. The key point is that not all minutes have 60 seconds. The last minute of UTC days where a leap second happens have 59 or 60 seconds. We can normalize Durations into a combination of seconds, minutes and months.

When you specify a duration as a number of minutes, then it effectively ignores leap seconds, and you can do the computation without knowing when there were leap seconds (this is usually what people want).

It's a matter of people saying what they want. Imagine a duration of 86401 seconds. You need to decide whether you always want an elapsed time of 86401 seconds (even in the presence of a a leap second), in which case you would specify a duration of 86401 seconds, or whether you really mean 24 hours plus 1 second, in which case you would have a normalized duration of 1440 minutes and 1 second, which might have an elapsed time of 86399 or 88401 seconds in rare circumstances.

This means that in some cases the addition/subtraction of a duration will need to know about leap seconds, and it may fail if the duration is past the last known leap second. There should be an object like a time zone that includes this information, and there should be addition/subtraction methods that return an error in the case where they need leap second information that is not available.

We should be implementing time APIs using as much Ballerina as possible, rather than just a thin layer on top of Java APIs. WSO2 won't be using Java for ever.

daneshk commented 1 week ago

@Atharva1723, Based on @jclark's comment, we need to incorporate the leap seconds into the original design as well. Are you still active on this issue?

Atharva1723 commented 1 week ago

Yes working on the issue

daneshk commented 1 week ago

Great. Thanks, @Atharva1723. To capture the leap seconds, we need to change our original design. Let's work together. Please keep us updated on the progress

daneshk commented 1 week ago

@Atharva1723,

Given the complexity involved in incorporating leap seconds into the design, we believe it is challenging for external users to contribute effectively to this issue. Therefore, we have decided to remove this issue from Hacktoberfest and continue working on it within our internal team.

Could you please update me on your current progress so that we can evaluate it and determine the next steps?

I apologize for retracting this issue, and I appreciate your interest and contributions. Thank you!