gedcomkit 0.1.17__tar.gz → 0.1.19__tar.gz

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.
Files changed (100) hide show
  1. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/CHANGELOG.md +211 -0
  2. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/Cargo.lock +1 -1
  3. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/Cargo.toml +1 -1
  4. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/PKG-INFO +1 -1
  5. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/README.md +3 -0
  6. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/bindings/gedcomkit-py/Cargo.lock +2 -2
  7. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/bindings/gedcomkit-py/Cargo.toml +1 -1
  8. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/bindings/gedcomkit-py/gedcomkit.pyi +32 -3
  9. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/bindings/gedcomkit-py/src/lib.rs +7 -2
  10. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/src/convert.rs +412 -7
  11. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/src/dates.rs +574 -77
  12. gedcomkit-0.1.19/src/validate.rs +3508 -0
  13. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/src/view.rs +42 -1
  14. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/tests/typescript.rs +1 -0
  15. gedcomkit-0.1.17/src/validate.rs +0 -855
  16. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/LICENSE +0 -0
  17. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/bindings/gedcomkit-py/LICENSE +0 -0
  18. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/bindings/gedcomkit-py/README.md +0 -0
  19. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom-spec/LICENSE +0 -0
  20. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom-spec/README.md +0 -0
  21. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom-spec/cardinalities.tsv +0 -0
  22. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom-spec/enumerations.tsv +0 -0
  23. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom-spec/enumerationsets.tsv +0 -0
  24. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom-spec/grammar.abnf +0 -0
  25. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom-spec/grammar.gedstruct +0 -0
  26. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom-spec/payloads.tsv +0 -0
  27. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom-spec/substructures.tsv +0 -0
  28. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/age-invalid.ged +0 -0
  29. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/age-valid.ged +0 -0
  30. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/atsign.ged +0 -0
  31. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/char_ascii_1.ged +0 -0
  32. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/char_ascii_2.ged +0 -0
  33. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/char_utf16be-1.ged +0 -0
  34. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/char_utf16be-2.ged +0 -0
  35. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/char_utf16le-1.ged +0 -0
  36. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/char_utf16le-2.ged +0 -0
  37. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/char_utf8-1.ged +0 -0
  38. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/char_utf8-2.ged +0 -0
  39. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/char_utf8-3.ged +0 -0
  40. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/date-all.ged +0 -0
  41. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/date-dual-invalid.ged +0 -0
  42. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/date-dual-valid.ged +0 -0
  43. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/enum-ext.ged +0 -0
  44. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/filename-1.ged +0 -0
  45. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/lang-all.ged +0 -0
  46. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/notes-1.ged +0 -0
  47. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/obje-1.ged +0 -0
  48. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/obsolete-1.ged +0 -0
  49. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/pedi-1.ged +0 -0
  50. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/rela_1.ged +0 -0
  51. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/sour-1.ged +0 -0
  52. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/tiny-1.ged +0 -0
  53. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/5/xref-case.ged +0 -0
  54. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/age-invalid.ged +0 -0
  55. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/age-valid.ged +0 -0
  56. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/atsign.ged +0 -0
  57. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/char_ascii_1.ged +0 -0
  58. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/char_ascii_2.ged +0 -0
  59. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/char_utf16be-1.ged +0 -0
  60. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/char_utf16be-2.ged +0 -0
  61. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/char_utf16le-1.ged +0 -0
  62. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/char_utf16le-2.ged +0 -0
  63. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/char_utf8-1.ged +0 -0
  64. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/char_utf8-2.ged +0 -0
  65. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/char_utf8-3.ged +0 -0
  66. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/date-all.ged +0 -0
  67. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/date-dual-invalid.ged +0 -0
  68. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/enum-ext.ged +0 -0
  69. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/filename-1.ged +0 -0
  70. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/lang-all.ged +0 -0
  71. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/notes-1.ged +0 -0
  72. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/obje-1.ged +0 -0
  73. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/obsolete-1.ged +0 -0
  74. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/pedi-1.ged +0 -0
  75. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/rela_1.ged +0 -0
  76. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/sour-1.ged +0 -0
  77. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/tiny-1.ged +0 -0
  78. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/7/xref-case.ged +0 -0
  79. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/LICENSE +0 -0
  80. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/gedcom7code/UPSTREAM-README.md +0 -0
  81. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/zip-interop/README.md +0 -0
  82. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/zip-interop/python-zip64.gdz +0 -0
  83. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/fixtures/vendored/zip-interop/python-zipfile.gdz +0 -0
  84. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/pyproject.toml +0 -0
  85. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/src/build.rs +0 -0
  86. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/src/decode.rs +0 -0
  87. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/src/family.rs +0 -0
  88. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/src/gedzip.rs +0 -0
  89. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/src/lib.rs +0 -0
  90. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/src/names.rs +0 -0
  91. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/src/schema.rs +0 -0
  92. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/src/synthetic.rs +0 -0
  93. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/src/tags.rs +0 -0
  94. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/tests/endurance.rs +0 -0
  95. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/tests/external_charset.rs +0 -0
  96. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/tests/properties.rs +0 -0
  97. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/tests/requirement_corpus.rs +0 -0
  98. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/tests/spec_conformance.rs +0 -0
  99. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/tests/vendored_corpus.rs +0 -0
  100. {gedcomkit-0.1.17 → gedcomkit-0.1.19}/tests/version_conversion.rs +0 -0
