Releases: bertalhia/swift-chrono-pt
Releases · bertalhia/swift-chrono-pt
Release list
0.13.0
Added
Match.isApproximate: "umas 8", "por volta de 15h", "daqui a pouco",
"meados de outubro".ChronoPT.strippingDates(_:from:)takes matches an app already has.Recurrence.rrule(allDayIn:)writes UNTIL as a date for events with no
time, andRecurrence.recurrenceRule(in:)gives Foundation's
Calendar.RecurrenceRulefrom iOS 18 and macOS 15.- A weekday that ends a phrase is a day: "dentista terça", "ligar sexta pro
João". After "a", "o" or "em" it stays an ordinal. - A weekday after its date: "15/10, quinta", "28/9 (seg)", "ter., 29 de set.".
- Opening phrases: "a partir de segunda", "antes das 10", "depois das 18h",
also taken out by strippingDates with the brackets around a date. - Chat spellings: "semana q vem", "próx. segunda", "sexta próxima", "dia 1°",
"15hrs30", "td dia"; with past dates, "sábado retrasado". - "meio dia" without a hyphen next to a day or with minutes: "amanhã meio
dia", "meio dia e meia". - Short hours and prescriptions: "a cada 4h", "8/8h", "12/12h por 10 dias";
"daqui uns 10 dias", "1 mês e meio", "5 minutinhos". - Repeating forms: "todo dia útil", "todo fim de semana", "todo fim de mês",
"todo dia 15 e 30", "toda segunda a sexta", "segundas e quartas às 19h",
"toda semana na quarta", "quinzenal às quintas", "toda noite às 22h".
Fixed
- "todo dia útil" was daily, weekends included.
- "1 hora de academia" and "2 horas de estudo" were clock times.
Performance
- Compiled regexes are shared through a pool: after the first parse in a
process, a parse on a new thread takes 0.5 ms instead of 10 ms, and memory
follows how many parses run at once, not how many threads ran one. - Business days keep each year's holidays for the length of a count: "em 999
dias úteis" went from 8.4 ms to 1.4 ms with interpret.
0.12.0
The API as it is meant to stay for 1.0. See Stability in the README.
Breaking
Recurrence.weekdaysis a set ofRecurrence.Weekday, each with its own
place in the month (.every(.monday),.nth(-1, .friday));
weekdayOrdinalis gone.Recurrence.timesPerPeriodbecomesrate, a count with its own unit.RecurrenceandOptionshave chosenCodableforms:Endwrites
{"until": …}or{"count": 5}, sets are written in order, invalid values
are refused, andOptionswritesdefaultHour. Options saved by 0.x still
decode.Options.momentsmaps phrases toChronoPT.TimeOfDay, so a moment can
have minutes;["no treino": 7]still compiles.Match.intervalbecomesdateInterval: whole days for a date with no time,
start to end for a time range.Parserdefaults toCalendar.autoupdatingCurrent.
Added
Recurrence.timesOfDay: "remédio às 8h e às 20h todo dia" is one daily rule
at both times.PartialDate.timeZonekeeps the zone the text named.- A day inside a period: "semana que vem, na quarta", "dia 25 do mês que vem",
"em outubro, dia 5", "dia 20 de outubro do ano que vem". - Named days in a month: "a primeira segunda-feira de outubro", "na última
sexta do mês", "fim de outubro", "meados de outubro", "na primeira semana de
outubro", "5º dia útil", "último dia útil de outubro". - Ranges: "do dia 10 ao 15", "de 10 a 15/10", "10-15 de outubro", "de outubro
a dezembro", and a time at each end, "de segunda às 14h até sexta às 18h". - From now: "daqui a 1h", "em 30min", "daqui 1h30", "daqui a 2 horas e meia".
- "às 7:30 pm", "à uma", "às 3 h", "9:30hrs"; "no natal de 2027"; "toda terça,
3 vezes ao dia". - A Stability section in the README, and doc comments on every public symbol.
Fixed
- "dia 10 às 14h" was a range from 10:00 to 14:00; it is the 10th at 14:00.
- A second clock time was dropped ("às 8h e às 20h"), and interpret took a
time that belonged to another day. - "todo dia 30 minutos" was monthly; "em setembro" said in September meant
next year; weekends counted wrong on a Sunday. - Ordinals, fractions, scores and street names were read as dates: "na
segunda fase", "1/2 xícara", "Rua 25 de Março". - "as 2 crianças" and "reunião de 2h" were times; "hoje às 9" said at 10:00
gave the past 9:00 instead of 21:00. - Normalization shifted positions after some Unicode marks, and
strippingDates was quadratic in a run of spaces.
Performance
- Joining steps look only at neighbouring candidates, and each rule tries its
regex only near its trigger words: 7 KB of notes with a date on every line
went from 100 ms to 28 ms, and repeated dates that took seconds take tens
of milliseconds.
0.11.0
Added
- Approximate hours: "umas 8", "lá pras 3", "por volta de 15h", "em torno
das 10", "perto do meio-dia". They read like exact ones, and a bare number
needs the end of a phrase, so "umas 8 laranjas" and "umas 2 horas" are not
times. - Approximate amounts from now: "em uns 15 minutos", "daqui umas 2 horas".
- With past dates on: "semana retrasada", "mês retrasado", "ano retrasado",
and a redundant "atrás" in "há duas semanas atrás". - The bug report form has an optional context field (thanks, @anjugoyal954).
0.10.0
Breaking
Recurrenceis a struct with the fields of an iCalendar rule (RFC 5545),
the same onesEKRecurrenceRuletakes:frequency,interval,
weekdays,weekdayOrdinal,daysOfMonth,months,timesPerPeriodand
end..daily(),.weekly(on:),.monthly(day:),.yearly(),
.hourly(every:)and.minutely(every:)build the common ones. One reading
has one value: "diariamente" and "a cada 1 dia" are both.daily().Recurrence.descriptionis itsrrule: "FREQ=WEEKLY;BYDAY=MO,WE".
Added
- Where a repeating date stops: "toda terça até dezembro", "todo dia por 10
dias", "toda segunda, 5 vezes", inRecurrence.end. - More repeating dates: "3x ao dia", "duas vezes por semana", "dia sim, dia
não", "toda última sexta do mês", "todo ano em julho", "todo 25 de
dezembro". Match.isAllDayfor "amanhã o dia todo"; whole parts of the day, "a manhã
toda" (6:00 to 12:00), "a tarde inteira", "a noite toda".- Lengths of time: "por 3 dias", "nos próximos 5 dias", "amanhã por uma
semana", "durante a semana". - Semesters, trimesters and halves of the month: "no primeiro semestre", "no
próximo trimestre", "primeira quinzena de outubro". - A weekday with a bare day number: "sexta, 25" is the 25th when it is a
Friday. - Time zones after a clock time: "15h BRT", "10h UTC", "16h GMT-3", "às 9
horário de Brasília".knownComponentshas.timeZone. - English clock times and ISO date-times: "9am", "7:30 pm",
"2026-10-15T14:30:00-03:00". Options.moments, an app's own phrases with the hour each one means:
["no treino": 7].
Fixed
- "todo dia 25 de dezembro" is yearly, not monthly.
- "na 2ª quinzena" and "na segunda quinzena" are no longer Monday.
Performance
- Mapping results back to the original text resumes where the last one
stopped instead of walking from the start each time.
0.9.0
Added
ChronoPT.Parserkeeps a calendar and options for an app that reads many
texts the same way.PartialDate.alternative: "às 7" reads as 19:00, and the alternative holds
7:00, so a UI can offer it.- Dates counted from another: "dois dias antes do natal", "véspera do ano
novo", "uma semana depois do dia 10". - Vague times: "daqui a pouco" (30 minutes) and "mais tarde" (2 hours).
"agora" alone stays unread. - Date formats: "10/out", "25-12-2027", "25.12.2027", "dez/2027", "12/2027".
Recurrencehas a stabledescription, weekdays in week order.- DocC articles: getting started, the grammar, and recipes.
Changed
- A time counted from now joins a day when it lands on that day: "hoje mais
tarde" is two hours from now.
Performance
- Rules skip their regex when the text holds none of the words it needs. In a
release build, 10 KB of text with no date goes from 105 ms to 2.3 ms, 7.7 KB
of notes from 118 ms to 28 ms, and a one-line note from 1.1 ms to 0.28 ms. - Normalization no longer allocates per character, and the part-of-day table
is indexed by first word instead of searched 102 times.
0.8.0
0.8.0
Breaking
- The public types moved into the
ChronoPTnamespace:ChronoPT.Match,
ChronoPT.PartialDate,ChronoPT.OptionsandChronoPT.Recurrence. An app
with its ownRecurrencekeeps compiling.
Added
ChronoPT.strippingDates(from:)returns the text without the dates, along
with the word that introduced each one: "comprar pão amanhã no almoço" gives
"comprar pão".Match.ranges, every span that produced the date, so a time said apart from
its day can be highlighted or removed too.Match.interval,PartialDate.hasDay,PartialDate.dateComponents(in:),
public initializers,Hashableon every type,CodableonOptionsand
Recurrence, and a debug description that prints the text and the date.- Business days: "em 5 dias úteis", "no próximo dia útil", "primeiro dia útil
do mês", counting the national holidays and the three the banks close for. - Deadlines: "prazo de 5 dias", "em até 48 horas".
- Years in amounts: "daqui a 1 ano", "em 2 anos", "há 3 anos".
- Periods: "fim do ano", "no início do mês", "no meio da semana", and a month
on its own ("férias em dezembro", "março de 2027"). - Intervals of hours and minutes: "de 8 em 8 horas", "a cada 30 minutos".
- Ordinal indicators and numbered weekdays: "dia 1º", "6ª feira", "toda 2ª
feira"; "sábado agora" and "quinta dessa semana"; more parts of the day
("no comecinho da tarde", "na boquinha da noite", "de madruga").
Fixed
- Parsing was cubic: 2.4 KB of repeated weekdays took 13 seconds and a pasted
note could freeze a UI thread. It now takes 105 ms. The regexes were never
the problem; the candidate pair loops asked questions that scanned the rest
of the text. - A time counted from now no longer takes over a day said in the same text:
"consulta dia 30, sair daqui a 20 minutos" is the 30th. - "sáb - 3/10" is that date, not a range from Saturday to it. Two rules
produced the same span and the winner depended on the sort being stable. - Any calendar is honoured: a Buddhist calendar used to give 1483 for
"25/09/2026", and every movable holiday moved. - A time inside a daylight saving gap stays on its day.
- Normalization keeps one character for each character, so a combining mark
after a line break no longer shifts every range after it. - "da meia-noite à meia-noite" is a whole day.
- Digits that are not ASCII are no longer read as zero.
- "fim de semana que vem" and "fim do mês que vem" matched the shorter phrase.
- "de manhã bem cedo" gave 9:00, "reunião 10 às 12" gave a single 12:00,
"de 8 em 8 horas" lost its recurrence, and "em 5 dias úteis" gave a Saturday.
0.7.0
New
Recurrence.every(DateComponents)for intervals between repeats: "a cada 15 dias", "de 2 em 2 semanas", "toda semana", "todo mês", "semanalmente", "mensalmente" (#18). The components go straight intoCalendar.date(byAdding:to:). Counts that disagree, as in "de 2 em 3 semanas", are not an interval.- A weekday that needs a hint now takes a time before it when "de" joins them: "às 10 de quinta" is Thursday (#17). "às 10 segunda via do boleto" stays a plain time.
Adding a case to Recurrence breaks exhaustive switches over it.
0.6.0
Breaking change
ParsedResult now has start: ParsedDate and end: ParsedDate?, in place of date, end: Date? and hasTime.
let note = ChronoPT.interpret("25/09 às 14h")
note?.start.date // 25 September at 14:00
note?.start.hasTime // true
note?.start.knownComponents // [.day, .month, .hour, .minute]New
ParsedDate.knownComponentssays which calendar components the text fixed, by naming them ("25/09" gives the day and the month) or by counting from the reference date ("amanhã" gives the day, the month and the year). The rest comes from the reference or from defaults, so an app can show "25/09" without inventing a year (#10).hasTimeis derived from the components, and each end of a period or a range has its own date and components.Recurrence.weeklytakes aSet<Locale.Weekday>and reads as.weekly(on: [.tuesday]), so "às quartas e segundas" equals "às segundas e quartas".
Nothing depended on the old shape yet, so there is no migration path beyond renaming result.date to result.start.date.
0.5.0
New API
ParseOptions, a new parameter ofparseandinterpret(#4, #11). Its defaults keep the current behavior.allowsPastreads "ontem", "anteontem", "sexta passada", "na última sexta", "semana passada", "mês passado", "há 2 dias", "3 dias atrás" and "há 2 horas". Without it, "sexta passada às 10" gives no date instead of next Friday.defaultHoursets the time of a day with no time (noon by default).
ParsedResult.recurrenceand theRecurrenceenum (#5):.daily("todo dia", "diariamente"),.weekly([Locale.Weekday])("toda terça", "às segundas e quartas") and.monthly(day:)("todo dia 5", "todo mês no dia 10").dateis the next time it happens, counting today.
ChronoPT.interpret("tirar o lixo toda terça às 20h")?.recurrence // .weekly([.tuesday])
ChronoPT.interpret("paguei ontem", options: ParseOptions(allowsPast: true))Source compatible with 0.4.0: the new parameter has a default, and the new property is additive.
0.4.0
New
- Abbreviations: "na seg", "qua 14h", "sex que vem", "no sáb", "de seg a sex", "amn", "dps de amanhã", "dps do almoço", "prox sexta", "prox semana" (#12). "ter" is always the verb "to have", never Tuesday.
- More written times: "15:30h", "15h30m", "15h30min" (#13).
- Forms common in Portugal: "ao pequeno-almoço", "ao jantar", "à hora de almoço", "ao fim da tarde" (#13).
- A weekday followed by its date is that date: "segunda-feira, dia 5" (#13).
- Agenda-style ranges: "14h às 16h", "9h até 12h", "10h-11h", "segunda a sexta", "seg-sex" (#15).
Performance
- Compiled regexes are kept per thread:
parsewent from 11 ms to 0.58 ms per call in a release build (#14).
Tests
- A corpus of 60 notes written the way people write them, with the answer the person meant (#15).
No API changes.