opentripmodel / otm5-change-requests

Tracking and reporting bugs and change requests of the OTM5 specification.
5 stars 1 forks source link

Support for recurring dates and - time intervals #76

Closed filogic closed 9 months ago

filogic commented 1 year ago

Type of request

Is your feature request related to a problem? The Dutch government intends to disseminate information regarding restrictions on public road usage or regional limitations through the use of OpenTripModel. Some of this information has a clear start- and end date while other restrictions are recurring (e.g. weekly markets, school zones, etc ... ).

(can we add images to this ticket?)

One of the primary reasons why one would like to specify the start dates of restrictions as recurring dates and times is for the sake of flexibility and efficiency. By allowing for recurring dates and times, it is possible to define restrictions that repeat on a regular basis without the need to manually specify each occurrence individually. This saves time and effort, and allows for more efficient management of road usage restrictions.

For example, imagine a road that needs to be closed to traffic every weekday from 7:00 am to 9:00 am to allow for construction work. Instead of manually specifying the start and end dates for each weekday, it is much more efficient to specify a recurring date and time for the start of the restriction, such as "every weekday at 7:00 am." This way, the restriction can be automatically applied without the need for manual intervention.

Another reason why one would like to specify the start dates of restrictions as recurring dates and times is for the sake of clarity and consistency. By using a standardized format such as ISO 8601, it is possible to clearly define when a restriction begins and ends in a way that is easily understood by both humans and machines.

For example, imagine a road that is closed to traffic every other weekend to allow for a local street fair. Instead of using a non-standard format such as "closed on the second and fourth Saturdays of each month," it is much clearer and more consistent to use a recurring date and time format such as "closed every other Saturday from 8:00 am to 6:00 pm," which conforms to ISO 8601.

In addition, using a standardized format for recurring dates and times makes it easier to integrate this data with other systems and applications that may rely on this information, such as navigation systems or traffic management software. This can help to improve overall traffic flow and reduce congestion by providing accurate and consistent information about road usage restrictions.

Describe the solution you'd like The original version of the ISO 8601 standard, which was published in 1988, did not include support for recurring dates and time intervals, but it did not have a version that explicitly excluded it. The original standard focused on the representation of specific dates and times, and included a comprehensive set of formatting rules for representing dates and times in a way that is unambiguous and universally understood.

Support for recurring dates and time intervals was added in later revisions of the ISO 8601 standard, including the most recent version published in 2019. This extension allows the representation of recurring dates and times in a standardized way, making it easier to exchange and process this type of information across different systems and platforms.

The most recent version is called "ISO 8601:2019 - Representation of dates and times" and it includes provisions for recurring dates and times. The ISO 8601 standard provides several ways to represent recurring moments, including:

ISO 8601 is widely used and recognized in various industries and applications, including computer systems, international trade, and legal documentation.

In JSON, recurring date can be described as followed:

{
   "startTime" : "R/2022-01-01/P1M/1/1/MON"
   "endTime" : "R/2022-01-01T03:30:00"
}

This ISO 8601 representation consists of the following components:

Describe alternatives you've considered Programming languages like C# and JAVA have default (de)serialisation mechanisms build-in. Normally, an ISO-8601 date is parsed to a System.DateTime (C#) or java.time (JAVA) object. These classes do not support recurring dates and timesout of the box.

This means that developers should implement custom (de)serialisation methods that are able to handle the formats described in this change request correctly. An alternative solution would be to add a new property to the timeWindowConstrant called "recurringTime", like depicted below:

{
   "type": "timeWindowConstraint",
   "startTime": null,
   "endTime": null,
   "recurringTime": "R/2022-01-01/P1M/1/1/MON"
}

A timeWindowConstraint is only valid when either "startTime" or "recurringTime" is set.

In C#, "recurringTime" can be deserialised to RecurringDate class from the NodaTime.Extensions namespace. In JAVA, developers may consider using java.time.TemporalAdjuster interface.

bmeesters commented 1 year ago

Thanks for the extensive change request! We encounter the same problem within Simacan and would like to see a solution as well. I think the proposed approach is the cleanest solution but I do also think that most implementations will not be able to support it out of the box.

So, if we continue with this change I think we should add examples for Java, C#, JavaScript, Python as the most popular programming languages on how to deal with this, e.g. how to get some runtime object from the string.

filogic commented 1 year ago

I agree. Maybe the alternative solution is therefore the best solution ? Let's discuss in the working council

bmeesters commented 1 year ago

I played around with it a bit and could create a Scala version that deals with recurring code. It is not completely foul proof or generic yet, but it shows that it is not too hard to make it work. It can easily be translated to Java since it relies on the java std library.

// Class that **might** contain a recurring date time.
class RecurringDateTime(offsetDateTime: OffsetDateTime, duration: Option[Duration]) {

  // check whether it is just a date time or actually recurring 
  def isRecurring: Boolean =
    duration.isDefined

  def toOffsetDateTime: OffsetDateTime =
    offsetDateTime

  // the Nth time slot in the recurring date time. Just repeat the duration N times.
  def toOffsetDateTimeAfterIterations(iteration: Int): OffsetDateTime = {
    val dur = duration.getOrElse(Duration.ZERO)
    var last = offsetDateTime
    (0 to iteration).foreach(_ => last = last.plus(dur))
    last
  }
}

object RecurringDateTime {

  // Parse a recurring date time by using the Duration and OffsetDateTime parsers of Java itself. 
  def parse(string: String): RecurringDateTime = {
    val isRecurring = string.startsWith("R")
    if (isRecurring) {
      // split the time and duration parts
      val dateTimePlusDuration = string.dropWhile(char => char != '/').tail
      val indexOfSlash = dateTimePlusDuration.indexOf("/")
      val (timePart, durationPart) = dateTimePlusDuration.splitAt(indexOfSlash)
      val time = OffsetDateTime.parse(timePart)
      val duration = Duration.parse(durationPart.tail)
      new RecurringDateTime(time, Some(duration))
    } else {
      new RecurringDateTime(OffsetDateTime.parse(string), None)
    }
  }

}
bmeesters commented 1 year ago

We discussed using some existing specification, but I forget which one it was. Do you have anything you could link @filogic?

bmeesters commented 1 year ago

Might have been this one: https://developers.google.com/calendar/api/concepts/events-calendars#recurrence_rule

bmeesters commented 1 year ago

My proposal would be to use the Google Calendar API specification and make the following additions:

bmeesters commented 11 months ago

TODO: check whether there exist standard open source libraries (Java, .NET, JavaScript, Python) that implement the Google API spec so that it is easy to adopt.

bmeesters commented 11 months ago

I did a bit of digging and found the following blog post. It mentions a few libraries that implement the recurring specification

There are more options per ecosystem, but here are a few examples:

So I think we can embed this is in OTM5 specification.

bmeesters commented 9 months ago

This is now part of OTM5.6 so can be closed.