jscalendar 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fdf3b15b894e49f349876787d56ce76102b83822e622c079dfc7966804eab2bf
4
+ data.tar.gz: 23259294ec16d3a2420c1af9ad97ce72aaa0df6502a7328b19d27bb7c427f158
5
+ SHA512:
6
+ metadata.gz: 5ff2aeaa9fb416f63ede56b7d018f6db8e671bb35c71badec0a76a142bf75c9aac6ac8d9ca4e850b832168105d65e06fa970196a06848da135045994092d5065
7
+ data.tar.gz: 1cb88675b31c5d4d7fb2609e399dc667e57212d44893d96e6114a5037eca727ccc6d4c418f0803f7bc4d3f681cd0c5e36586a4db44706ebfa5e3d1b10d58e7f2
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 - 2026-10-12
4
+
5
+ Initial release.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Yudai Takada
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # JSCalendar
2
+
3
+ Read, validate, build and expand calendar events and tasks using [RFC 8984](https://www.rfc-editor.org/rfc/rfc8984).
4
+
5
+ ```ruby
6
+ require "jscalendar"
7
+
8
+ meeting = JSCalendar::Event.build do |event|
9
+ event.title = "Weekly review"
10
+ event.start = "2026-10-12T10:00:00"
11
+ event.time_zone = "Asia/Tokyo"
12
+ event.duration = "PT1H"
13
+ event.add_recurrence_rule(frequency: "weekly", by_day: [{ day: "mo" }])
14
+ end
15
+
16
+ json = meeting.to_json
17
+ copy = JSCalendar.parse(json)
18
+ copy.start_time # => 2026-10-12 01:00:00 UTC
19
+
20
+ instances = copy.occurrences(from: Time.utc(2026, 10, 1), to: Time.utc(2026, 11, 1), limit: 100)
21
+ instances.each { |instance| puts [instance.recurrence_id, instance.start_time, instance.event.title] }
22
+ instances.truncated?
23
+ ```
24
+
25
+ Install with `gem install jscalendar`. Ruby 3.3 or newer is required. CI checks maintained CRuby versions, Ruby head, JRuby and TruffleRuby on Linux and macOS, and json 2 and 3. Runtime dependencies are `json`, `tzinfo` and `rrule`. Install `tzinfo-data` on systems without a zoneinfo database, including Windows. No other Gem from this format family is required.
26
+
27
+ ## Scope
28
+
29
+ | Feature | Support |
30
+ | --- | --- |
31
+ | Event, Task, Group and every RFC 8984 object type | Parsing, validation, building, serialization and RBS |
32
+ | Unknown and vendor fields | Preserved; unknown Group entries and trigger types remain opaque |
33
+ | PatchObject | Atomic object patches, pointer escaping and overlap checks; array traversal is invalid in JSCalendar 1.0 |
34
+ | Localizations | Permitted text patches with locale selection |
35
+ | IANA and custom time zones | Floating time, Group inheritance and pre-transition offsets at gaps and folds |
36
+ | Gregorian recurrence | All frequencies and BY fields, union, exclusions, additions and overrides |
37
+ | Non-Gregorian calendars, `skip: backward/forward`, leap-second recurrence | Preserved and validated; expansion raises `UnsupportedError` |
38
+ | JSCalendar 2.0 | Awaiting a published RFC |
39
+ | iCalendar conversion, invitations, free/busy services and alert delivery | Outside this release |
40
+
41
+ `Jscalendar` is an alias of `JSCalendar`. Wire names are camelCase; Ruby accessors also accept snake_case. `Event.build` and `Task.build` generate `uid` and `updated`. Nested typed views update their owning object; `to_h` returns an independent copy. `Group#entries` contains typed Event/Task views and preserved unknown entries.
42
+
43
+ ## Validation and patches
44
+
45
+ ```ruby
46
+ calendar = JSCalendar.parse(json, strict: false)
47
+ calendar.warnings.each { |violation| warn "#{violation.pointer}: #{violation.message}" }
48
+ JSCalendar.validate(json) # array of violations
49
+ calendar.localize("ja") # a localized copy; source stays unchanged
50
+
51
+ patch = JSCalendar::PatchObject.new("title" => "New title", "description" => nil)
52
+ patched = patch.apply(calendar.to_h)
53
+ ```
54
+
55
+ Strict parsing raises `ValidationError` with `errors` and a JSON Pointer in `pointer`. Lenient parsing preserves invalid fields with diagnostics. Serialization always validates. Unknown root types and malformed JSON raise `ParseError`; resource bounds raise `LimitError`, including in lenient mode. Duplicate keys are rejected with both supported json major versions.
56
+
57
+ Recurrence overrides ignore the protected property prefixes specified by RFC 8984 §4.3.5. An `excluded: true` override can contain no other change. Localizations ignore disallowed paths. Patches never partially modify their target.
58
+
59
+ ## Time and recurrence
60
+
61
+ `start` and `due` return `LocalDateTime` values; `duration` returns a `Duration`. `Duration#to_seconds` is the nominal elapsed time, while `add_to` and `Event#end_time` honor calendar days across DST. `SignedDuration` supports positive and negative offsets. JSON and typed values preserve fractional seconds. Conversion to Ruby `Time` raises `UnsupportedError` for leap seconds or precision the runtime cannot retain (for example, subnanosecond fractions on TruffleRuby). Rational seconds must have a finite decimal representation.
62
+
63
+ Floating date-times require an explicit zone for an instant: `event.start_time(time_zone: "Europe/London")`. Occurrence windows use `Time` values, include `from`, and exclude `to`. Without a zone, floating recurrence windows are interpreted as UTC-shaped wall times, and each occurrence still needs a reference zone for `start_time`.
64
+
65
+ An occurrence inherits the base object and applies its override. `recurrence_id` identifies its original wall time, even if an override moves its start. DTSTART is always an included first instance and consumes one COUNT, as specified by JSCalendar. Exclusion rules include DTSTART only when it matches the rule. The result reports `truncated?` when the output/candidate bound cuts off the requested expansion.
66
+
67
+ Time-zone results follow the installed TZInfo data. Its explicit future transition horizon can be shorter than other timezone databases (for example, the current Ruby data ends New York transitions in 2076); do not use the last known offset as a guarantee about distant-future civil time. Custom `timeZones` definitions can express application-owned transition rules.
68
+
69
+ ## Resource bounds
70
+
71
+ | Option | Default |
72
+ | --- | --- |
73
+ | `max_input_size` | 16 MiB |
74
+ | `max_depth` | 32 |
75
+ | `max_map_size` | 10,000 members per collection |
76
+ | `max_data_uri_size` | 1 MiB decoded |
77
+ | `max_patches` | 1,000 |
78
+ | `max_occurrences` | 10,000 candidates per rule |
79
+ | `max_rules` | 100 combined inclusion/exclusion rules |
80
+ | `max_occurrence_bytes` | 32 MiB of expanded calendar data |
81
+
82
+ Pass these keyword options to `parse` or model constructors. `occurrences` additionally requires `to`, defaults to `limit: 1000`, and enforces `timeout: 2.0` seconds over the entire expansion. An exclusion set larger than the candidate budget fails instead of returning an incorrect partial subtraction. Impossible recurrence rules also time out. URIs are data and are never fetched.
83
+
84
+ ## Development and references
85
+
86
+ ```sh
87
+ bundle install
88
+ bundle exec rake test
89
+ COVERAGE=1 bundle exec rake test
90
+ bundle exec rubocop
91
+ bundle exec rbs -I sig validate
92
+ bundle exec ruby script/interop.rb
93
+ bundle exec ruby script/benchmark.rb
94
+ ```
95
+
96
+ RFC source documents and 52 generated examples are fetched into ignored `tmp/` with pinned checksums and a generated-fixture fingerprint. Original text, correction notes and the applicable test-source license notice remain there. RFC material, IANA downloads, external implementations, tests and build artifacts are excluded from the Gem. Authored implementation and validation metadata are MIT licensed.
97
+
98
+ `script/update_registry.rb` checks property and enum identifiers against pinned IANA registries, CLDR calendar names and CSS Level 3 color names. Downloaded sources remain in ignored `tmp/registry/`; only identifiers needed for validation are present in the authored metadata. Updating a checksum requires reviewing the corresponding metadata differences. Source terms are available from [IANA](https://www.iana.org/help/licensing-terms), [Unicode](https://www.unicode.org/license.txt) and [W3C](https://www.w3.org/copyright/document-license/).
99
+
100
+ The interoperability job compares all 42 RFC 5545 recurrence examples with Stalwart's `calcard` 0.3.2. It records one exact upstream UTC UNTIL discrepancy and compares dates before 2050, within the shared time-zone transition horizon. RFC 8984's ten examples all round trip. See [specification decisions](docs/specification.md) for errata and extraction corrections.
data/SECURITY.md ADDED
@@ -0,0 +1,9 @@
1
+ # Security
2
+
3
+ Report vulnerabilities through [GitHub private vulnerability reporting](https://github.com/ydah/jscalendar/security/advisories/new).
4
+ Include a minimal reproducer and the affected gem and Ruby versions.
5
+
6
+ Calendar data can include private locations and contact details. Applications control authentication, sharing, storage and logging.
7
+ Parsing never fetches calendar URIs, executes content, schedules invitations or sends alerts.
8
+ Input size, depth, collection sizes and recurrence work are bounded. Resource limits are hard failures even with `strict: false`.
9
+ Recurrence expansion requires an end time, caps its output and interrupts work after a configurable time budget.
data/data/enums.yml ADDED
@@ -0,0 +1,450 @@
1
+ # RFC 8984 enum identifiers.
2
+ ---
3
+ relation:
4
+ - first
5
+ - next
6
+ - child
7
+ - parent
8
+ display:
9
+ - badge
10
+ - graphic
11
+ - fullsize
12
+ - thumbnail
13
+ relativeTo:
14
+ - start
15
+ - end
16
+ feature:
17
+ - audio
18
+ - chat
19
+ - feed
20
+ - moderator
21
+ - phone
22
+ - screen
23
+ - video
24
+ frequency:
25
+ - yearly
26
+ - monthly
27
+ - weekly
28
+ - daily
29
+ - hourly
30
+ - minutely
31
+ - secondly
32
+ skip:
33
+ - omit
34
+ - backward
35
+ - forward
36
+ day:
37
+ - mo
38
+ - tu
39
+ - we
40
+ - th
41
+ - fr
42
+ - sa
43
+ - su
44
+ freeBusyStatus:
45
+ - free
46
+ - busy
47
+ privacy:
48
+ - public
49
+ - private
50
+ - secret
51
+ kind:
52
+ - individual
53
+ - group
54
+ - location
55
+ - resource
56
+ role:
57
+ - owner
58
+ - attendee
59
+ - optional
60
+ - informational
61
+ - chair
62
+ - contact
63
+ participationStatus:
64
+ - needs-action
65
+ - accepted
66
+ - declined
67
+ - tentative
68
+ - delegated
69
+ scheduleAgent:
70
+ - server
71
+ - client
72
+ - none
73
+ action:
74
+ - display
75
+ - email
76
+ status:
77
+ - confirmed
78
+ - cancelled
79
+ - tentative
80
+ progress:
81
+ - needs-action
82
+ - in-process
83
+ - completed
84
+ - failed
85
+ - cancelled
86
+ rel:
87
+ - about
88
+ - acl
89
+ - alternate
90
+ - amphtml
91
+ - api-catalog
92
+ - appendix
93
+ - apple-touch-icon
94
+ - apple-touch-startup-image
95
+ - archives
96
+ - author
97
+ - blocked-by
98
+ - bookmark
99
+ - c2pa-manifest
100
+ - canonical
101
+ - chapter
102
+ - cite-as
103
+ - collection
104
+ - compression-dictionary
105
+ - contents
106
+ - convertedfrom
107
+ - copyright
108
+ - create-form
109
+ - current
110
+ - deprecation
111
+ - describedby
112
+ - describes
113
+ - disclosure
114
+ - dns-prefetch
115
+ - dpp
116
+ - duplicate
117
+ - edit
118
+ - edit-form
119
+ - edit-media
120
+ - enclosure
121
+ - external
122
+ - first
123
+ - geofeed
124
+ - glossary
125
+ - help
126
+ - hosts
127
+ - hub
128
+ - ice-server
129
+ - icon
130
+ - index
131
+ - intervalafter
132
+ - intervalbefore
133
+ - intervalcontains
134
+ - intervaldisjoint
135
+ - intervalduring
136
+ - intervalequals
137
+ - intervalfinishedby
138
+ - intervalfinishes
139
+ - intervalin
140
+ - intervalmeets
141
+ - intervalmetby
142
+ - intervaloverlappedby
143
+ - intervaloverlaps
144
+ - intervalstartedby
145
+ - intervalstarts
146
+ - item
147
+ - last
148
+ - latest-version
149
+ - license
150
+ - linkset
151
+ - lrdd
152
+ - manifest
153
+ - mask-icon
154
+ - me
155
+ - media-feed
156
+ - memento
157
+ - micropub
158
+ - modulepreload
159
+ - monitor
160
+ - monitor-group
161
+ - next
162
+ - next-archive
163
+ - nofollow
164
+ - noopener
165
+ - noreferrer
166
+ - opener
167
+ - openid2.local_id
168
+ - openid2.provider
169
+ - original
170
+ - p3pv1
171
+ - payment
172
+ - pingback
173
+ - preconnect
174
+ - predecessor-version
175
+ - prefetch
176
+ - preload
177
+ - prerender
178
+ - prev
179
+ - prev-archive
180
+ - preview
181
+ - previous
182
+ - privacy-policy
183
+ - profile
184
+ - publication
185
+ - rdap-active
186
+ - rdap-bottom
187
+ - rdap-down
188
+ - rdap-top
189
+ - rdap-up
190
+ - related
191
+ - replies
192
+ - restconf
193
+ - ruleinput
194
+ - search
195
+ - section
196
+ - self
197
+ - service
198
+ - service-desc
199
+ - service-doc
200
+ - service-meta
201
+ - sip-trunking-capability
202
+ - sponsored
203
+ - start
204
+ - status
205
+ - stylesheet
206
+ - subsection
207
+ - successor-version
208
+ - sunset
209
+ - tag
210
+ - terms-of-service
211
+ - timegate
212
+ - timemap
213
+ - type
214
+ - ugc
215
+ - up
216
+ - version-history
217
+ - via
218
+ - webmention
219
+ - working-copy
220
+ - working-copy-of
221
+ locationType:
222
+ - aircraft
223
+ - airport
224
+ - arena
225
+ - automobile
226
+ - bank
227
+ - bar
228
+ - bicycle
229
+ - bus
230
+ - bus-station
231
+ - cafe
232
+ - campground
233
+ - care-facility
234
+ - classroom
235
+ - club
236
+ - construction
237
+ - convention-center
238
+ - detached-unit
239
+ - fire-station
240
+ - government
241
+ - hospital
242
+ - hotel
243
+ - industrial
244
+ - landmark-address
245
+ - library
246
+ - motorcycle
247
+ - municipal-garage
248
+ - museum
249
+ - office
250
+ - other
251
+ - outdoors
252
+ - parking
253
+ - phone-box
254
+ - place-of-worship
255
+ - post-office
256
+ - prison
257
+ - public
258
+ - public-transport
259
+ - residence
260
+ - restaurant
261
+ - school
262
+ - shopping-area
263
+ - stadium
264
+ - store
265
+ - street
266
+ - theater
267
+ - toll-booth
268
+ - town-hall
269
+ - train
270
+ - train-station
271
+ - truck
272
+ - underway
273
+ - unknown
274
+ - utilitybox
275
+ - warehouse
276
+ - waste-transfer-facility
277
+ - water
278
+ - water-facility
279
+ - watercraft
280
+ - youth-camp
281
+ rscale:
282
+ - buddhist
283
+ - chinese
284
+ - coptic
285
+ - dangi
286
+ - ethioaa
287
+ - ethiopic
288
+ - ethiopic-amete-alem
289
+ - gregorian
290
+ - gregory
291
+ - hebrew
292
+ - indian
293
+ - islamic
294
+ - islamic-civil
295
+ - islamic-rgsa
296
+ - islamic-tbla
297
+ - islamic-umalqura
298
+ - islamicc
299
+ - iso8601
300
+ - japanese
301
+ - persian
302
+ - roc
303
+ color:
304
+ - aliceblue
305
+ - antiquewhite
306
+ - aqua
307
+ - aquamarine
308
+ - azure
309
+ - beige
310
+ - bisque
311
+ - black
312
+ - blanchedalmond
313
+ - blue
314
+ - blueviolet
315
+ - brown
316
+ - burlywood
317
+ - cadetblue
318
+ - chartreuse
319
+ - chocolate
320
+ - coral
321
+ - cornflowerblue
322
+ - cornsilk
323
+ - crimson
324
+ - cyan
325
+ - darkblue
326
+ - darkcyan
327
+ - darkgoldenrod
328
+ - darkgray
329
+ - darkgreen
330
+ - darkgrey
331
+ - darkkhaki
332
+ - darkmagenta
333
+ - darkolivegreen
334
+ - darkorange
335
+ - darkorchid
336
+ - darkred
337
+ - darksalmon
338
+ - darkseagreen
339
+ - darkslateblue
340
+ - darkslategray
341
+ - darkslategrey
342
+ - darkturquoise
343
+ - darkviolet
344
+ - deeppink
345
+ - deepskyblue
346
+ - dimgray
347
+ - dimgrey
348
+ - dodgerblue
349
+ - firebrick
350
+ - floralwhite
351
+ - forestgreen
352
+ - fuchsia
353
+ - gainsboro
354
+ - ghostwhite
355
+ - gold
356
+ - goldenrod
357
+ - gray
358
+ - green
359
+ - greenyellow
360
+ - grey
361
+ - honeydew
362
+ - hotpink
363
+ - indianred
364
+ - indigo
365
+ - ivory
366
+ - khaki
367
+ - lavender
368
+ - lavenderblush
369
+ - lawngreen
370
+ - lemonchiffon
371
+ - lightblue
372
+ - lightcoral
373
+ - lightcyan
374
+ - lightgoldenrodyellow
375
+ - lightgray
376
+ - lightgreen
377
+ - lightgrey
378
+ - lightpink
379
+ - lightsalmon
380
+ - lightseagreen
381
+ - lightskyblue
382
+ - lightslategray
383
+ - lightslategrey
384
+ - lightsteelblue
385
+ - lightyellow
386
+ - lime
387
+ - limegreen
388
+ - linen
389
+ - magenta
390
+ - maroon
391
+ - mediumaquamarine
392
+ - mediumblue
393
+ - mediumorchid
394
+ - mediumpurple
395
+ - mediumseagreen
396
+ - mediumslateblue
397
+ - mediumspringgreen
398
+ - mediumturquoise
399
+ - mediumvioletred
400
+ - midnightblue
401
+ - mintcream
402
+ - mistyrose
403
+ - moccasin
404
+ - navajowhite
405
+ - navy
406
+ - oldlace
407
+ - olive
408
+ - olivedrab
409
+ - orange
410
+ - orangered
411
+ - orchid
412
+ - palegoldenrod
413
+ - palegreen
414
+ - paleturquoise
415
+ - palevioletred
416
+ - papayawhip
417
+ - peachpuff
418
+ - peru
419
+ - pink
420
+ - plum
421
+ - powderblue
422
+ - purple
423
+ - red
424
+ - rosybrown
425
+ - royalblue
426
+ - saddlebrown
427
+ - salmon
428
+ - sandybrown
429
+ - seagreen
430
+ - seashell
431
+ - sienna
432
+ - silver
433
+ - skyblue
434
+ - slateblue
435
+ - slategray
436
+ - slategrey
437
+ - snow
438
+ - springgreen
439
+ - steelblue
440
+ - tan
441
+ - teal
442
+ - thistle
443
+ - tomato
444
+ - turquoise
445
+ - violet
446
+ - wheat
447
+ - white
448
+ - whitesmoke
449
+ - yellow
450
+ - yellowgreen