Calendar é uma classe Foundation que define um sistema de calendário e fornece métodos para cálculos com calendários: extração de componentes de data, cálculo de diferenças entre datas, busca de limites de períodos e deslocamento de datas. O calendário conecta o tempo absoluto (Date) a componentes legíveis por humanos e leva em consideração características regionais: início da semana, fuso horário e horário de verão. De acordo com a Documentação para Desenvolvedores Apple (2025), Foundation suporta 17 sistemas de calendário — do gregoriano ao budista e japonês — tornando Calendar uma ferramenta universal para aplicativos internacionalizados.
Principais pontos
Calendar é uma classe Foundation que implementa cálculos com calendários baseados em ICU (International Components for Unicode). O calendário define como o tempo absoluto (Date) mapeia para componentes do calendário: ano, mês, dia, hora, minuto, segundo. Sem Calendar é impossível saber qual é o ano, mês e dia atuais — Date por si só não contém esta informação.
O calendário leva em conta três grupos de parâmetros: o sistema de calendário (gregoriano, budista, japonês), o fuso horário e a configuração regional. Calendar.current combina os três a partir das configurações do sistema do usuário. Calendar.autoupdatingCurrent é uma versão especial que atualiza automaticamente quando as configurações mudam sem reiniciar o aplicativo através do NotificationCenter.
Calendar é um tipo por valor (value type) no Foundation. Calendar(identifier:) cria uma nova instância com parâmetros fixos. Calendar pode ser copiado, comparado através de == e usado como chave em um dicionário. Isso permite criar calendários com configurações específicas de timeZone e locale para testes.
Calendar é a versão Swift do NSCalendar do Objective-C, com ponte através de as Calendar / as NSCalendar. No Swift moderno, Calendar é usado em toda parte. NSCalendar permanece para compatibilidade reversa com APIs Objective-C. Calendar tem um conjunto completo de métodos sem o prefixo NS, com argumentos type-safe e opcionais do Swift.
Segurança para threads — Calendar é seguro para leitura de várias threads. Uma instância criada pode ser lida com segurança de várias threads. A modificação de propriedades (timeZone, locale) não é segura para threads — crie instâncias separadas de Calendar para diferentes configurações.
Foundation suporta 17 sistemas de calendário através da enumeração Calendar.Identifier. Cada sistema tem suas próprias regras para anos bissextos, número de meses e início da era. A escolha do calendário afeta todos os cálculos: dateComponents, dateInterval, nextDate.
Principais sistemas de calendário:
Calendar(identifier: .gregorian) — o mais utilizado. Está em conformidade com o padrão internacional ISO 8601 e é o calendário padrão na maioria dos países. Para aplicativos com audiência internacional, use Calendar.current — ele corresponde automaticamente ao calendário do sistema do usuário.
| Identificador | Tipo | Região de uso |
|---|---|---|
| .gregorian | Solar | Internacional |
| .buddhist | Solar | Tailândia, Camboja |
| .japanese | Solar | Japão |
| .hebrew | Lunissolar | Israel |
| .islamic | Lunar | Países islâmicos |
| .chinese | Lunissolar | China |
DateComponents e Calendar são um par inseparável. Calendar.dateComponents(_:from:) extrai componentes de Date respeitando o fuso horário do calendário. Calendar.date(from:) monta um Date a partir de DateComponents, preenchendo campos ausentes com valores padrão: dia = 1, hora = 0, minuto = 0, segundo = 0.
O método Calendar.component extrai um único componente, conveniente para verificações rápidas. Calendar.dateComponents extrai vários componentes em uma única chamada — isso é mais eficiente porque Calendar realiza os cálculos de calendário uma vez em vez de fazê-lo para cada componente separadamente. Para uma lista de 3+ componentes, sempre use dateComponents.
Calendar.compare compara dois Dates com uma precisão determinada. O parâmetro toGranularity define a precisão do componente: .year compara apenas o ano, .month — ano e mês, .day — ano, mês, dia. É útil para verificar se duas datas caem no mesmo dia, ignorando a hora.
let calendar = Calendar.current
let now = Date()
// Extrair um único componente
let year = calendar.component(.year, from: now)
// Extrair um conjunto de componentes
let comps = calendar.dateComponents(
[.year, .month, .day], from: now
)
// Comparar com granularidade de dia
let isSameDay = calendar.compare(date1, to: date2,
toGranularity: .day) == .orderedSame
// Verificar se a data é hoje
let isToday = calendar.isDateInToday(someDate)
Calendar.isDateInToday, isDateInTomorrow, isDateInYesterday — métodos para verificações relativas. Calendar.isDate(_:inSameDayAs:) verifica se duas datas caem no mesmo dia do calendário considerando o fuso horário do calendário. Estes métodos usam Calendar.compare internamente e são otimizados para chamadas frequentes.
Calendar.dateInterval é um dos métodos mais úteis para análise e interface de usuário. Ele retorna um DateInterval para o componente especificado: início e fim de um dia, semana, mês, ano. DateInterval contém start (Date) e end (Date) — os limites do período. Por exemplo, dateInterval(of: .weekOfYear, for: Date()) retorna o início de segunda-feira e o fim de domingo da semana atual.
Calendar.date com byAdding — um método para deslocar datas. Calendar.date(byAdding: .day, value: 7, to: Date()) retorna a data uma semana depois. Calendar.date(byAdding: DateComponents) é uma versão mais flexível que permite deslocar vários componentes de uma vez: +1 mês +3 dias. Calendar leva em conta automaticamente os diferentes comprimentos dos meses e os anos bissextos.
Calendar.nextDate procura a próxima data que corresponda aos DateComponents especificados. O parâmetro matchingPolicy define o comportamento em caso de não correspondência: .nextTime — a próxima coincidência de horário, .nextTimePreservingSmallerComponents — preserva minutos e segundos da data original, .strict — requer uma correspondência exata.
let calendar = Calendar.current
let today = Date()
// Início e fim da semana
let weekInterval = calendar.dateInterval(
of: .weekOfYear, for: today
)!
// Deslocar 1 mês
let nextMonth = calendar.date(
byAdding: .month, value: 1, to: today
)!
// Deslocar via DateComponents
var delta = DateComponents()
delta.month = 1
delta.day = 3
let shifted = calendar.date(byAdding: delta, to: today)!
// Próxima sexta-feira 13
let friday13Components = DateComponents(
weekday: 6, day: 13
)
let nextFriday13 = calendar.nextDate(
after: today, matching: friday13Components,
matchingPolicy: .nextTime
)
EnumerateDates — um método poderoso para iterar sobre datas por padrão. Calendar.enumerateDates(startingAfter:matching:matchingPolicy:using:) chama um bloco para cada correspondência até que o bloco retorne stop = true. Usado para gerar eventos recorrentes em calendários e agendas. Este método é mais eficiente que um loop manual com nextDate, pois é otimizado pelo ICU.
TimeZone é uma parte integrante do Calendar. O fuso horário determina a que hora do calendário corresponde um Date absoluto. O mesmo Date em UTC e em Moscou produz componentes diferentes: Date() em UTC pode mostrar 10:00, enquanto em MSK — 13:00. Calendar.timeZone por padrão é TimeZone.current.
Locale afeta o primeiro dia da semana, o número mínimo de dias na primeira semana do ano (minDaysInFirstWeek) e os nomes dos meses/dias da semana (ao converter através de DateFormatter). Calendar.locale por padrão é Locale.current. Na configuração regional russa a semana começa na segunda-feira, na americana — no domingo.
Calendar.availableIdentifiers retorna uma lista de todos os identificadores de calendário suportados. A propriedade estática Calendar.availableCalendarIdentifiers é um array de strings com os mesmos identificadores. Usado para construir uma interface de seleção de calendário e para verificar a disponibilidade de um sistema de calendário específico no dispositivo.
// Calendar com fuso horário específico
var utcCalendar = Calendar(identifier: .gregorian)
utcCalendar.timeZone = TimeZone(identifier: "UTC")!
// Calendar com configuração regional russa
var russianCalendar = Calendar(identifier: .gregorian)
russianCalendar.locale = Locale(identifier: "ru_RU")
// Primeiro dia útil depende da configuração regional
let firstWeekday = russianCalendar.firstWeekday
// 2 = segunda-feira (em ru_RU)
// Lista de calendários disponíveis
for identifier in Calendar.availableIdentifiers {
print(identifier)
}
firstWeekday — uma propriedade do Calendar que determina qual dia da semana é considerado o primeiro. Na configuração regional russa Sunday = 2 (segunda-feira é o primeiro). Na configuração regional americana Sunday = 1. Isso afeta weekOfMonth e weekOfYear: a mesma data pode pertencer a diferentes números de semana em diferentes configurações regionais. Para aplicativos que trabalham com datas, use Calendar.current ou defina firstWeekday explicitamente.
Vamos considerar cenários práticos que demonstram as capacidades do Calendar. Cada exemplo resolve uma tarefa específica de desenvolvimento iOS e mostra a maneira correta de usar cálculos com calendário.
Calendar.dateInterval(of: .month, for:) retorna os limites do mês atual. Verificar se um Date cai dentro deste intervalo é a maneira mais rápida de determinar se uma data pertence ao mês atual. Uma alternativa é Calendar.compare com granularidade .month: se o resultado for .orderedSame, o mês corresponde.
func isInCurrentMonth(_ date: Date) -> Bool {
let calendar = Calendar.current
let monthInterval = calendar.dateInterval(
of: .month, for: Date()
)!
return monthInterval.contains(date)
}
// Número de dias em um mês
func daysInMonth(for date: Date) -> Int {
let calendar = Calendar.current
return calendar.range(
of: .day, in: .month, for: date
)?.count ?? 0
}
// Adição de meses com ajuste correto
func addMonths(_ months: Int, to date: Date) -> Date {
let calendar = Calendar.current
return calendar.date(
byAdding: .month, value: months, to: date
)!
}
Calendar.range(of:in:for:) retorna o intervalo de valores válidos para um componente especificado no contexto de outro componente. Por exemplo, range(of: .day, in: .month, for: date) retorna 1..<32 para meses com 31 dias ou 1..<29 para fevereiro de um ano não bissexto. Esta é a maneira correta de obter o número de dias em um mês, sem usar valores fixos.
Adição de meses através de Calendar.date(byAdding:value:to:) lida corretamente com datas limite. Se você adicionar 1 mês a 31 de janeiro, Calendar retorna 28 de fevereiro (ou 29 em ano bissexto), em vez de 3 de março, que resultaria de simplesmente adicionar 30 dias através de TimeInterval. Esta é mais uma razão para não usar TimeInterval em cálculos com calendário.
| Método de Calendar | Propósito | Exemplo |
|---|---|---|
| dateInterval | Limites de período | Início e fim de um mês |
| range(of:in:for:) | Intervalo de componente | Dias do mês atual |
| date(byAdding:) | Deslocamento de data | +1 mês a partir de hoje |
| isDateInToday | Verificação de hoje | A data pertence a hoje? |
| compare(toGranularity:) | Comparação com precisão | Mesmo dia ignorando a hora |
Perguntas frequentes
Calendar.current retorna o calendário das configurações do sistema do usuário — pode não ser gregoriano (por exemplo, budista na Tailândia). Calendar(identifier: .gregorian) sempre cria um calendário gregoriano independentemente das configurações. Use Calendar.current para exibir datas e um identificador explicitamente escolhido para a lógica de negócios.
Isso se deve aos diferentes comprimentos dos meses. Se a data atual é 31 de janeiro, adicionar 1 mês resulta em 28 de fevereiro, já que fevereiro não tem 31 dias. Calendar ajusta automaticamente a data para o último dia válido do mês. Para controle preciso, use DateComponents com day: 1 para ir ao primeiro dia do mês.
DateFormatter usa Calendar.current — o calendário do sistema do usuário. Se um aplicativo deve sempre exibir datas no calendário gregoriano independentemente das configurações, defina formatter.calendar = Calendar(identifier: .gregorian). Isso garante uma exibição uniforme para todos os usuários.
Calendar.range(of: .day, in: .year, for: date) retorna 365 ou 366 dias. Mais simples: Calendar.date(from: DateComponents(year: year, month: 2, day: 29)) != nil — se 29 de fevereiro existe, o ano é bissexto. Calendar lida automaticamente com as regras do sistema de calendário específico.
Sim, a propriedade firstWeekday pode ser modificada. A alteração afeta weekOfMonth, weekOfYear e todos os cálculos relacionados a números de semana. Ao definir locale = Locale(identifier: “ru_RU”), firstWeekday se torna automaticamente 2 (segunda-feira). A definição manual substitui o valor do locale.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também