1 // CalendarTimezone.cpp - CalendarTimezone class functions
3 // (c) 2016-2017 Xestia Software Development.
5 // This file is part of Xestia Calendar.
7 // Xestia Calendar is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by the
9 // Free Software Foundation, version 3 of the license.
11 // Xestia Calendar is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License along
17 // with Xestia Calendar. If not, see <http://www.gnu.org/licenses/>
19 #include "CalendarTimezone.h"
23 CalendarObjectValidResult CalendarTimezoneObject::ValidObject(){
25 bool ValidBegin = false;
26 bool ValidEnd = false;
27 bool ValidTimeZoneID = false;
31 // Look for BEGIN:VEVENT.
33 for (vector<string>::iterator iter = ObjectName.begin();
34 iter != ObjectName.end(); iter++){
36 if (ObjectName[SeekCount] == "BEGIN" &&
37 ObjectData[SeekCount] == "VTIMEZONE"){
39 if (ValidBegin == false){
42 return CALENDAROBJECTVALID_INVALIDFORMAT;
47 if (ObjectName[SeekCount] == "END" &&
48 ObjectData[SeekCount] == "VTIMEZONE" &&
51 return CALENDAROBJECTVALID_INVALIDFORMAT;
63 for (vector<string>::iterator iter = ObjectName.begin();
64 iter != ObjectName.end(); iter++){
67 PropertyName = ObjectName[SeekCount].substr(0,4);
70 catch(const out_of_range& oor){
74 if (PropertyName == "TZID"){
76 if (ValidTimeZoneID == false){
77 ValidTimeZoneID = true;
79 return CALENDAROBJECTVALID_INVALIDFORMAT;
90 // Look for END:VEVENT.
92 for (vector<string>::iterator iter = ObjectName.begin();
93 iter != ObjectName.end(); iter++){
95 if (ObjectName[SeekCount] == "END" &&
96 ObjectData[SeekCount] == "VTIMEZONE"){
98 if (ValidEnd == false){
101 return CALENDAROBJECTVALID_INVALIDFORMAT;
110 // Check if the VEVENT is valid.
112 if (ValidBegin == true &&
114 ValidTimeZoneID == true){
116 return CALENDAROBJECTVALID_OK;
120 return CALENDAROBJECTVALID_INVALIDFORMAT;
126 void CalendarTimezoneObject::ProcessData(){
130 multimap<string,string> DataReceived;
131 map<string,string> PropertyData;
132 string *PropertyNameData = nullptr;
133 int ObjectSeekCount = 0;
135 // Process the data from TZID.
137 DataReceived = ProcessTextVectors(&ObjectName, &ObjectData, false, "TZID");
139 if (DataReceived.begin() != DataReceived.end()){
142 TimeZoneDataTokens = DataReceived.begin()->first.substr(5);
145 catch(const out_of_range &oor){
146 // Do nothing as there is no data.
149 TimeZoneData = DataReceived.begin()->second;
153 // Process the data from LAST-MODIFIED.
155 DataReceived = ProcessTextVectors(&ObjectName, &ObjectData, false, "LAST-MODIFIED");
157 if (DataReceived.begin() != DataReceived.end()){
160 LastModifiedTokens = DataReceived.begin()->first.substr(14);
163 catch(const out_of_range &oor){
164 // Do nothing as there is no data.
167 LastModifiedData = DataReceived.begin()->second;
171 // Process the data from TZURL.
173 DataReceived = ProcessTextVectors(&ObjectName, &ObjectData, false, "TZURL");
175 if (DataReceived.begin() != DataReceived.end()){
178 TimeZoneURLTokens = DataReceived.begin()->first.substr(6);
181 catch(const out_of_range &oor){
182 // Do nothing as there is no data.
185 TimeZoneURLData = DataReceived.begin()->second;
189 // Process data from each STANDARD and DAYLIGHT.
191 ProcessStandardDaylight();
196 for (vector<vector<string>>::iterator tzsiter = TimezoneStandardName.begin();
197 tzsiter != TimezoneStandardName.end(); tzsiter++){
199 bool DateTimeStartFound = false;
200 bool TimeZoneOffsetToFound = false;
201 bool TimeZoneOffsetFromFound = false;
203 TimezoneDataStruct NewTZData;
205 // Process the data from DTSTART.
207 DataReceived = ProcessTextVectors(&TimezoneStandardName[SeekCount],
208 &TimezoneStandardData[SeekCount], false, "DTSTART");
210 if (DataReceived.begin() != DataReceived.end()){
213 NewTZData.DateTimeStartTokens = DataReceived.begin()->first.substr(8);
216 catch(const out_of_range &oor){
217 // Do nothing as there is no data.
220 NewTZData.DateTimeStartData = DataReceived.begin()->second;
221 DateTimeStartFound = true;
225 // Process the data from TZOFFSETFROM.
227 DataReceived = ProcessTextVectors(&TimezoneStandardName[SeekCount],
228 &TimezoneStandardData[SeekCount], false, "TZOFFSETFROM");
230 if (DataReceived.begin() != DataReceived.end()){
233 NewTZData.TimeZoneOffsetFromTokens = DataReceived.begin()->first.substr(13);
236 catch(const out_of_range &oor){
237 // Do nothing as there is no data.
240 NewTZData.TimeZoneOffsetFromData = DataReceived.begin()->second;
241 TimeZoneOffsetFromFound = true;
245 // Process the data from TZOFFSETTO.
247 DataReceived = ProcessTextVectors(&TimezoneStandardName[SeekCount],
248 &TimezoneStandardData[SeekCount], false, "TZOFFSETTO");
250 if (DataReceived.begin() != DataReceived.end()){
253 NewTZData.TimeZoneOffsetToTokens = DataReceived.begin()->first.substr(11);
256 catch(const out_of_range &oor){
257 // Do nothing as there is no data.
260 NewTZData.TimeZoneOffsetToData = DataReceived.begin()->second;
261 TimeZoneOffsetToFound = true;
265 // Process the data from RRULE.
267 DataReceived = ProcessTextVectors(&TimezoneStandardName[SeekCount],
268 &TimezoneStandardData[SeekCount], false, "RRULE");
270 if (DataReceived.begin() != DataReceived.end()){
273 NewTZData.RecurranceRuleDataTokens = DataReceived.begin()->first.substr(6);
276 catch(const out_of_range &oor){
277 // Do nothing as there is no data.
280 NewTZData.RecurranceRuleData = DataReceived.begin()->second;
284 // Process the data from COMMENT.
286 DataReceived = ProcessTextVectors(&TimezoneStandardName[SeekCount],
287 &TimezoneStandardData[SeekCount], true, "COMMENT");
291 for(multimap<string,string>::iterator propiter = DataReceived.begin();
292 propiter != DataReceived.end();
295 NewTZData.CommentListTokens.push_back("");
296 NewTZData.CommentListAltRep.push_back("");
297 NewTZData.CommentListLanguage.push_back("");
298 NewTZData.CommentList.push_back("");
300 bool TokenData = false;
301 string PropertyTokens;
303 PropertyNameData = (string*)&propiter->first;
305 PropertyData = SplitValues(*PropertyNameData);
307 for(map<string,string>::iterator propdataiter = PropertyData.begin();
308 propdataiter != PropertyData.end(); propdataiter++){
310 if (propdataiter->first == "ALTREP"){
312 NewTZData.CommentListAltRep[ObjectSeekCount] = propdataiter->second;
314 } else if (propdataiter->first == "LANGUAGE"){
316 NewTZData.CommentListLanguage[ObjectSeekCount] = propdataiter->second;
320 if (TokenData == false){
323 PropertyTokens += ";";
326 PropertyTokens += propdataiter->first;
327 PropertyTokens += "=";
328 PropertyTokens += propdataiter->second;
334 if (PropertyTokens.size() > 0){
335 NewTZData.CommentListTokens[ObjectSeekCount] = PropertyTokens;
338 NewTZData.CommentList[ObjectSeekCount] = propiter->second;
344 // Process the data from RDATE.
346 DataReceived = ProcessTextVectors(&TimezoneStandardName[SeekCount],
347 &TimezoneStandardData[SeekCount], true, "RDATE");
351 for(multimap<string,string>::iterator propiter = DataReceived.begin();
352 propiter != DataReceived.end();
355 NewTZData.RecurranceDateDataTokens.push_back("");
356 NewTZData.RecurranceDateDataValue.push_back("");
357 NewTZData.RecurranceDateDataTimeZoneParam.push_back("");
358 NewTZData.RecurranceDateData.push_back("");
360 bool TokenData = false;
361 string PropertyTokens;
363 PropertyNameData = (string*)&propiter->first;
365 PropertyData = SplitValues(*PropertyNameData);
367 for(map<string,string>::iterator dataiter = PropertyData.begin();
368 dataiter != PropertyData.end(); dataiter++){
370 if (dataiter->first == "VALUE"){
372 NewTZData.RecurranceDateDataValue[ObjectSeekCount] = dataiter->second;
374 } else if (dataiter->first == "TZID"){
376 NewTZData.RecurranceDateDataTimeZoneParam[ObjectSeekCount] = dataiter->second;
380 if (TokenData == false){
383 PropertyTokens += ";";
386 PropertyTokens += dataiter->first;
387 PropertyTokens += "=";
388 PropertyTokens += dataiter->second;
394 if (PropertyTokens.size() > 0){
395 NewTZData.RecurranceDateDataTokens[ObjectSeekCount] = PropertyTokens;
398 NewTZData.RecurranceDateData[ObjectSeekCount] = propiter->second;
404 // Process the data from TZNAME.
406 DataReceived = ProcessTextVectors(&TimezoneStandardName[SeekCount],
407 &TimezoneStandardData[SeekCount], true, "TZNAME");
411 for(multimap<string,string>::iterator propiter = DataReceived.begin();
412 propiter != DataReceived.end();
415 NewTZData.TimeZoneNameTokens.push_back("");
416 NewTZData.TimeZoneNameLanguage.push_back("");
417 NewTZData.TimeZoneNameData.push_back("");
419 bool TokenData = false;
420 string PropertyTokens;
422 PropertyNameData = (string*)&propiter->first;
424 PropertyData = SplitValues(*PropertyNameData);
426 for(map<string,string>::iterator dataiter = PropertyData.begin();
427 dataiter != PropertyData.end(); dataiter++){
429 if (dataiter->first == "LANGUAGE"){
431 NewTZData.TimeZoneNameLanguage[ObjectSeekCount] = dataiter->second;
435 if (TokenData == false){
438 PropertyTokens += ";";
441 PropertyTokens += dataiter->first;
442 PropertyTokens += "=";
443 PropertyTokens += dataiter->second;
449 if (PropertyTokens.size() > 0){
450 NewTZData.TimeZoneNameTokens[ObjectSeekCount] = PropertyTokens;
453 NewTZData.TimeZoneNameData[ObjectSeekCount] = propiter->second;
461 // Process data from X-*
463 for(vector<string>::iterator propiter = TimezoneStandardName[SeekCount].begin();
464 propiter != TimezoneStandardName[SeekCount].end(); ++propiter){
466 if (propiter->substr(0,2) == "X-" &&
467 propiter->size() > 2){
469 NewTZData.XTokensData.push_back(TimezoneStandardData[SeekCount][ObjectSeekCount]);
470 NewTZData.XTokensDataTokens.push_back(TimezoneStandardName[SeekCount][ObjectSeekCount]);
478 // Check if the required values were given and
479 // insert NewTZData into the vector list of
480 // standard timezones.
482 if (DateTimeStartFound == true &&
483 TimeZoneOffsetToFound == true &&
484 TimeZoneOffsetFromFound == true){
486 TimezoneStandardCollection.push_back(NewTZData);
497 for (vector<vector<string>>::iterator tzsiter = TimezoneDaylightName.begin();
498 tzsiter != TimezoneDaylightName.end(); tzsiter++){
500 bool DateTimeStartFound = false;
501 bool TimeZoneOffsetToFound = false;
502 bool TimeZoneOffsetFromFound = false;
504 TimezoneDataStruct NewTZData;
506 // Process the data from DTSTART.
508 DataReceived = ProcessTextVectors(&TimezoneDaylightName[SeekCount],
509 &TimezoneDaylightData[SeekCount], false, "DTSTART");
511 if (DataReceived.begin() != DataReceived.end()){
514 NewTZData.DateTimeStartTokens = DataReceived.begin()->first.substr(8);
517 catch(const out_of_range &oor){
518 // Do nothing as there is no data.
521 NewTZData.DateTimeStartData = DataReceived.begin()->second;
522 DateTimeStartFound = true;
526 // Process the data from TZOFFSETFROM.
528 DataReceived = ProcessTextVectors(&TimezoneDaylightName[SeekCount],
529 &TimezoneDaylightData[SeekCount], false, "TZOFFSETFROM");
531 if (DataReceived.begin() != DataReceived.end()){
534 NewTZData.TimeZoneOffsetFromTokens = DataReceived.begin()->first.substr(13);
537 catch(const out_of_range &oor){
538 // Do nothing as there is no data.
541 NewTZData.TimeZoneOffsetFromData = DataReceived.begin()->second;
542 TimeZoneOffsetFromFound = true;
546 // Process the data from TZOFFSETTO.
548 DataReceived = ProcessTextVectors(&TimezoneDaylightName[SeekCount],
549 &TimezoneDaylightData[SeekCount], false, "TZOFFSETTO");
551 if (DataReceived.begin() != DataReceived.end()){
554 NewTZData.TimeZoneOffsetToTokens = DataReceived.begin()->first.substr(11);
557 catch(const out_of_range &oor){
558 // Do nothing as there is no data.
561 NewTZData.TimeZoneOffsetToData = DataReceived.begin()->second;
562 TimeZoneOffsetToFound = true;
566 // Process the data from RRULE.
568 DataReceived = ProcessTextVectors(&TimezoneDaylightName[SeekCount],
569 &TimezoneDaylightData[SeekCount], false, "RRULE");
571 if (DataReceived.begin() != DataReceived.end()){
574 NewTZData.RecurranceRuleDataTokens = DataReceived.begin()->first.substr(6);
577 catch(const out_of_range &oor){
578 // Do nothing as there is no data.
581 NewTZData.RecurranceRuleData = DataReceived.begin()->second;
585 // Process the data from COMMENT.
587 DataReceived = ProcessTextVectors(&TimezoneDaylightName[SeekCount],
588 &TimezoneDaylightData[SeekCount], true, "COMMENT");
592 for(multimap<string,string>::iterator propiter = DataReceived.begin();
593 propiter != DataReceived.end();
596 NewTZData.CommentListTokens.push_back("");
597 NewTZData.CommentListAltRep.push_back("");
598 NewTZData.CommentListLanguage.push_back("");
599 NewTZData.CommentList.push_back("");
601 bool TokenData = false;
602 string PropertyTokens;
604 PropertyNameData = (string*)&propiter->first;
606 PropertyData = SplitValues(*PropertyNameData);
608 for(map<string,string>::iterator propdataiter = PropertyData.begin();
609 propdataiter != PropertyData.end(); propdataiter++){
611 if (propdataiter->first == "ALTREP"){
613 NewTZData.CommentListAltRep[ObjectSeekCount] = propdataiter->second;
615 } else if (propdataiter->first == "LANGUAGE"){
617 NewTZData.CommentListLanguage[ObjectSeekCount] = propdataiter->second;
621 if (TokenData == false){
624 PropertyTokens += ";";
627 PropertyTokens += propdataiter->first;
628 PropertyTokens += "=";
629 PropertyTokens += propdataiter->second;
635 if (PropertyTokens.size() > 0){
636 NewTZData.CommentListTokens[ObjectSeekCount] = PropertyTokens;
639 NewTZData.CommentList[ObjectSeekCount] = propiter->second;
645 // Process the data from RDATE.
647 DataReceived = ProcessTextVectors(&TimezoneDaylightName[SeekCount],
648 &TimezoneDaylightData[SeekCount], true, "RDATE");
652 for(multimap<string,string>::iterator propiter = DataReceived.begin();
653 propiter != DataReceived.end();
656 NewTZData.RecurranceDateDataTokens.push_back("");
657 NewTZData.RecurranceDateDataValue.push_back("");
658 NewTZData.RecurranceDateDataTimeZoneParam.push_back("");
659 NewTZData.RecurranceDateData.push_back("");
661 bool TokenData = false;
662 string PropertyTokens;
664 PropertyNameData = (string*)&propiter->first;
666 PropertyData = SplitValues(*PropertyNameData);
668 for(map<string,string>::iterator dataiter = PropertyData.begin();
669 dataiter != PropertyData.end(); dataiter++){
671 if (dataiter->first == "VALUE"){
673 NewTZData.RecurranceDateDataValue[ObjectSeekCount] = dataiter->second;
675 } else if (dataiter->first == "TZID"){
677 NewTZData.RecurranceDateDataTimeZoneParam[ObjectSeekCount] = dataiter->second;
681 if (TokenData == false){
684 PropertyTokens += ";";
687 PropertyTokens += dataiter->first;
688 PropertyTokens += "=";
689 PropertyTokens += dataiter->second;
695 if (PropertyTokens.size() > 0){
696 NewTZData.RecurranceDateDataTokens[ObjectSeekCount] = PropertyTokens;
699 NewTZData.RecurranceDateData[ObjectSeekCount] = propiter->second;
705 // Process the data from TZNAME.
707 DataReceived = ProcessTextVectors(&TimezoneDaylightName[SeekCount],
708 &TimezoneDaylightData[SeekCount], true, "TZNAME");
712 for(multimap<string,string>::iterator propiter = DataReceived.begin();
713 propiter != DataReceived.end();
716 NewTZData.TimeZoneNameTokens.push_back("");
717 NewTZData.TimeZoneNameLanguage.push_back("");
718 NewTZData.TimeZoneNameData.push_back("");
720 bool TokenData = false;
721 string PropertyTokens;
723 PropertyNameData = (string*)&propiter->first;
725 PropertyData = SplitValues(*PropertyNameData);
727 for(map<string,string>::iterator dataiter = PropertyData.begin();
728 dataiter != PropertyData.end(); dataiter++){
730 if (dataiter->first == "LANGUAGE"){
732 NewTZData.TimeZoneNameLanguage[ObjectSeekCount] = dataiter->second;
736 if (TokenData == false){
739 PropertyTokens += ";";
742 PropertyTokens += dataiter->first;
743 PropertyTokens += "=";
744 PropertyTokens += dataiter->second;
750 if (PropertyTokens.size() > 0){
751 NewTZData.TimeZoneNameTokens[ObjectSeekCount] = PropertyTokens;
754 NewTZData.TimeZoneNameData[ObjectSeekCount] = propiter->second;
762 // Process data from X-*
764 for(vector<string>::iterator propiter = TimezoneDaylightName[SeekCount].begin();
765 propiter != TimezoneDaylightName[SeekCount].end(); ++propiter){
767 if (propiter->substr(0,2) == "X-" &&
768 propiter->size() > 2){
770 NewTZData.XTokensData.push_back(TimezoneDaylightData[SeekCount][ObjectSeekCount]);
771 NewTZData.XTokensDataTokens.push_back(TimezoneDaylightName[SeekCount][ObjectSeekCount]);
779 // Check if the required values were given and
780 // insert NewTZData into the vector list of
781 // daylight timezones.
783 if (DateTimeStartFound == true &&
784 TimeZoneOffsetToFound == true &&
785 TimeZoneOffsetFromFound == true){
787 TimezoneDaylightCollection.push_back(NewTZData);
797 void CalendarTimezoneObject::ProcessStandardDaylight(){
801 bool TZMode = false; // False = STANDARD, True = DAYLIGHT.
802 bool ValidBegin = false;
803 vector<string> TimezoneObjectName;
804 vector<string> TimezoneObjectData;
806 for (vector<string>::iterator iter = ObjectName.begin();
807 iter != ObjectName.end(); iter++){
809 // Check if the current name is BEGIN and
810 // data is either STANDARD or DAYLIGHT.
812 if (ObjectName[SeekCount] == "BEGIN" &&
813 (ObjectData[SeekCount] == "STANDARD" ||
814 ObjectData[SeekCount] == "DAYLIGHT")){
816 if (ValidBegin == false){
822 if (ObjectData[SeekCount] == "STANDARD"){
824 } else if (ObjectData[SeekCount] == "DAYLIGHT") {
833 // Check if current name is END and
834 // data is either STANDARD or DAYLIGHT.
836 if (ObjectName[SeekCount] == "END" &&
837 (ObjectData[SeekCount] == "STANDARD" ||
838 ObjectData[SeekCount] == "DAYLIGHT") &&
841 if (TZMode == false && TimezoneObjectName.size() > 0){
842 TimezoneStandardName.push_back(TimezoneObjectName);
843 TimezoneStandardData.push_back(TimezoneObjectData);
845 TimezoneDaylightName.push_back(TimezoneObjectName);
846 TimezoneDaylightData.push_back(TimezoneObjectData);
849 TimezoneObjectName.clear();
850 TimezoneObjectData.clear();
856 if (ValidBegin == true){
858 TimezoneObjectName.push_back(ObjectName[SeekCount]);
859 TimezoneObjectData.push_back(ObjectData[SeekCount]);