Skip to content

Commit 0127edc

Browse files
authored
Merge pull request #5 from thoven87/timezone
Adding timezone as a parameter with default to current
2 parents 1a5c014 + f4573dc commit 0127edc

File tree

3 files changed

+79
-54
lines changed

3 files changed

+79
-54
lines changed

Sources/ICalendar/ICalendar.swift

Lines changed: 68 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,13 @@ public struct ICalendarClient: Sendable {
179179
duration: ICalDuration? = nil,
180180
location: String? = nil,
181181
description: String? = nil,
182-
uid: String? = nil
182+
uid: String? = nil,
183+
timeZone: TimeZone = .current
183184
) -> ICalEvent {
184185
var event = ICalEvent(uid: uid ?? UUID().uuidString, summary: summary)
185186

186-
event.dateTimeStamp = ICalDateTime(date: Date())
187-
event.dateTimeStart = ICalDateTime(date: startDate)
187+
event.dateTimeStamp = ICalDateTime(date: Date(), timeZone: timeZone)
188+
event.dateTimeStart = ICalDateTime(date: startDate, timeZone: timeZone)
188189

189190
if let endDate = endDate {
190191
event.dateTimeEnd = ICalDateTime(date: endDate)
@@ -209,12 +210,13 @@ public struct ICalendarClient: Sendable {
209210
date: Date,
210211
location: String? = nil,
211212
description: String? = nil,
212-
uid: String? = nil
213+
uid: String? = nil,
214+
timeZone: TimeZone = .current
213215
) -> ICalEvent {
214216
var event = ICalEvent(uid: uid ?? UUID().uuidString, summary: summary)
215217

216-
event.dateTimeStamp = ICalDateTime(date: Date())
217-
event.dateTimeStart = ICalDateTime(date: date, isDateOnly: true)
218+
event.dateTimeStamp = ICalDateTime(date: Date(), timeZone: timeZone)
219+
event.dateTimeStart = ICalDateTime(date: date, timeZone: timeZone, isDateOnly: true)
218220

219221
if let location = location {
220222
event.location = location
@@ -235,15 +237,17 @@ public struct ICalendarClient: Sendable {
235237
recurrenceRule: ICalRecurrenceRule,
236238
location: String? = nil,
237239
description: String? = nil,
238-
uid: String? = nil
240+
uid: String? = nil,
241+
timeZone: TimeZone = .current
239242
) -> ICalEvent {
240243
var event = createEvent(
241244
summary: summary,
242245
startDate: startDate,
243246
endDate: endDate,
244247
location: location,
245248
description: description,
246-
uid: uid
249+
uid: uid,
250+
timeZone: timeZone
247251
)
248252

249253
event.recurrenceRule = recurrenceRule
@@ -258,14 +262,15 @@ public struct ICalendarClient: Sendable {
258262
dueDate: Date? = nil,
259263
priority: Int? = nil,
260264
description: String? = nil,
261-
uid: String? = nil
265+
uid: String? = nil,
266+
timeZone: TimeZone = .current
262267
) -> ICalTodo {
263268
var todo = ICalTodo(uid: uid ?? UUID().uuidString, summary: summary)
264269

265-
todo.dateTimeStamp = ICalDateTime(date: Date())
270+
todo.dateTimeStamp = ICalDateTime(date: Date(), timeZone: timeZone)
266271

267272
if let dueDate = dueDate {
268-
todo.dueDate = ICalDateTime(date: dueDate)
273+
todo.dueDate = ICalDateTime(date: dueDate, timeZone: timeZone)
269274
}
270275

271276
if let priority = priority {
@@ -286,17 +291,19 @@ public struct ICalendarClient: Sendable {
286291
dueDate: Date? = nil,
287292
priority: Int? = nil,
288293
description: String? = nil,
289-
uid: String? = nil
294+
uid: String? = nil,
295+
timeZone: TimeZone = .current
290296
) -> ICalTodo {
291297
var todo = createTodo(
292298
summary: summary,
293299
dueDate: dueDate,
294300
priority: priority,
295301
description: description,
296-
uid: uid
302+
uid: uid,
303+
timeZone: timeZone
297304
)
298305

299-
todo.dateTimeStart = ICalDateTime(date: startDate)
306+
todo.dateTimeStart = ICalDateTime(date: startDate, timeZone: timeZone)
300307
return todo
301308
}
302309

@@ -394,14 +401,15 @@ public struct ICalendarClient: Sendable {
394401
interval: Int = 1,
395402
daysOfWeek: [ICalWeekday] = [],
396403
count: Int? = nil,
397-
until: Date? = nil
404+
until: Date? = nil,
405+
timeZone: TimeZone = .current
398406
) -> ICalRecurrenceRule {
399407
let byDay = daysOfWeek.isEmpty ? nil : daysOfWeek.map { $0.rawValue }
400408
return ICalRecurrenceRule(
401409
frequency: .weekly,
402410
interval: interval,
403411
count: count,
404-
until: until.map { ICalDateTime(date: $0) },
412+
until: until.map { ICalDateTime(date: $0, timeZone: timeZone) },
405413
byDay: byDay
406414
)
407415
}
@@ -413,7 +421,8 @@ public struct ICalendarClient: Sendable {
413421
weekdayOrdinal: Int? = nil,
414422
weekday: ICalWeekday? = nil,
415423
count: Int? = nil,
416-
until: Date? = nil
424+
until: Date? = nil,
425+
timeZone: TimeZone = .current
417426
) -> ICalRecurrenceRule {
418427
var byMonthDay: [Int]? = nil
419428
var byDay: [String]? = nil
@@ -428,7 +437,7 @@ public struct ICalendarClient: Sendable {
428437
frequency: .monthly,
429438
interval: interval,
430439
count: count,
431-
until: until.map { ICalDateTime(date: $0) },
440+
until: until.map { ICalDateTime(date: $0, timeZone: timeZone) },
432441
byDay: byDay,
433442
byMonthDay: byMonthDay
434443
)
@@ -440,13 +449,14 @@ public struct ICalendarClient: Sendable {
440449
month: Int? = nil,
441450
dayOfMonth: Int? = nil,
442451
count: Int? = nil,
443-
until: Date? = nil
452+
until: Date? = nil,
453+
timeZone: TimeZone = .current
444454
) -> ICalRecurrenceRule {
445455
ICalRecurrenceRule(
446456
frequency: .yearly,
447457
interval: interval,
448458
count: count,
449-
until: until.map { ICalDateTime(date: $0) },
459+
until: until.map { ICalDateTime(date: $0, timeZone: timeZone) },
450460
byMonthDay: dayOfMonth.map { [$0] },
451461
byMonth: month.map { [$0] }
452462
)
@@ -523,7 +533,8 @@ public struct ICalendarClient: Sendable {
523533
public func updateEvent(
524534
in calendar: inout ICalendar,
525535
eventUID: String,
526-
updateBlock: (inout ICalEvent) -> Void
536+
updateBlock: (inout ICalEvent) -> Void,
537+
timeZone: TimeZone = .current
527538
) -> Bool {
528539
guard let eventIndex = calendar.events.firstIndex(where: { $0.uid == eventUID }) else {
529540
return false
@@ -537,7 +548,7 @@ public struct ICalendarClient: Sendable {
537548
event.sequence = currentSequence + 1
538549

539550
// Update last modified timestamp
540-
event.lastModified = ICalDateTime(date: Date.now)
551+
event.lastModified = ICalDateTime(date: Date.now, timeZone: timeZone)
541552

542553
// Update the event in the calendar
543554
calendar.components[
@@ -668,15 +679,16 @@ public struct ICalendarClient: Sendable {
668679
public func findEventsWithUpcomingAlarms(
669680
in calendar: ICalendar,
670681
within timeInterval: TimeInterval,
671-
from referenceDate: Date = Date()
682+
from referenceDate: Date = Date(),
683+
timeZone: TimeZone = .current
672684
) -> [(event: ICalEvent, alarm: ICalAlarm, triggerDate: Date)] {
673685
var upcomingAlarms: [(event: ICalEvent, alarm: ICalAlarm, triggerDate: Date)] = []
674686

675687
for event in calendar.events {
676688
guard let eventStart = event.dateTimeStart?.date else { continue }
677689

678690
for alarm in event.alarms {
679-
if let triggerDate = calculateAlarmTriggerDate(alarm: alarm, eventStart: eventStart) {
691+
if let triggerDate = calculateAlarmTriggerDate(alarm: alarm, eventStart: eventStart, timeZone: timeZone) {
680692
let timeDifference = triggerDate.timeIntervalSince(referenceDate)
681693
if timeDifference >= 0 && timeDifference <= timeInterval {
682694
upcomingAlarms.append(
@@ -691,7 +703,7 @@ public struct ICalendarClient: Sendable {
691703
}
692704

693705
/// Calculate when an alarm will trigger based on the event start time
694-
private func calculateAlarmTriggerDate(alarm: ICalAlarm, eventStart: Date) -> Date? {
706+
private func calculateAlarmTriggerDate(alarm: ICalAlarm, eventStart: Date, timeZone: TimeZone = .current) -> Date? {
695707
guard let trigger = alarm.trigger else { return nil }
696708

697709
// Handle duration-based triggers (e.g., "-PT15M" for 15 minutes before)
@@ -703,7 +715,7 @@ public struct ICalendarClient: Sendable {
703715
}
704716

705717
// Handle absolute date-time triggers
706-
if let absoluteDate = ICalendarFormatter.parseDateTime(trigger) {
718+
if let absoluteDate = ICalendarFormatter.parseDateTime(trigger, timeZone: timeZone) {
707719
return absoluteDate.date
708720
}
709721

@@ -718,7 +730,8 @@ public struct ICalendarClient: Sendable {
718730
eventUID: String,
719731
newStartDate: Date,
720732
newEndDate: Date? = nil,
721-
keepDuration: Bool = true
733+
keepDuration: Bool = true,
734+
timeZone: TimeZone = .current
722735
) -> Bool {
723736
updateEvent(in: &calendar, eventUID: eventUID) { event in
724737
let originalDuration: TimeInterval?
@@ -731,12 +744,12 @@ public struct ICalendarClient: Sendable {
731744
originalDuration = nil
732745
}
733746

734-
event.dateTimeStart = ICalDateTime(date: newStartDate)
747+
event.dateTimeStart = ICalDateTime(date: newStartDate, timeZone: timeZone)
735748

736749
if let newEndDate = newEndDate {
737-
event.dateTimeEnd = ICalDateTime(date: newEndDate)
750+
event.dateTimeEnd = ICalDateTime(date: newEndDate, timeZone: timeZone)
738751
} else if let duration = originalDuration {
739-
event.dateTimeEnd = ICalDateTime(date: newStartDate.addingTimeInterval(duration))
752+
event.dateTimeEnd = ICalDateTime(date: newStartDate.addingTimeInterval(duration), timeZone: timeZone)
740753
}
741754
}
742755
}
@@ -875,7 +888,9 @@ extension ICalendarClient {
875888
description: String? = nil,
876889
organizer: ICalAttendee,
877890
attendees: [ICalAttendee],
878-
reminderMinutes: Int? = nil
891+
reminderMinutes: Int? = nil,
892+
timeZone: TimeZone = .current,
893+
uid: String = UUID().uuidString
879894
) -> ICalendar {
880895
var calendar = createCalendar()
881896
calendar.method = "REQUEST"
@@ -885,7 +900,9 @@ extension ICalendarClient {
885900
startDate: startDate,
886901
endDate: endDate,
887902
location: location,
888-
description: description
903+
description: description,
904+
uid: uid,
905+
timeZone: timeZone
889906
)
890907

891908
event.organizer = organizer
@@ -908,15 +925,17 @@ extension ICalendarClient {
908925
/// Create a task list calendar
909926
public func createTaskList(
910927
title: String,
911-
tasks: [(summary: String, dueDate: Date?, priority: Int?)]
928+
tasks: [(summary: String, dueDate: Date?, priority: Int?)],
929+
timeZone: TimeZone = .current
912930
) -> ICalendar {
913931
var calendar = createCalendar()
914932

915933
for task in tasks {
916934
let todo = createTodo(
917935
summary: task.summary,
918936
dueDate: task.dueDate,
919-
priority: task.priority
937+
priority: task.priority,
938+
timeZone: timeZone
920939
)
921940
calendar.addTodo(todo)
922941
}
@@ -989,7 +1008,7 @@ extension ICalEvent {
9891008
}
9901009

9911010
/// Check if this event is currently happening
992-
public func isHappeningNow(at date: Date = Date()) -> Bool {
1011+
public func isHappeningNow(at date: Date = Date(), timeZone: TimeZone = .current) -> Bool {
9931012
guard let start = dateTimeStart?.date else { return false }
9941013

9951014
if let end = dateTimeEnd?.date {
@@ -999,22 +1018,27 @@ extension ICalEvent {
9991018
return date >= start && date <= end
10001019
} else {
10011020
// For events without end time or duration, check if it's the same day
1002-
return Calendar.current.isDate(date, inSameDayAs: start)
1021+
var calendar = Calendar.current
1022+
calendar.timeZone = timeZone
1023+
return calendar.isDate(start, inSameDayAs: date)
10031024
}
10041025
}
10051026

10061027
/// Check if this event occurs on a specific date
1007-
public func occursOn(date: Date) -> Bool {
1028+
public func occursOn(date: Date, timeZone: TimeZone = .current) -> Bool {
10081029
guard let start = dateTimeStart?.date else { return false }
10091030

1031+
var calendar = Calendar.current
1032+
calendar.timeZone = timeZone
1033+
10101034
if isAllDay {
1011-
return Calendar.current.isDate(start, inSameDayAs: date)
1035+
return calendar.isDate(start, inSameDayAs: date)
10121036
} else {
10131037
if let end = dateTimeEnd?.date {
1014-
return date >= Calendar.current.startOfDay(for: start)
1015-
&& date <= Calendar.current.startOfDay(for: end).addingTimeInterval(86400)
1038+
return date >= calendar.startOfDay(for: start)
1039+
&& date <= calendar.startOfDay(for: end).addingTimeInterval(86400)
10161040
} else {
1017-
return Calendar.current.isDate(start, inSameDayAs: date)
1041+
return calendar.isDate(start, inSameDayAs: date)
10181042
}
10191043
}
10201044
}
@@ -1110,7 +1134,8 @@ extension ICalendar {
11101134
/// Update an event by UID using a closure
11111135
public mutating func updateEvent(
11121136
withUID uid: String,
1113-
updateBlock: (inout ICalEvent) -> Void
1137+
updateBlock: (inout ICalEvent) -> Void,
1138+
timeZone: TimeZone = .current
11141139
)
11151140
-> Bool
11161141
{
@@ -1131,7 +1156,7 @@ extension ICalendar {
11311156
// Update sequence number and last modified
11321157
let currentSequence = event.sequence ?? 0
11331158
event.sequence = currentSequence + 1
1134-
event.lastModified = ICalDateTime(date: Date())
1159+
event.lastModified = ICalDateTime(date: Date(), timeZone: timeZone)
11351160

11361161
components[index] = event
11371162
return true

Sources/ICalendar/ICalendarFormatter.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,24 +38,24 @@ internal struct ICalendarFormatter {
3838
}
3939
}
4040

41-
static func parseDateTime(_ value: String) -> ICalDateTime? {
41+
static func parseDateTime(_ value: String, timeZone: TimeZone = .current) -> ICalDateTime? {
4242
let trimmedValue = value.trimmingCharacters(in: .whitespacesAndNewlines)
4343

4444
// Check for date-only format (YYYYMMDD)
4545
if trimmedValue.count == 8, !trimmedValue.contains("T") {
4646
guard let date = dateOnlyFormatter.date(from: trimmedValue) else { return nil }
47-
return ICalDateTime(date: date, timeZone: nil, isDateOnly: true)
47+
return ICalDateTime(date: date, timeZone: timeZone, isDateOnly: true)
4848
}
4949

5050
// Check for UTC format (YYYYMMDDTHHMMSSZ)
5151
if trimmedValue.hasSuffix("Z") {
5252
guard let date = iso8601BasicFormatter.date(from: trimmedValue) else { return nil }
53-
return ICalDateTime(date: date, timeZone: TimeZone(abbreviation: "UTC"), isDateOnly: false)
53+
return ICalDateTime(date: date, timeZone: timeZone, isDateOnly: false)
5454
}
5555

5656
// Local time format (YYYYMMDDTHHMMSS)
5757
guard let date = iso8601LocalFormatter.date(from: trimmedValue) else { return nil }
58-
return ICalDateTime(date: date, timeZone: nil, isDateOnly: false)
58+
return ICalDateTime(date: date, timeZone: timeZone, isDateOnly: false)
5959
}
6060

6161
// MARK: - Duration Formatting

0 commit comments

Comments
 (0)