Skip to content

Commit 8f92af2

Browse files
gromandreasjengelh
authored andcommitted
ews: calculate offset from timezone for local time only
It is only necessary to calculate the offset if the client sends datetime values using local time and provides timezone information in separate tags. For UTC times, it is not necesary to calculate the offset.
1 parent f84756b commit 8f92af2

File tree

3 files changed

+39
-5
lines changed

3 files changed

+39
-5
lines changed

exch/ews/context.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1813,13 +1813,16 @@ void EWSContext::toContent(const std::string& dir, tCalendarItem& item, sShape&
18131813
shape.write(TAGGED_PROPVAL{PR_MESSAGE_CLASS, deconst("IPM.Appointment")});
18141814
int64_t startOffset = 0, endOffset = 0;
18151815
time_t startTime = 0, endTime = 0;
1816+
bool calcStartOffset, calcEndOffset = false;
18161817
if(item.Start) {
18171818
startTime = clock::to_time_t(item.Start.value().time);
18181819
startOffset = std::chrono::duration_cast<std::chrono::minutes>(item.Start.value().offset).count();
1820+
calcStartOffset = item.Start.value().needCalcOffset();
18191821
}
18201822
if(item.End) {
18211823
endTime = clock::to_time_t(item.End.value().time);
18221824
endOffset = std::chrono::duration_cast<std::chrono::minutes>(item.Start.value().offset).count();
1825+
calcEndOffset = item.Start.value().needCalcOffset();
18231826
}
18241827
// TODO handle no start and/or end times
18251828

@@ -2035,17 +2038,20 @@ void EWSContext::toContent(const std::string& dir, tCalendarItem& item, sShape&
20352038
shape.write(NtAppointmentTimeZoneDefinitionEndDisplay,
20362039
TAGGED_PROPVAL{PT_BINARY, temp_bin});
20372040

2038-
// If the offsets of start or end times are not set, probably
2039-
// the client didn't send the offset information in date tags.
2040-
// Try to get the offset from the timezone definition.
2041-
if(startOffset == 0 || endOffset == 0)
2041+
// If the offsets of start or end times are 0 and
2042+
// the client didn't send the offset information in date tags,
2043+
// try to get the offset from the timezone definition.
2044+
if((startOffset == 0 && calcStartOffset) || (endOffset == 0 && calcEndOffset))
20422045
{
20432046
EXT_PULL ext_pull;
20442047
TIMEZONEDEFINITION tzdef;
20452048
ext_pull.init(buf->data(), buf->size(), alloc, EXT_FLAG_UTF16);
20462049
if(ext_pull.g_tzdef(&tzdef) != EXT_ERR_SUCCESS)
20472050
throw EWS::DispatchError(E3294);
2048-
startOffset = endOffset = offset_from_tz(&tzdef, startTime);
2051+
if(calcStartOffset)
2052+
startOffset = offset_from_tz(&tzdef, startTime);
2053+
if(calcEndOffset)
2054+
endOffset = offset_from_tz(&tzdef, endTime);
20492055
}
20502056
item.Start.value().offset = std::chrono::minutes(startOffset);
20512057
item.End.value().offset = std::chrono::minutes(endOffset);

exch/ews/structures.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,6 +1194,24 @@ sTimePoint::sTimePoint(time_point tp, const tSerializableTimeZone& tz) :
11941194
/**
11951195
* @brief Create time point from date-time string
11961196
*
1197+
* As of 2025-01-27 we have identified 3 different datetime formats sent by
1198+
* the clients.
1199+
* Zulu time (UTC), no DST, no offset:
1200+
* `<t:Start>2024-04-29T19:30:00Z</t:Start>`
1201+
* With the offset information in datetime, additionally a timezone tag:
1202+
* `<t:Start>2024-11-27T13:00:00+01:00</t:Start>`
1203+
* `<t:StartTimeZone Id="W. Europe Standard Time"/>`
1204+
* `<t:EndTimeZone Id="W. Europe Standard Time"/>`
1205+
* Local time with a timezone tag, it's necessary to calculate the offset:
1206+
* `<t:Start>2024-09-25T09:00:00</t:Start>`
1207+
* ```
1208+
* <t:ExtendedProperty>
1209+
* <t:ExtendedFieldURI PropertyName="CalendarTimeZone" PropertySetId="A7B529B5-4B75-47A7-A24F-20743D6C55CD" PropertyType="String"/>
1210+
* <t:Value>Europe/Vienna</t:Value>
1211+
* </t:ExtendedProperty>
1212+
* ```
1213+
* `<t:MeetingTimeZone TimeZoneName="W. Europe Standard Time"/>`
1214+
*
11971215
* @throw DeserializationError Conversion failed
11981216
*
11991217
* @param Date-time string
@@ -1218,6 +1236,8 @@ sTimePoint::sTimePoint(const char* dtstr)
12181236
time = clock::from_time_t(timestamp);
12191237
time += std::chrono::duration_cast<time_point::duration>(std::chrono::duration<double>(seconds));
12201238
offset = std::chrono::minutes(60*tz_hour+(tz_hour < 0? -tz_min : tz_min));
1239+
if(strlen(dtstr) == 19)
1240+
calcOffset = true;
12211241
}
12221242

12231243
/**
@@ -1232,6 +1252,12 @@ sTimePoint sTimePoint::fromNT(uint64_t timestamp)
12321252
uint64_t sTimePoint::toNT() const
12331253
{return rop_util_unix_to_nttime(time-offset);}
12341254

1255+
/**
1256+
* @brief Whether it's necessary to calculate the offset from timezone
1257+
*/
1258+
bool sTimePoint::needCalcOffset() const
1259+
{return calcOffset;}
1260+
12351261
///////////////////////////////////////////////////////////////////////////////////////////////////
12361262
// Types implementation
12371263

exch/ews/structures.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,9 +452,11 @@ struct sTimePoint
452452

453453
static sTimePoint fromNT(uint64_t);
454454
uint64_t toNT() const;
455+
bool needCalcOffset() const;
455456

456457
time_point time{};
457458
std::chrono::minutes offset = std::chrono::minutes(0);
459+
bool calcOffset = false;
458460
};
459461

460462
///////////////////////////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)