@@ -5,6 +5,217 @@ format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
5
5
  the project uses semantic versioning within the usual pre-1.0 compatibility
6
6
  rules.
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.19] - 2026-10-17
11
+
12
+ ### Added
13
+
14
+ - `view::EventView::date_phrase` (`datePhrase` in the serialized view, so
15
+ in both bindings and the TypeScript definitions): the `PHRASE` beneath an
16
+ event's `DATE`, exactly as written. A GEDCOM 7 date with an empty payload
17
+ and a `PHRASE`, or a date with a phrase beside it, showed nothing of the
18
+ phrase before. A 5.5.1 phrase is written inside the payload, which `date`
19
+ already holds. The Python stub gains an `EventView` typed dict naming
20
+ every key of an event.
21
+ - The date reader reads ISO 8601's year and month: `1834-02` is February
22
+ 1834, as `1834-02-10` was already 10 February 1834. Only four digits, a
23
+ hyphen and two digits are read, and only when the month is no more than
24
+ the year's last two digits: `1808-10`, `1711-12` and `2010-11` are as
25
+ often a span of years or a dual year as a month, and stay unparsed. So
26
+ does every month of a year ending in 00 (`1800-01` to `1800-12`), and
27
+ the later months of a year ending in 01 to 11 (`2005-06` to `2005-12`).
28
+
29
+ ### Changed
30
+
31
+ - `validate::inspect` no longer reports two events out of order when an
32
+ approximate date's leeway could put them in order, which is how
33
+ `conflicting-vitals` already read them. A birth `ABT 1854` with a
34
+ christening in 1853 was a `christened-before-born` question; it is now
35
+ no finding. The same holds for `died-before-born`, `buried-before-died`,
36
+ `event-after-death`, `event-before-birth`, `married-before-born`,
37
+ `married-after-died` and `child-born-after-mother-died`, each of which
38
+ now reports only what is out of order beyond the leeway, as a
39
+ contradiction (a question for an `EVEN` or `FACT`, as before). Within the
40
+ leeway no other check fires in its place: a marriage in 1849 for somebody
41
+ born about 1850 is not `married-as-a-child`. Bounds (`BEF`, `AFT`, `BET`)
42
+ are read as the ranges they admit, unchanged.
43
+ - `convert::to_version_5_5_1` turns a name type back into 5.5.1's
44
+ spelling: `OTHER` with a one-line `PHRASE` becomes the phrase's words,
45
+ and an enumerated value is written in lower case (`BIRTH` becomes
46
+ `birth`). `OTHER` whose words are themselves an enumerated value (the
47
+ phrase `birth`) stays `other`, its `PHRASE` kept and reported, since the
48
+ words alone would convert back up as `BIRTH`. A `PHRASE` beside any other
49
+ value is kept and reported.
50
+ - `DatePoint::from_ymd` refuses a day its month never has (30 February,
51
+ 31 April, day 0). The point is written with no calendar escape, which
52
+ makes it Gregorian, so 29 February is taken only in a Gregorian leap
53
+ year: 2000, not 1900.
54
+
55
+ ### Fixed
56
+
57
+ - A range with an end the date reader cannot read is no longer read in
58
+ part. `FROM ABT 1924 TO 1959` was read as `TO 1959`, dropping the start
59
+ without a word, and `BET 1950 AND JULIAN DEC 302` as a range with only
60
+ its first end; `FROM Infant` was a period with neither end. Each is now
61
+ `DateKind::Unparsed`, keeps its text as the phrase, and still offers its
62
+ year to sort by; `to_version_7` keeps it whole as an empty `DATE` with a
63
+ `PHRASE`. A range whose ends are both dates reads as before.
64
+ - The date reader no longer accepts a day its month does not have.
65
+ `1834-02-30` and `30 FEB 1834` were read as 30 February 1834; they are
66
+ now unparsed, as is `29 FEB 1834`. Leap years follow the payload's
67
+ calendar: `@#DJULIAN@ 29 FEB 1900` is read and `@#DGREGORIAN@ 29 FEB
68
+ 1900` is not. A payload with no calendar escape keeps the Julian leap
69
+ day, since files often leave the escape off a Julian date, so
70
+ `29 FEB 1900` is still read; ISO 8601's `1900-02-29` is not, since ISO
71
+ 8601 is Gregorian by definition. A dual year's leap day is its second
72
+ year's, so `29 FEB 1711/12` and `@#DJULIAN@ 29 FEB 1711/12` are read. A
73
+ Roman or unrecognized calendar is held to the Julian month lengths, and
74
+ no Hebrew or French Republican month has more than 30 days. A numeric
75
+ date with a month or day out of range (`1834-13-05`, `13/05/1834`) is
76
+ unparsed too; before, the piece out of range was dropped and the rest
77
+ read (`1834-13-05` as the year 1834).
78
+ - `convert::to_version_5_5_1` no longer writes `INT (…)`, an interpreted
79
+ date with no date, for a version 7 `DATE` that is empty beside its
80
+ `PHRASE`; 5.5.1 does not allow it. The phrase is written as 5.5.1's date
81
+ phrase, `(FROM ABT 1924 TO 1959)`, or as the date itself when it is
82
+ already one in 5.5.1's grammar that version 7 cannot spell, so a dual
83
+ year (`1740/41`) comes back as the date it was.
84
+ - `convert::to_version_7` / `to_version_7_with` convert a personal name's
85
+ type (`INDI`.`NAME`.`TYPE`), which 5.5.1 writes as free text and version
86
+ 7 as an enumeration. A value that is one of version 7's words in any case
87
+ (`aka`, `birth`, `immigrant`, `maiden`, `married`, `professional`,
88
+ `other`) becomes that word in upper case; any other becomes `OTHER`, with
89
+ the text kept verbatim as a `PHRASE`, the way a role has been converted
90
+ since 0.1.13. Before, `TYPE aka` was carried into the 7.0 document as it
91
+ stood, where it is not valid. A type that already has a `PHRASE`, is
92
+ empty, or has a continuation line with substructures is kept and
93
+ reported.
94
+
95
+ ## [0.1.18] - 2026-10-16
96
+
97
+ ### Added
98
+
99
+ - `validate::inspect` runs nine more checks. Each has a stable name, and
100
+ the module documentation now lists every check with its severity:
101
+ - `married-after-died` (contradiction): a family's first `MARR` wholly
102
+ after a partner's death, or, failing a dated death, their burial. A
103
+ burial before the wedding beside a death after it is no finding here:
104
+ the death says when the life ended, and the burial is
105
+ `buried-before-died`'s.
106
+ - `child-born-long-after-father-died`: a child born more than 300 days
107
+ after the death of the family's `HUSB`. A contradiction when both dates
108
+ name a day; a question otherwise, since a bare year is often a guess.
109
+ - `christened-before-born` (contradiction): a `CHR`, `CHRA` or `BAPM`
110
+ wholly before the birth.
111
+ - `event-before-birth`: the mirror of `event-after-death`, for any other
112
+ event or attribute wholly before the birth year. A burial or cremation
113
+ is left to `died-before-born` and `buried-before-died` when the person
114
+ has a dated death.
115
+ - `buried-long-after-died` (question): a burial more than 365 days after
116
+ the death, counted from the latest day the death allows to the earliest
117
+ the burial does.
118
+ - `children-too-close`: two children of one mother born 2 to 270 days
119
+ apart. A contradiction when both dates name a day and are under 150
120
+ days apart; a question otherwise, since a very early birth makes the
121
+ longer gaps possible. A mother's children are taken from every family
122
+ she is the `WIFE` in; a family with no `WIFE` is compared alone.
123
+ - `possible-duplicate-child` (question): two children of one mother with
124
+ one given name (one name may be the start of the other: `Mira` and
125
+ `Mira Elowen`, but not `Mira Elowen` and `Mira Rose`), born on one
126
+ date, or on one day and month of different years.
127
+ - `conflicting-vitals` (question): two `BIRT`, or two `DEAT`, events of
128
+ one person that cannot both be true, even with an approximate date
129
+ allowed a year either way (`BIRT 1851` and `BIRT ABT 1850` are not in
130
+ conflict). GEDCOM lets a file record alternatives with the preferred
131
+ first, which is the one the other checks read, so this asks rather
132
+ than accuses.
133
+ - `own-ancestor` (contradiction): a loop in the parent links, read from
134
+ both directions of the family link as `FamilyGraph` reads them. One
135
+ finding per loop, on the person in it the file lists first, naming the
136
+ shortest way round: in full up to six steps, and past that its first
137
+ five and how many more, so a loop through 20,000 people is one short
138
+ message.
139
+
140
+ The three checks that compare pairs are bounded, so a hostile file cannot
141
+ make the report as large as the square of its people. `children-too-close`
142
+ and `possible-duplicate-child` report at most ten pairs for one mother,
143
+ and `conflicting-vitals` ten for one person's births and ten for their
144
+ deaths, the first in the order the file lists them; one more finding under
145
+ the same check, as serious as the most serious pair it stands for, then
146
+ says how many more pairs there are and how many in all (`4059990 more
147
+ pairs of children born 2 to 270 days apart, 4060000 in all`). The work is
148
+ bounded too: births and deaths are counted by sorting rather than compared
149
+ pair by pair; children who share a span of birth days, or a given name,
150
+ are compared as one group; and one mother's children are compared at most
151
+ 100,000 times for each check, past which the summary gives its counts as
152
+ lower bounds. A family of 3,000 children of one name born in one month,
153
+ which made 4.5 million sibling findings, makes 22; a person with 5,000
154
+ births, which made 12.5 million `conflicting-vitals` findings, makes 11.
155
+
156
+ ### Changed
157
+
158
+ - PyPI gets one wheel, Linux x86_64, beside the source distribution. The
159
+ macOS and Windows wheels are no longer cross-built; on those platforms pip
160
+ builds from the sdist, which needs a Rust toolchain.
161
+ - `died-before-born`, `buried-before-died`, `christened-before-born`,
162
+ `married-after-died` and `conflicting-vitals` compare as finely as both
163
+ dates allow: a burial on 12 September before a death on 14 September of
164
+ one year is now found. Julian and Gregorian dates are put on one count of days first.
165
+ An approximate date (`ABT`, `CAL`, `EST`) still vouches for its year and
166
+ nothing finer, and a bound (`BEF`, `AFT`, `BET`) admits its own day. A
167
+ finding the years alone decide keeps its wording (`born 1900 and died
168
+ 1880`); one only a finer comparison finds names the dates in full
169
+ (`died 14 September 1862 and buried 12 September 1862`).
170
+ - An approximate date (`ABT`, `CAL`, `EST`) may be a year out. A finding
171
+ that two events are out of order is a question, not a contradiction, when
172
+ moving each approximate date a year either way could put them in order:
173
+ `BIRT ABT 1850` with `CHR 1849` or `CENS 1849`, or `DEAT ABT 1850` with a
174
+ burial in 1849. Two years out it is still a contradiction. This holds for
175
+ `died-before-born`, `buried-before-died`, `event-after-death`,
176
+ `married-before-born` and `child-born-after-mother-died`, and for the new
177
+ `christened-before-born`, `event-before-birth` and `married-after-died`;
178
+ the checks about an age or an interval take an approximate year as
179
+ written. Their messages say an approximate date is one: `born about 1850,
180
+ and Christening is dated 1849`.
181
+ - `event-after-death` is a question, not a contradiction, for an `EVEN` or
182
+ `FACT`. Such an event is whatever its free-text `TYPE` says — a land
183
+ entry completed, a patent issued, an estate settled — and many of those
184
+ are done in a dead person's name. The other events keep their severity.
185
+ - `by` is read as a `BEF` qualifier: `by 5 October 1752` is
186
+ `BEF 5 OCT 1752`, where it was read as 5 May 1752. The qualifiers the
187
+ reader already knew (`abt`, `about`, `c.`, `ca.`, `bef`, `before`, `aft`,
188
+ `after`, and the others) are unchanged.
189
+
190
+ ### Fixed
191
+
192
+ - The date reader no longer shifts the pieces of a four-word date around a
193
+ word it does not know. `chr 10 Nov 1788` read as 10 October 1788,
194
+ `b 9 Jun 1750` as 9 September, `bapt. 3 May 1801` as 3 March: the leading
195
+ word was tried as a month, failed, and the day number was taken as the
196
+ month instead. Four pieces are now read only as a day, its ordinal ending
197
+ and a month written as a word, in either order (`Feb 11 th 1809`,
198
+ `11 th Feb 1809`, `1er janvier 1800`, `5te Januar 1800`); anything else is
199
+ `DateKind::Unparsed`, keeps its text as the phrase, and still offers its
200
+ year to sort by, so `to_gedcom_5_5_1` and `to_gedcom_7` return `None` and
201
+ the caller writes it as a date phrase. A month given only as a number never
202
+ decides the order of four pieces.
203
+ - A dual year (`10 JAN 1731/32`) admits both its years in every check, not
204
+ only where days are compared. The checks that compare years read it as
205
+ its first year alone, so `BIRT 1732` with `DEAT 10 JAN 1731/32` was
206
+ `died-before-born` ("born 1732 and died 1731"); it is now no finding, and
207
+ a death past both years says `died between 1731 and 1732`. Where the days
208
+ of two dates are compared, they alone decide the order, so a burial in the
209
+ Julian year 1700, which runs eleven days into the Gregorian 1701, is no
210
+ longer wholly before a death in the Gregorian 1701.
211
+ - `child-listed-twice` keeps the children it has seen in a set rather than
212
+ a list, so a family listing tens of thousands of children is checked in
213
+ time proportional to their number, not its square.
214
+ - The checks on an individual's own record read that record's dates when it
215
+ has no identifier, or shares one with another record. Before, such a
216
+ record was checked against no dates, or against those of the last record
217
+ with its identifier.
218
+
8
219
  ## [0.1.17] - 2026-10-16
