From cbf9b6da3e065637491ece01b3c263ed5b484324 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Tue, 3 Nov 2015 15:47:30 -0500 Subject: [PATCH] Fix all-day events covering a timezone change --- SoObjects/Appointments/iCalEvent+SOGo.m | 36 +++++-------------- UI/Scheduler/UIxAppointmentEditor.m | 23 +++++++++--- UI/WebServerResources/js/Common/utils.js | 9 +++++ .../js/Scheduler/Component.service.js | 20 ++++++++--- .../js/Scheduler/ComponentController.js | 4 +-- 5 files changed, 53 insertions(+), 39 deletions(-) diff --git a/SoObjects/Appointments/iCalEvent+SOGo.m b/SoObjects/Appointments/iCalEvent+SOGo.m index 75ce514f5..0c4c3b879 100644 --- a/SoObjects/Appointments/iCalEvent+SOGo.m +++ b/SoObjects/Appointments/iCalEvent+SOGo.m @@ -328,30 +328,10 @@ */ - (NSDictionary *) attributesInContext: (WOContext *) context { - BOOL isAllDay; - NSCalendarDate *eventStartDate, *eventEndDate; NSMutableDictionary *data; - NSTimeZone *timeZone; - SOGoUserDefaults *ud; - - isAllDay = [self isAllDay]; - ud = [[context activeUser] userDefaults]; - timeZone = [ud timeZone]; - eventStartDate = [self startDate]; - eventEndDate = [self endDate]; - - if (!isAllDay) - { - [eventStartDate setTimeZone: timeZone]; - [eventEndDate setTimeZone: timeZone]; - } data = [NSMutableDictionary dictionaryWithDictionary: [super attributesInContext: context]]; - [data setObject: [eventStartDate iso8601DateString] forKey: @"startDate"]; - [data setObject: [eventEndDate iso8601DateString] forKey: @"endDate"]; - - [data setObject: [NSNumber numberWithBool: isAllDay] forKey: @"isAllDay"]; [data setObject: [NSNumber numberWithBool: ![self isOpaque]] forKey: @"isTransparent"]; return data; @@ -383,18 +363,10 @@ if ([o isKindOfClass: [NSString class]] && [o length]) aptStartDate = [self dateFromString: o inContext: context]; - o = [data objectForKey: @"startTime"]; - if ([o isKindOfClass: [NSString class]] && [o length]) - [self adjustDate: &aptStartDate withTimeString: o inContext: context]; - o = [data objectForKey: @"endDate"]; if ([o isKindOfClass: [NSString class]] && [o length]) aptEndDate = [self dateFromString: o inContext: context]; - o = [data objectForKey: @"endTime"]; - if ([o isKindOfClass: [NSString class]] && [o length]) - [self adjustDate: &aptEndDate withTimeString: o inContext: context]; - o = [data objectForKey: @"isTransparent"]; if ([o isKindOfClass: [NSNumber class]]) [self setTransparency: ([o boolValue]? @"TRANSPARENT" : @"OPAQUE")]; @@ -418,6 +390,14 @@ } else { + o = [data objectForKey: @"startTime"]; + if ([o isKindOfClass: [NSString class]] && [o length]) + [self adjustDate: &aptStartDate withTimeString: o inContext: context]; + + o = [data objectForKey: @"endTime"]; + if ([o isKindOfClass: [NSString class]] && [o length]) + [self adjustDate: &aptEndDate withTimeString: o inContext: context]; + [self setStartDate: aptStartDate]; [self setEndDate: aptEndDate]; } diff --git a/UI/Scheduler/UIxAppointmentEditor.m b/UI/Scheduler/UIxAppointmentEditor.m index 769548ac7..be5aa1ddc 100644 --- a/UI/Scheduler/UIxAppointmentEditor.m +++ b/UI/Scheduler/UIxAppointmentEditor.m @@ -652,7 +652,8 @@ iCalEvent *event; BOOL resetAlarm; - unsigned int snoozeAlarm; + NSInteger offset; + NSUInteger snoozeAlarm; event = [self event]; co = [self clientObject]; @@ -662,12 +663,23 @@ timeZone = [ud timeZone]; eventStartDate = [event startDate]; eventEndDate = [event endDate]; - if (!isAllDay) + + if (isAllDay) { - [eventStartDate setTimeZone: timeZone]; - [eventEndDate setTimeZone: timeZone]; + eventEndDate = [eventEndDate dateByAddingYears: 0 months: 0 days: -1]; + + // Convert the dates to the user's timezone + offset = [timeZone secondsFromGMTForDate: eventStartDate]; + eventStartDate = [eventStartDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 + seconds:-offset]; + offset = [timeZone secondsFromGMTForDate: eventEndDate]; + eventEndDate = [eventEndDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 + seconds:-offset]; } + [eventStartDate setTimeZone: timeZone]; + [eventEndDate setTimeZone: timeZone]; + // resetAlarm=yes is set only when we are about to show the alarm popup in the Web // interface of SOGo. See generic.js for details. snoozeAlarm=X is called when the // user clicks on "Snooze for" X minutes, when the popup is being displayed. @@ -704,8 +716,11 @@ data = [NSMutableDictionary dictionaryWithObjectsAndKeys: [componentCalendar nameInContainer], @"pid", [componentCalendar displayName], @"calendar", + [NSNumber numberWithBool: isAllDay], @"isAllDay", [NSNumber numberWithBool: [self isReadOnly]], @"isReadOnly", [NSNumber numberWithBool: [self userHasRSVP]], @"userHasRSVP", + [eventStartDate iso8601DateString], @"startDate", + [eventEndDate iso8601DateString], @"endDate", [dateFormatter formattedDate: eventStartDate], @"localizedStartDate", [dateFormatter formattedDate: eventEndDate], @"localizedEndDate", [self alarm], @"alarm", diff --git a/UI/WebServerResources/js/Common/utils.js b/UI/WebServerResources/js/Common/utils.js index e10215d6e..0928aead7 100644 --- a/UI/WebServerResources/js/Common/utils.js +++ b/UI/WebServerResources/js/Common/utils.js @@ -184,6 +184,15 @@ Date.prototype.daysUpTo = function(otherDate) { return days; }; +Date.prototype.minutesTo = function(otherDate) { + var delta, dstOffset; + + delta = Math.floor(otherDate.valueOf() - this.valueOf())/1000/60; + dstOffset = otherDate.getTimezoneOffset() - this.getTimezoneOffset(); + + return delta - dstOffset; +}; + Date.prototype.stringWithSeparator = function(separator) { var month = '' + (this.getMonth() + 1); var day = '' + this.getDate(); diff --git a/UI/WebServerResources/js/Scheduler/Component.service.js b/UI/WebServerResources/js/Scheduler/Component.service.js index 111902161..a71bcefdb 100644 --- a/UI/WebServerResources/js/Scheduler/Component.service.js +++ b/UI/WebServerResources/js/Scheduler/Component.service.js @@ -422,13 +422,10 @@ if (this.endDate) { this.end = new Date(this.endDate.substring(0,10) + ' ' + this.endDate.substring(11,16)); - this.delta = Math.floor((Math.abs(this.end - this.start)/1000)/60); + this.delta = this.start.minutesTo(this.end); } else if (this.type == 'appointment') { - this.end = new Date(this.start.getTime()); - this.end.setMinutes(Math.round(this.end.getMinutes()/15)*15); - this.end.addMinutes(this.delta); - //this.end.addMinutes(this.delta); + this.setDelta(this.delta); } if (this.dueDate) @@ -650,6 +647,19 @@ } }; + /** + * @function setDelta + * @memberof Component.prototype + * @desc Set the end time to the specified number of minutes after the start time. + * @param {number} delta - the number of minutes + */ + Component.prototype.setDelta = function(delta) { + this.delta = delta; + this.end = new Date(this.start.getTime()); + this.end.setMinutes(Math.round(this.end.getMinutes()/15)*15); + this.end.addMinutes(this.delta); + }; + /** * @function updateFreeBusy * @memberof Component.prototype diff --git a/UI/WebServerResources/js/Scheduler/ComponentController.js b/UI/WebServerResources/js/Scheduler/ComponentController.js index 552590675..aa65643cf 100644 --- a/UI/WebServerResources/js/Scheduler/ComponentController.js +++ b/UI/WebServerResources/js/Scheduler/ComponentController.js @@ -299,11 +299,11 @@ function adjustEndTime() { // The end date must be after the start date - var delta = vm.component.end.valueOf() - vm.component.start.valueOf(); + var delta = vm.component.start.minutesTo(vm.component.end); if (delta < 0) vm.component.end = new Date(oldEndDate.getTime()); else { - vm.component.delta = Math.floor((Math.abs(vm.component.end - vm.component.start)/1000)/60); + vm.component.delta = delta; oldEndDate = new Date(vm.component.end.getTime()); } }