NIP-52: Calendar Events

This commit is contained in:
Terry Yiu 2023-06-10 20:57:57 -04:00 committed by fiatjaf_
parent 5d63b1570c
commit b4cdc1a73d
2 changed files with 216 additions and 2 deletions

209
52.md Normal file
View File

@ -0,0 +1,209 @@
NIP-52
======
Calendar Events
---------------
`draft` `optional` `author:tyiu`
This specification defines calendar events representing an occurrence at a specific moment or between moments. These calendar events are replaceable and referenceable per [NIP-33](33.md) and deletable per [NIP-09](09.md).
Unlike the term `calendar event` specific to this NIP, the term `event` is used broadly in all the NIPs to describe any Nostr event. The distinction is being made here to discern between the two terms.
## Calendar Events
There are two types of calendar events represented by different kinds: date-based and time-based calendar events. Calendar events are not required to be part of a [calendar](#calendar).
### Date-Based Calendar Event
This kind of calendar event starts on a date and ends before a different date in the future. Its use is appropriate for all-day or multi-day events where time and time zone hold no significance. e.g., anniversary, public holidays, vacation days.
#### Format
The format uses a parameterized replaceable event kind `31922`.
The `.content` of these events is optional and should be a detailed description of the calendar event.
The list of tags are as follows:
* `d` (required) universally unique identifier (UUID). Generated by the client creating the calendar event.
* `name` (required) name of the calendar event
* `start` (required) inclusive start date in ISO 8601 format (YYYY-MM-DD). Must be less than `end`, if it exists.
* `end` (optional) exclusive end date in ISO 8601 format (YYYY-MM-DD). If omitted, the calendar event ends on the same date as `start`.
* `location` (optional) location of the calendar event. e.g. address, GPS coordinates, meeting room name, link to video call
* `g` (optional) [geohash](https://en.wikipedia.org/wiki/Geohash) to associate calendar event with a searchable physical location as suggested as an example by [NIP-12](12.md)
* `p` (optional, repeated) 32-bytes hex pubkey of a participant, optional recommended relay URL, and participant's role in the meeting
* `t` (optional, repeated) hashtag to categorize calendar event
* `r` (optional, repeated) references / links to web pages, documents, video calls, recorded videos, etc.
```json
{
"id": <32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>,
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
"created_at": <Unix timestamp in seconds>,
"kind": "31922",
"content": "<description of calendar event>",
"tags": [
["d", "<UUID>"],
["name", "<name of calendar event>"],
// Dates
["start", "<YYYY-MM-DD>"],
["end", "<YYYY-MM-DD>"],
// Location
["location", "<location>"],
["g", "<geohash>"],
// Participants
["p", "<32-bytes hex of a pubkey>", "<optional recommended relay URL>", "<role>"],
["p", "<32-bytes hex of a pubkey>", "<optional recommended relay URL>", "<role>"],
// Hashtags
["t", "<tag>"],
["t", "<tag>"],
// Reference links
["r", "<url>"],
["r", "<url>"]
]
}
```
### Time-Based Calendar Event
This kind of calendar event spans between a start time and end time.
#### Format
The format uses a parameterized replaceable event kind `31923`.
The `.content` of these events is optional and should be a detailed description of the calendar event.
The list of tags are as follows:
* `d` (required) universally unique identifier (UUID). Generated by the client creating the calendar event.
* `name` (required) name of the calendar event
* `start` (required) inclusive start Unix timestamp in seconds. Must be less than `end`, if it exists.
* `end` (optional) exclusive end Unix timestamp in seconds. If omitted, the calendar event ends instantaneously.
* `start_tzid` (optional) time zone of the start timestamp, as defined by the IANA Time Zone Database. e.g., `America/Costa_Rica`
* `end_tzid` (optional) time zone of the end timestamp, as defined by the IANA Time Zone Database. e.g., `America/Costa_Rica`. If omitted and `start_tzid` is provided, the time zone of the end timestamp is the same as the start timestamp.
* `location` (optional) location of the calendar event. e.g. address, GPS coordinates, meeting room name, link to video call
* `g` (optional) [geohash](https://en.wikipedia.org/wiki/Geohash) to associate calendar event with a searchable physical location as suggested as an example by [NIP-12](12.md)
* `p` (optional, repeated) 32-bytes hex pubkey of a participant, optional recommended relay URL, and participant's role in the meeting
* `t` (optional, repeated) hashtag to categorize calendar event
* `r` (optional, repeated) references / links to web pages, documents, video calls, recorded videos, etc.
```json
{
"id": <32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>,
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
"created_at": <Unix timestamp in seconds>,
"kind": "31923",
"content": "<description of calendar event>",
"tags": [
["d", "<UUID>"],
["name", "<name of calendar event>"],
// Timestamps
["start", "<Unix timestamp in seconds>"],
["end", "<Unix timestamp in seconds>"],
["start_tzid", "<IANA Time Zone Database identifier>"],
["end_tzid", "<IANA Time Zone Database identifier>"],
// Location
["location", "<location>"],
["g", "<geohash>"],
// Participants
["p", "<32-bytes hex of a pubkey>", "<optional recommended relay URL>", "<role>"],
["p", "<32-bytes hex of a pubkey>", "<optional recommended relay URL>", "<role>"],
// Hashtags
["t", "<tag>"],
["t", "<tag>"],
// Reference links
["r", "<url>"],
["r", "<url>"]
]
}
```
## Calendar
A calendar is a collection of calendar events, represented as a custom replaceable list event using kind `31924`. A user can have multiple calendars. One may create a calendar to segment calendar events for specific purposes. e.g., personal, work, travel, meetups, and conferences.
### Format
The format uses a custom replaceable list of kind `31924` with a list of tags as described below:
* `d` (required) calendar name
* `a` (repeated) reference tag to kind `31922` or `31923` calendar event being responded to per [NIP-33](33.md)
```json
{
"kind": 31924,
"tags": [
["d", "<calendar name>"],
["a", "<31922 or 31923>:<calendar event author pubkey>:<d-identifier of calendar event>", "<optional relay url>"],
["a", "<31922 or 31923>:<calendar event author pubkey>:<d-identifier of calendar event>", "<optional relay url>"]
]
}
```
## Calendar Event RSVP
A calendar event RSVP is a response to a calendar event to indicate a user's attendance intention.
If a calendar event tags a pubkey, that can be interpreted as the calendar event creator inviting that user to attend. Clients MAY choose to prompt the user to RSVP for the calendar event.
Any user may RSVP, even if they were not tagged on the calendar event. Clients MAY choose to prompt the calendar event creator to invite the user who RSVP'd. Clients also MAY choose to ignore these RSVPs.
This NIP is intentionally not defining who is authorized to attend a calendar event if the user who RSVP'd has not been tagged. It is up to the calendar event creator to determine the semantics.
This NIP is also intentionally not defining what happens if a calendar event changes after an RSVP is submitted.
### Format
The format uses a parameterized replaceable event kind `31925`.
The `.content` of these events is optional and should be a free-form note that adds more context to this calendar event response.
The list of tags are as follows:
* `a` (required) reference tag to kind `31922` or `31923` calendar event being responded to per [NIP-33](33.md)
* `d` (required) universally unique identifier. Generated by the client creating the calendar event RSVP.
* `L` (required) label namespace of `status` per [NIP-32](32.md)
* `l` (required) label of `accepted`, `declined`, or `tentative` under the label namespace of `status` per [NIP-32](32.md). Determines attendance status to the referenced calendar event.
* `L` (optional) label namespace of `freebusy` per [NIP-32](32.md). Exists if and only if corresponding `l` tag under the same label namespace exists.
* `l` (optional) label of `free` or `busy` under the label namespace of `freebusy` per [NIP-32](32.md). Determines if the user would be free or busy for the duration of the calendar event. This tag must be omitted or ignored if the `status` label is set to `declined`. Exists if and only if corresponding `l` tag under the same label namespace exists.
```json
{
"id": <32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>,
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
"created_at": <Unix timestamp in seconds>,
"kind": "31925",
"content": "<note>",
"tags": [
["a", "<31922 or 31923>:<calendar event author pubkey>:<d-identifier of calendar event>", "<optional relay url>"],
["d", "<UUID>"],
["L", "status"],
["l", "<accepted/declined/tentative>", "status"],
["L", "freebusy"],
["l", "<free/busy>", "freebusy"]
]
}
```
## Unsolved Limitations
* No private events
## Intentionally Unsupported Scenarios
### Recurring Calendar Events
Recurring calendar events come with a lot of complexity, making it difficult for software and humans to deal with. This complexity includes time zone differences between invitees, daylight savings, leap years, multiple calendar systems, one-off changes in schedule or other metadata, etc.
This NIP intentionally omits support for recurring calendar events and pushes that complexity up to clients to manually implement if they desire. i.e., individual calendar events with duplicated metadata represent recurring calendar events.

View File

@ -58,6 +58,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
- [NIP-47: Wallet Connect](47.md)
- [NIP-50: Keywords filter](50.md)
- [NIP-51: Lists](51.md)
- [NIP-52: Calendar Events](52.md)
- [NIP-53: Live Activities](53.md)
- [NIP-56: Reporting](56.md)
- [NIP-57: Lightning Zaps](57.md)
@ -115,6 +116,10 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
| `30311` | Live Event | [53](53.md) |
| `30402` | Classified Listing | [99](99.md) |
| `30403` | Draft Classified Listing | [99](99.md) |
| `31922` | Date-Based Calendar Event | [52](52.md) |
| `31923` | Time-Based Calendar Event | [52](52.md) |
| `31924` | Calendar | [52](52.md) |
| `31925` | Calendar Event RSVP | [52](52.md) |
| `31989` | Handler recommendation | [89](89.md) |
| `31990` | Handler information | [89](89.md) |
@ -162,7 +167,7 @@ When experimenting with kinds, keep in mind the classification introduced by [NI
| `alt` | Alt tag | -- | [31](31.md) |
| `d` | identifier | -- | [33](33.md) |
| `e` | event id (hex) | relay URL, marker | [1](01.md), [10](10.md) |
| `g` | geohash | -- | [12](12.md) |
| `g` | geohash | -- | [12](12.md), [52](52.md) |
| `i` | identity | proof | [39](39.md) |
| `k` | kind number (string) | -- | [18](18.md) |
| `l` | label, label namespace | annotations | [32](32.md) |
@ -181,7 +186,7 @@ When experimenting with kinds, keep in mind the classification introduced by [NI
| `expiration` | unix timestamp (string) | -- | [40](40.md) |
| `image` | image URL | dimensions in pixels | [23](23.md), [58](58.md) |
| `lnurl` | `bech32` encoded `lnurl` | -- | [57](57.md) |
| `location` | location string | -- | [99](99.md) |
| `location` | location string | -- | [52](52.md), [99](99.md) |
| `name` | badge name | -- | [58](58.md) |
| `nonce` | random | -- | [13](13.md) |
| `preimage` | hash of `bolt11` invoice | -- | [57](57.md) |