Skip to content

Commit

Permalink
Merge pull request #248 from Open-EO/date-time
Browse files Browse the repository at this point in the history
Manipulation of date & time
  • Loading branch information
m-mohr authored May 20, 2021
2 parents 090c9fc + 1ce0dd5 commit a677258
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- New processes in proposal state
- `date_shift`
- `is_infinite`
- `nan`
- Added return value details (property `returns`) for the schemas with the subtype `process-graph`. [API#350](https://github.com/Open-EO/openeo-api/issues/350)
Expand Down
27 changes: 26 additions & 1 deletion meta/implementation.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,29 @@ you may define something like the following for the parameter:

]
}
```
```

## Date and Time manipulation

Working with dates is a lot more complex than it seems to be at first sight. Issues arise especially with daylight saving times (DST), time zones, leap years and leap seconds.

The date/time functions in openEO don't have any effect on time zones right now as only dates and times in UTC (with potential numerical time zone modifier) are supported.

Month overflows, including the specific case of leap years, are implemented in a way that computations handle them gracefully. For example:

- If you add a month to January, 31th, it will result in February 29th (leap year) or 28th (other years). This means for invalid dates due to month overflow we round down (or "snap") to the last valid date of the month.
- If you add a month to February, 29th, it will result in March, 29. So the "snap" behavior doesn't work the other way round.

Leap seconds are basically ignored in manipulations as they don't follow a regular pattern. So leap seconds may be passed into the processes, but will never be returned by date manipulation processes in openEO. See the examples for the leap second `2016-12-31T23:59:60Z`:

- If you add a minute to `2016-12-31T23:59:60Z`, it will result in `2017-01-01T00:00:59Z`. This means for invalid times we round down (or "snap") to the next valid time.
- If you add a seconds to `2016-12-31T23:59:59Z`, it will result in `2017-01-01T00:00:00Z`.

### Language support

To make `date_shift` easier to implement, we have found some libraries that follow this specification and can be used for implementations:

- Java: [java.time](https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html)
- JavaScript: [Moment.js](https://momentjs.com/)
- Python: [dateutil](https://dateutil.readthedocs.io/en/stable/index.html)
- R: [lubridate](https://lubridate.tidyverse.org/) ([Cheatsheet](https://rawgit.com/rstudio/cheatsheets/master/lubridate.pdf))
136 changes: 136 additions & 0 deletions proposals/date_shift.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
{
"id": "date_shift",
"summary": "Manipulates dates and times by addition or subtraction",
"description": "Based on a given date (and optionally time), calculates a new date (and time if given) by adding or subtracting a given temporal period.\n\nSome specifics about dates and times need to be taken into account:\n\n* This process doesn't have any effect on the time zone.\n* It doesn't take daylight saving time (DST) into account as only dates and time in UTC (with potential numerical time zone modifier) are supported.\n* Leap years are implemented in a way that computations handle them gracefully (see parameter `unit` for details).\n* Leap seconds are mostly ignored in manipulations as they don't follow a regular pattern. Leap seconds can be passed to the process, but will never be returned.",
"categories": [
"date & time"
],
"experimental": true,
"parameters": [
{
"name": "date",
"description": "The date (and optionally time) to manipulate.\n\nIf the given date doesn't include the time, the process assumes that the time component is `00:00:00Z` (i.e. midnight, in UTC). The millisecond part of the time is optional and defaults to `0` if not given.",
"schema": [
{
"type": "string",
"format": "date-time",
"subtype": "date-time"
},
{
"type": "string",
"format": "date",
"subtype": "date"
}
]
},
{
"name": "value",
"description": "The period of time in the unit given that is added (positive numbers) or subtracted (negative numbers). The value `0` doesn't have any effect.",
"schema": {
"type": "integer"
}
},
{
"name": "unit",
"description": "The unit for the value given. The following pre-defined units are available:\n\n- millisecond: Milliseconds\n- second: Seconds - leap seconds are ignored in computations.\n- minute: Minutes\n- hour: Hours\n- day: Days - changes only the the day part of a date\n- week: Weeks (equivalent to 7 days)\n- month: Months\n- year: Years\n\nManipulations with the unit `year`, `month`, `week` or `day` do never change the time. If any of the manipulations result in an invalid date or time, the corresponding part is rounded down to the next valid date or time respectively. For example, adding a month to `2020-01-31` would result in `2020-02-29`.",
"schema": {
"type": "string",
"enum": [
"millisecond",
"second",
"minute",
"hour",
"day",
"week",
"month",
"year"
]
}
}
],
"returns": {
"description": "The manipulated date. If a time component was given in the parameter `date`, the time component is returned with the date.",
"schema": [
{
"type": "string",
"format": "date-time",
"subtype": "date-time"
},
{
"type": "string",
"format": "date",
"subtype": "date"
}
]
},
"examples": [
{
"arguments": {
"date": "2020-02-01T17:22:45Z",
"value": 6,
"unit": "month"
},
"returns": "2020-08-01T17:22:45Z"
},
{
"arguments": {
"date": "2021-03-31T00:00:00+02:00",
"value": -7,
"unit": "day"
},
"returns": "2021-03-24T00:00:00+02:00"
},
{
"description": "Adding a year to February 29th in a leap year will result in February 28th in the next (non-leap) year.",
"arguments": {
"date": "2020-02-29T17:22:45Z",
"value": 1,
"unit": "year"
},
"returns": "2021-02-28T17:22:45Z"
},
{
"description": "Adding a month to January 31th will result in February 29th in leap years.",
"arguments": {
"date": "2020-01-31",
"value": 1,
"unit": "month"
},
"returns": "2020-02-29"
},
{
"description": "The process skips over the leap second `2016-12-31T23:59:60Z`.",
"arguments": {
"date": "2016-12-31T23:59:59Z",
"value": 1,
"unit": "second"
},
"returns": "2017-01-01T00:00:00Z"
},
{
"description": "Milliseconds can be added or subtracted. If not given, the default value is `0`.",
"arguments": {
"date": "2018-12-31T17:22:45Z",
"value": 1150,
"unit": "millisecond"
},
"returns": "2018-12-31T17:22:46.150Z"
},
{
"arguments": {
"date": "2018-01-01",
"value": 25,
"unit": "hour"
},
"returns": "2018-01-02"
},
{
"arguments": {
"date": "2018-01-01",
"value": -1,
"unit": "hour"
},
"returns": "2017-12-31"
}
]
}

0 comments on commit a677258

Please sign in to comment.