9
220
 
10
221
  ### Changed
@@ -35,7 +35,7 @@ dependencies = [
35
35
 
36
36
  [[package]]
37
37
  name = "gedcomkit"
38
- version = "0.1.17"
38
+ version = "0.1.19"
39
39
  dependencies = [
40
40
  "flate2",
41
41
  "serde",
@@ -3,7 +3,7 @@
3
3
  # was checked on crates.io, npm, and PyPI before the initial release.
4
4
  [package]
5
5
  name = "gedcomkit"
6
- version = "0.1.17"
6
+ version = "0.1.19"
7
7
  description = "A byte-preserving GEDCOM document model: decoding, parsing, readings, version conversion, plausibility checks, and the GEDZIP container, for GEDCOM 5.5 through 7.x."
8
8
  edition = "2024"
9
9
  # The true minimum, not the workspace's toolchain: a parser library is a leaf
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gedcomkit
3
- Version: 0.1.17
3
+ Version: 0.1.19
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: Programming Language :: Python :: Implementation :: CPython
@@ -23,6 +23,9 @@ Python:
23
23
  python -m pip install gedcomkit
24
24
  ```
25
25
 
26
+ Wheels are published for Linux x86_64 only. Elsewhere pip builds the package from
27
+ source, which needs a Rust toolchain ([rustup](https://rustup.rs)).
28
+
26
29
  Node.js / WASM:
27
30
 
28
31
  ```bash
@@ -35,7 +35,7 @@ dependencies = [
35
35
 
36
36
  [[package]]
37
37
  name = "gedcomkit"
38
- version = "0.1.17"
38
+ version = "0.1.19"
39
39
  dependencies = [
40
40
  "flate2",
41
41
  "serde",
@@ -43,7 +43,7 @@ dependencies = [
43
43
 
44
44
  [[package]]
45
45
  name = "gedcomkit-py"
46
- version = "0.1.17"
46
+ version = "0.1.19"
47
47
  dependencies = [
48
48
  "gedcomkit",
49
49
  "pyo3",
@@ -2,7 +2,7 @@
2
2
  # abi3 keeps it to one wheel per platform across Python versions.
3
3
  [package]
4
4
  name = "gedcomkit-py"
5
- version = "0.1.17"
5
+ version = "0.1.19"
6
6
  description = "Python bindings for gedcomkit: an archival-grade GEDCOM library — byte-preserving round-trip, encoding detection, GEDCOM 5.5 through 7.x, GEDZIP."
7
7
  edition = "2024"
8
8
  rust-version = "1.88"
@@ -19,6 +19,29 @@ class RecordSpec(TypedDict):
19
19
  value: NotRequired[str | None]
20
20
  children: NotRequired[list[RecordSpec]]
21
21
 
22
+ class EventView(TypedDict):
23
+ """One entry of an individual's or a family's ``events``.
24
+
25
+ ``date`` is the DATE payload exactly as written. ``datePhrase`` is the
26
+ GEDCOM 7 PHRASE beneath that DATE, or ``None``: beside a date, or the
27
+ only text an empty 7.0 DATE has. A 5.5.1 phrase is part of ``date``.
28
+ """
29
+
30
+ tag: str
31
+ label: str
32
+ date: str | None
33
+ datePhrase: str | None
34
+ dateDisplay: str | None
35
+ year: int | None
36
+ sortKey: int | None
37
+ place: str | None
38
+ age: str | None
39
+ cause: str | None
40
+ attribute: bool
41
+ value: str | None
42
+ citations: list[str]
43
+ path: list[int]
44
+
22
45
  class WrappedLine(TypedDict):
23
46
  record: str
24
47
  path: list[int]
@@ -42,9 +65,15 @@ class Gedcom:
42
65
  def content_records(self) -> int: ...
43
66
  def records(self) -> list[dict[str, Any]]: ...
44
67
  def record(self, xref: str) -> dict[str, Any]: ...
45
- def individuals(self) -> list[dict[str, Any]]: ...
46
- def families(self) -> list[dict[str, Any]]: ...
47
- def validate(self) -> list[dict[str, Any]]: ...
68
+ def individuals(self) -> list[dict[str, Any]]:
69
+ """Every individual's view; each of its ``events`` is an ``EventView``."""
70
+ def families(self) -> list[dict[str, Any]]:
71
+ """Every family's view; each of its ``events`` is an ``EventView``."""
72
+ def validate(self) -> list[dict[str, Any]]:
73
+ """Plausibility findings, each with ``record``, ``check``, ``severity``
74
+ and ``message``. ``check`` is the core's stable rule name, such as
75
+ ``died-before-born`` or ``own-ancestor``; the core's ``validate``
76
+ module documentation lists every one with its severity."""
48
77
  def unresolved_pointers(self) -> list[dict[str, Any]]: ...
49
78
  def parents_of(self, xref: str) -> list[str]: ...
50
79
  def children_of(self, xref: str) -> list[str]: ...
@@ -224,6 +224,8 @@ impl Gedcom {
224
224
  }
225
225
 
226
226
  /// Every individual, read for display: names, events, families, IDs.
227
+ /// Each event has its `date` payload as written and, beside it,
228
+ /// `datePhrase`: the GEDCOM 7 `PHRASE` under the date, or `None`.
227
229
  fn individuals(&self, py: Python<'_>) -> PyResult<Vec<Py<PyAny>>> {
228
230
  self.document
229
231
  .records_of("INDI")
@@ -231,7 +233,7 @@ impl Gedcom {
231
233
  .collect()
232
234
  }
233
235
 
234
- /// Every family record's view.
236
+ /// Every family record's view, its events shaped as `individuals`' are.
235
237
  fn families(&self, py: Python<'_>) -> PyResult<Vec<Py<PyAny>>> {
236
238
  self.document
237
239
  .records_of("FAM")
@@ -240,7 +242,10 @@ impl Gedcom {
240
242
  }
241
243
 
242
244
  /// Plausibility findings: contradictions, questions, and what cites
243
- /// nothing — reported with the numbers they were drawn from.
245
+ /// nothing — reported with the numbers they were drawn from. Each has
246
+ /// `record`, `check`, `severity` and `message`; `check` is the core's
247
+ /// stable rule name (`died-before-born`, `own-ancestor`, …), listed with
248
+ /// its severity in the core's `validate` module documentation.
244
249
  fn validate(&self, py: Python<'_>) -> PyResult<Vec<Py<PyAny>>> {
245
250
  gedcomkit::validate::inspect(&self.document)
246
251
  .iter()