oc-calendar: Extract time zone from TimeZoneDefinition

All-day and recurrent events have a binary property that describes the
time zone they take place in. We were using the user's time zone in
the webmail, but it may not be equal to the one in the client. This
difference eventually leads to time shifts in events.
This commit is contained in:
Juan Vallés
2015-12-11 12:15:11 +01:00
parent 332508e2db
commit 4ae5feb131
5 changed files with 228 additions and 36 deletions
+2 -1
View File
@@ -29,6 +29,7 @@
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalRecurrenceRule.h>
#import <NGCards/iCalTimeZone.h>
@class NSTimeZone;
@@ -46,7 +47,7 @@
fromRecurrencePattern: (struct RecurrencePattern *) rp
withExceptions: (struct ExceptionInfo *) exInfos
andExceptionCount: (uint16_t) exInfoCount
inTimeZone: (NSTimeZone *) tz;
inTimeZone: (iCalTimeZone *) tz;
@end
+5 -3
View File
@@ -35,6 +35,7 @@
#import <NGCards/iCalRepeatableEntityObject.h>
#import <NGCards/iCalRecurrenceRule.h>
#import <NGCards/iCalTimeZone.h>
#import <NGCards/iCalTimeZonePeriod.h>
#import "NSDate+MAPIStore.h"
#import "MAPIStoreRecurrenceUtils.h"
@@ -51,7 +52,7 @@
fromRecurrencePattern: (struct RecurrencePattern *) rp
withExceptions: (struct ExceptionInfo *) exInfos
andExceptionCount: (uint16_t) exInfoCount
inTimeZone: (NSTimeZone *) tz
inTimeZone: (iCalTimeZone *) tz
{
NSCalendarDate *startDate, *olEndDate, *untilDate, *exDate;
@@ -63,7 +64,7 @@
iCalWeekOccurrence weekOccurrence;
iCalWeekOccurrences dayMaskDays;
NSUInteger count, max;
NSInteger bySetPos;
NSInteger bySetPos, tzOffset;
unsigned char maskValue;
[entity removeAllRecurrenceRules];
@@ -242,9 +243,10 @@
{
/* The OriginalStartDate is in local time */
exDate = [NSDate dateFromMinutesSince1601: exInfos[count].OriginalStartDate];
tzOffset = -[[tz periodForDate: exDate] secondsOffsetFromGMT];
exDate = [exDate dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: - [tz secondsFromGMT]];
seconds: tzOffset];
[exceptionDates removeObject: exDate];
}
}
+33 -32
View File
@@ -36,6 +36,7 @@
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalPerson.h>
#import <NGCards/iCalTimeZone.h>
#import <NGCards/iCalTimeZonePeriod.h>
#import <NGCards/iCalTrigger.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoUser.h>
@@ -70,11 +71,12 @@
#include <mapistore/mapistore_nameid.h>
#import "iCalEvent+MAPIStore.h"
#import "iCalTimeZone+MAPIStore.h"
@implementation iCalEvent (MAPIStoreProperties)
- (void) _setupEventRecurrence: (NSData *) mapiRecurrenceData
inTimeZone: (NSTimeZone *) tz
inTimeZone: (iCalTimeZone *) tz
inMemCtx: (TALLOC_CTX *) memCtx
{
struct Binary_r *blob;
@@ -250,10 +252,8 @@
BOOL isAllDay;
iCalDateTime *start, *end;
iCalTimeZone *tz;
NSTimeZone *userTimeZone;
NSString *priority, *class = nil;
NSString *priority, *class = nil, *tzDescription = nil;
NSUInteger responseStatus = 0;
NSInteger tzOffset;
SOGoUser *ownerUser;
id value;
@@ -274,7 +274,31 @@
[self setAccessClass: @"PUBLIC"];
}
userTimeZone = [userContext timeZone];
/* Time zone = PidLidAppointmentTimeZoneDefinitionRecur
or PidLidAppointmentTimeZoneDefinition[Start|End]Display */
value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentTimeZoneDefinitionStartDisplay)];
if (!value)
{
value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentTimeZoneDefinitionEndDisplay)];
if (!value)
{
/* If PidLidtimeZoneStruct, TZID SHOULD come from PidLidTimeZoneDescription,
if PidLidAppointmentTimeZoneDefinition[Start|End]Display it MUST be derived from KeyName
(MS-OXCICAL] 2.1.3.1.1.19.1) */
value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentTimeZoneDefinitionRecur)];
tzDescription = [properties objectForKey: MAPIPropertyKey (PidLidTimeZoneDescription)];
}
}
if (value)
{
tz = [[iCalTimeZone alloc] iCalTimeZoneFromDefinition: value
withDescription: tzDescription
inMemCtx: memCtx];
}
else
/* The client is more likely to have the webmail's time zone than any other */
tz = [iCalTimeZone timeZoneForName: [[userContext timeZone] name]];
[(iCalCalendar *) parent addTimeZone: tz];
/* CREATED */
value = [properties objectForKey: MAPIPropertyKey (PidTagCreationTime)];
@@ -306,20 +330,13 @@
objectForKey: MAPIPropertyKey (PidLidAppointmentSubType)];
if (value)
isAllDay = [value boolValue];
if (!isAllDay)
{
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
[(iCalCalendar *) parent addTimeZone: tz];
}
else
tz = nil;
// recurrence-id
value
= [properties objectForKey: MAPIPropertyKey (PidLidExceptionReplaceTime)];
if (value)
[self setRecurrenceId: value];
// start
value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentStartWhole)];
if (!value)
@@ -330,15 +347,7 @@
[start setTimeZone: tz];
if (isAllDay)
{
/* when user TZ is positive (East) all-day events were not
shown properly in SOGo UI. This day delay fixes it */
tzOffset = [userTimeZone secondsFromGMTForDate: value];
if (tzOffset > 0)
{
value = [value dateByAddingYears: 0 months: 0 days: 1
hours: 0 minutes: 0
seconds: 0];
}
/* All-day events are set in floating time ([MS-OXCICAL] 2.1.3.1.1.20.8) */
[start setDate: value];
[start setTimeZone: nil];
}
@@ -356,15 +365,7 @@
[end setTimeZone: tz];
if (isAllDay)
{
/* when user TZ is positive (East) all-day events were not
shown properly in SOGo UI. This day delay fixes it */
tzOffset = [userTimeZone secondsFromGMTForDate: value];
if (tzOffset > 0)
{
value = [value dateByAddingYears: 0 months: 0 days: 1
hours: 0 minutes: 0
seconds: 0];
}
/* All-day events are set in floating time ([MS-OXCICAL] 2.1.3.1.1.20.8) */
[end setDate: value];
[end setTimeZone: nil];
}
@@ -467,7 +468,7 @@
value = [properties
objectForKey: MAPIPropertyKey (PidLidAppointmentRecur)];
if (value)
[self _setupEventRecurrence: value inTimeZone: userTimeZone inMemCtx: memCtx];
[self _setupEventRecurrence: value inTimeZone: tz inMemCtx: memCtx];
/* alarm */
[self _setupEventAlarmFromProperties: properties];
+3
View File
@@ -30,6 +30,9 @@
- (struct Binary_r *) asTimeZoneStructInMemCtx: (TALLOC_CTX *) memCtx;
- (struct Binary_r *) asZoneTimeDefinitionWithFlags: (enum TZRuleFlag) flags
inMemCtx: (TALLOC_CTX *) memCtx;
- (iCalTimeZone *) iCalTimeZoneFromDefinition: (NSData *) value
withDescription: (NSString *) description
inMemCtx: (TALLOC_CTX *) memCtx;
@end
+185
View File
@@ -23,11 +23,15 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSString.h>
#import <Foundation/NSTimeZone.h>
#import <NGCards/iCalByDayMask.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalTimeZonePeriod.h>
#import <NGCards/iCalRecurrenceRule.h>
#import "NSString+MAPIStore.h"
#import "NSData+MAPIStore.h"
#import "NSDate+MAPIStore.h"
#include <stdbool.h>
#include <stdint.h>
@@ -166,5 +170,186 @@
return set_TimeZoneDefinition (memCtx, &definition);
}
- (NSString *) _offsetStringFromOffset: (NSInteger) offset
{
NSInteger offsetHours, offsetMins;
NSString *offsetSign;
/* The offset format is, eg, "+0200" for 2 hours 0 minutes ahead */
if (offset < 0)
offsetSign = @"-";
else
offsetSign = @"+";
offsetHours = abs (offset) / 60;
offsetMins = abs (offset) % 60;
return [NSString stringWithFormat: @"%@%d%d%d%d",
offsetSign, offsetHours / 10, offsetHours % 10,
offsetMins / 10, offsetMins % 10];
}
- (NSString *) _rRuleStringFromSystemTime: (struct SYSTEMTIME) date
{
NSString *result, *byDay;
/* The conversion tables between the SYSTEMTIME fields and the RRULE ones
can be found at [MS-OXCICAL] 2.1.3.2.1 */
if (date.wDay == 5)
byDay = @"-1";
else
byDay = [NSString stringWithFormat: @"%d", date.wDay];
switch (date.wDayOfWeek)
{
case iCalWeekDaySunday:
byDay = [byDay stringByAppendingString: @"SU"];
break;
case iCalWeekDayMonday:
byDay = [byDay stringByAppendingString: @"MO"];
break;
case iCalWeekDayTuesday:
byDay = [byDay stringByAppendingString: @"TU"];
break;
case iCalWeekDayWednesday:
byDay = [byDay stringByAppendingString: @"WE"];
break;
case iCalWeekDayThursday:
byDay = [byDay stringByAppendingString: @"TH"];
break;
case iCalWeekDayFriday:
byDay = [byDay stringByAppendingString: @"FR"];
break;
case iCalWeekDaySaturday:
byDay = [byDay stringByAppendingString: @"SA"];
break;
}
result = [NSString stringWithFormat: @"FREQ=YEARLY;BYDAY=%@;BYMONTH=%d", byDay, date.wMonth];
return result;
}
- (iCalTimeZone *) iCalTimeZoneFromDefinition: (NSData *) value
withDescription: (NSString *) description
inMemCtx: (TALLOC_CTX *) memCtx
{
BOOL daylightDefined = NO, ruleFound = NO;
iCalDateTime *daylightStart, *standardStart;
iCalRecurrenceRule *daylightRRule, *standardRRule;
iCalTimeZone *tz = nil;
iCalTimeZonePeriod *daylight, *standard;
NSCalendarDate *dlStartValue, *stStartValue;
NSString *strOffsetFrom, *strOffsetTo, *tzID;
char *keyName;
struct Binary_r *binValue;
struct SYSTEMTIME initDate;
struct TimeZoneDefinition *definition;
struct TZRule rule;
uint16_t count;
binValue = [value asBinaryInMemCtx: memCtx];
definition = get_TimeZoneDefinition (memCtx, binValue);
if (!definition)
return nil;
if (!definition->cRules)
goto end;
for (count = 0; count < definition->cRules; count++)
{
/* ([MS-OXCICAL] 2.1.3.1.1.19) The TZRule with the
TZRULE_FLAG_EFFECTIVE_TZREG bit set in the TZRule flags field
is the one that MUST be exported */
if (definition->TZRules[count].flags & TZRULE_FLAG_EFFECTIVE_TZREG)
{
rule = definition->TZRules[count];
ruleFound = YES;
break;
}
}
if (!ruleFound)
goto end;
if (!description)
{
/* The cbHeader field contains the size, in bytes of the Reserved (2b),
cchKeyName (2b) keyName (variable Unicode string) and cRules (2b)
([MS-OXOCAL] 2.2.1.41). The keyName field is a non-NULL-terminated
char array. */
keyName = talloc_strndup (memCtx, definition->keyName, (definition->cbHeader - 6) / 2);
tzID = [NSString stringWithCString: keyName
encoding: [NSString defaultCStringEncoding]];
talloc_free (keyName);
}
else
tzID = [NSString stringWithString: description];
tz = [iCalTimeZone groupWithTag: @"vtimezone"];
[tz addChild: [CardElement simpleElementWithTag: @"tzid"
value: tzID]];
if (rule.stStandardDate.wMonth != 0)
daylightDefined = YES;
/* STANDARD TIME ([MS-OXCICAL] 2.1.3.1.1.19.2) */
standard = [iCalTimeZonePeriod groupWithTag: @"standard"];
/* TZOFFSETFROM = -1 * (PidLidTimeZoneStruct.lBias + PidLidTimeZoneStruct.lDaylightBias) */
strOffsetFrom = [self _offsetStringFromOffset: -1 * (rule.lBias + rule.lDaylightBias)];
[standard addChild: [CardElement simpleElementWithTag: @"tzoffsetfrom"
value: strOffsetFrom]];
/* TZOFFSETTO = -1 * (PidLidTimeZoneStruct.lBias + PidLidTimeZoneStruct.lStandardBias) */
strOffsetTo = [self _offsetStringFromOffset: -1 * (rule.lBias + rule.lStandardBias)];
[standard addChild: [CardElement simpleElementWithTag: @"tzoffsetto"
value: strOffsetTo]];
/* DTSTART & RRULE are derived from the stStandardDate and wYear properties */
standardStart = [iCalDateTime elementWithTag: @"dtstart"];
initDate = rule.stStandardDate;
stStartValue = [NSCalendarDate dateFromSystemTime: initDate
andRuleYear: rule.wYear];
[standardStart setDateTime: stStartValue];
[standard addChild: standardStart];
if (daylightDefined)
{
standardRRule = [[iCalRecurrenceRule alloc] initWithString: [self _rRuleStringFromSystemTime: initDate]];
[standard addChild: standardRRule];
/* DAYLIGHT SAVING TIME ([MS-OXCICAL] 2.1.3.1.1.19.3) */
daylight = [iCalTimeZonePeriod groupWithTag: @"daylight"];
/* TZOFFSETFROM = -1 * (PidLidTimeZoneStruct.lBias + PidLidTimeZoneStruct.lStandardBias) */
[daylight addChild: [CardElement simpleElementWithTag: @"tzoffsetfrom"
value: strOffsetTo]];
/* TZOFFSETTO = -1 * (PidLidTimeZoneStruct.lBias + PidLidTimeZoneStruct.lDaylightBias) */
[daylight addChild: [CardElement simpleElementWithTag: @"tzoffsetto"
value: strOffsetFrom]];
/* DTSTART & RRULE are derived from the stDaylightDate and wYear properties */
daylightStart = [iCalDateTime elementWithTag: @"dtstart"];
initDate = rule.stDaylightDate;
dlStartValue = [NSCalendarDate dateFromSystemTime: initDate
andRuleYear: rule.wYear];
[daylightStart setDateTime: dlStartValue];
[daylight addChild: daylightStart];
daylightRRule = [[iCalRecurrenceRule alloc] initWithString: [self _rRuleStringFromSystemTime: initDate]];
[daylight addChild: daylightRRule];
[tz addChild: daylight];
}
[tz addChild: standard];
end:
talloc_free (definition);
return tz;
}
@end