vtzone.cpp 88 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644
  1. // Copyright (C) 2016 and later: Unicode, Inc. and others.
  2. // License & terms of use: http://www.unicode.org/copyright.html
  3. /*
  4. *******************************************************************************
  5. * Copyright (C) 2007-2016, International Business Machines Corporation and
  6. * others. All Rights Reserved.
  7. *******************************************************************************
  8. */
  9. #include "utypeinfo.h" // for 'typeid' to work
  10. #include "unicode/utypes.h"
  11. #if !UCONFIG_NO_FORMATTING
  12. #include "unicode/vtzone.h"
  13. #include "unicode/rbtz.h"
  14. #include "unicode/ucal.h"
  15. #include "unicode/ures.h"
  16. #include "cmemory.h"
  17. #include "uvector.h"
  18. #include "gregoimp.h"
  19. #include "uassert.h"
  20. U_NAMESPACE_BEGIN
  21. // This is the deleter that will be use to remove TimeZoneRule
  22. U_CDECL_BEGIN
  23. static void U_CALLCONV
  24. deleteTimeZoneRule(void* obj) {
  25. delete (TimeZoneRule*) obj;
  26. }
  27. U_CDECL_END
  28. // Smybol characters used by RFC2445 VTIMEZONE
  29. static const UChar COLON = 0x3A; /* : */
  30. static const UChar SEMICOLON = 0x3B; /* ; */
  31. static const UChar EQUALS_SIGN = 0x3D; /* = */
  32. static const UChar COMMA = 0x2C; /* , */
  33. static const UChar PLUS = 0x2B; /* + */
  34. static const UChar MINUS = 0x2D; /* - */
  35. // RFC2445 VTIMEZONE tokens
  36. static const UChar ICAL_BEGIN_VTIMEZONE[] = {0x42, 0x45, 0x47, 0x49, 0x4E, 0x3A, 0x56, 0x54, 0x49, 0x4D, 0x45, 0x5A, 0x4F, 0x4E, 0x45, 0}; /* "BEGIN:VTIMEZONE" */
  37. static const UChar ICAL_END_VTIMEZONE[] = {0x45, 0x4E, 0x44, 0x3A, 0x56, 0x54, 0x49, 0x4D, 0x45, 0x5A, 0x4F, 0x4E, 0x45, 0}; /* "END:VTIMEZONE" */
  38. static const UChar ICAL_BEGIN[] = {0x42, 0x45, 0x47, 0x49, 0x4E, 0}; /* "BEGIN" */
  39. static const UChar ICAL_END[] = {0x45, 0x4E, 0x44, 0}; /* "END" */
  40. static const UChar ICAL_VTIMEZONE[] = {0x56, 0x54, 0x49, 0x4D, 0x45, 0x5A, 0x4F, 0x4E, 0x45, 0}; /* "VTIMEZONE" */
  41. static const UChar ICAL_TZID[] = {0x54, 0x5A, 0x49, 0x44, 0}; /* "TZID" */
  42. static const UChar ICAL_STANDARD[] = {0x53, 0x54, 0x41, 0x4E, 0x44, 0x41, 0x52, 0x44, 0}; /* "STANDARD" */
  43. static const UChar ICAL_DAYLIGHT[] = {0x44, 0x41, 0x59, 0x4C, 0x49, 0x47, 0x48, 0x54, 0}; /* "DAYLIGHT" */
  44. static const UChar ICAL_DTSTART[] = {0x44, 0x54, 0x53, 0x54, 0x41, 0x52, 0x54, 0}; /* "DTSTART" */
  45. static const UChar ICAL_TZOFFSETFROM[] = {0x54, 0x5A, 0x4F, 0x46, 0x46, 0x53, 0x45, 0x54, 0x46, 0x52, 0x4F, 0x4D, 0}; /* "TZOFFSETFROM" */
  46. static const UChar ICAL_TZOFFSETTO[] = {0x54, 0x5A, 0x4F, 0x46, 0x46, 0x53, 0x45, 0x54, 0x54, 0x4F, 0}; /* "TZOFFSETTO" */
  47. static const UChar ICAL_RDATE[] = {0x52, 0x44, 0x41, 0x54, 0x45, 0}; /* "RDATE" */
  48. static const UChar ICAL_RRULE[] = {0x52, 0x52, 0x55, 0x4C, 0x45, 0}; /* "RRULE" */
  49. static const UChar ICAL_TZNAME[] = {0x54, 0x5A, 0x4E, 0x41, 0x4D, 0x45, 0}; /* "TZNAME" */
  50. static const UChar ICAL_TZURL[] = {0x54, 0x5A, 0x55, 0x52, 0x4C, 0}; /* "TZURL" */
  51. static const UChar ICAL_LASTMOD[] = {0x4C, 0x41, 0x53, 0x54, 0x2D, 0x4D, 0x4F, 0x44, 0x49, 0x46, 0x49, 0x45, 0x44, 0}; /* "LAST-MODIFIED" */
  52. static const UChar ICAL_FREQ[] = {0x46, 0x52, 0x45, 0x51, 0}; /* "FREQ" */
  53. static const UChar ICAL_UNTIL[] = {0x55, 0x4E, 0x54, 0x49, 0x4C, 0}; /* "UNTIL" */
  54. static const UChar ICAL_YEARLY[] = {0x59, 0x45, 0x41, 0x52, 0x4C, 0x59, 0}; /* "YEARLY" */
  55. static const UChar ICAL_BYMONTH[] = {0x42, 0x59, 0x4D, 0x4F, 0x4E, 0x54, 0x48, 0}; /* "BYMONTH" */
  56. static const UChar ICAL_BYDAY[] = {0x42, 0x59, 0x44, 0x41, 0x59, 0}; /* "BYDAY" */
  57. static const UChar ICAL_BYMONTHDAY[] = {0x42, 0x59, 0x4D, 0x4F, 0x4E, 0x54, 0x48, 0x44, 0x41, 0x59, 0}; /* "BYMONTHDAY" */
  58. static const UChar ICAL_NEWLINE[] = {0x0D, 0x0A, 0}; /* CRLF */
  59. static const UChar ICAL_DOW_NAMES[7][3] = {
  60. {0x53, 0x55, 0}, /* "SU" */
  61. {0x4D, 0x4F, 0}, /* "MO" */
  62. {0x54, 0x55, 0}, /* "TU" */
  63. {0x57, 0x45, 0}, /* "WE" */
  64. {0x54, 0x48, 0}, /* "TH" */
  65. {0x46, 0x52, 0}, /* "FR" */
  66. {0x53, 0x41, 0} /* "SA" */};
  67. // Month length for non-leap year
  68. static const int32_t MONTHLENGTH[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  69. // ICU custom property
  70. static const UChar ICU_TZINFO_PROP[] = {0x58, 0x2D, 0x54, 0x5A, 0x49, 0x4E, 0x46, 0x4F, 0x3A, 0}; /* "X-TZINFO:" */
  71. static const UChar ICU_TZINFO_PARTIAL[] = {0x2F, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6C, 0x40, 0}; /* "/Partial@" */
  72. static const UChar ICU_TZINFO_SIMPLE[] = {0x2F, 0x53, 0x69, 0x6D, 0x70, 0x6C, 0x65, 0x40, 0}; /* "/Simple@" */
  73. /*
  74. * Simple fixed digit ASCII number to integer converter
  75. */
  76. static int32_t parseAsciiDigits(const UnicodeString& str, int32_t start, int32_t length, UErrorCode& status) {
  77. if (U_FAILURE(status)) {
  78. return 0;
  79. }
  80. if (length <= 0 || str.length() < start || (start + length) > str.length()) {
  81. status = U_INVALID_FORMAT_ERROR;
  82. return 0;
  83. }
  84. int32_t sign = 1;
  85. if (str.charAt(start) == PLUS) {
  86. start++;
  87. length--;
  88. } else if (str.charAt(start) == MINUS) {
  89. sign = -1;
  90. start++;
  91. length--;
  92. }
  93. int32_t num = 0;
  94. for (int32_t i = 0; i < length; i++) {
  95. int32_t digit = str.charAt(start + i) - 0x0030;
  96. if (digit < 0 || digit > 9) {
  97. status = U_INVALID_FORMAT_ERROR;
  98. return 0;
  99. }
  100. num = 10 * num + digit;
  101. }
  102. return sign * num;
  103. }
  104. static UnicodeString& appendAsciiDigits(int32_t number, uint8_t length, UnicodeString& str) {
  105. UBool negative = FALSE;
  106. int32_t digits[10]; // max int32_t is 10 decimal digits
  107. int32_t i;
  108. if (number < 0) {
  109. negative = TRUE;
  110. number *= -1;
  111. }
  112. length = length > 10 ? 10 : length;
  113. if (length == 0) {
  114. // variable length
  115. i = 0;
  116. do {
  117. digits[i++] = number % 10;
  118. number /= 10;
  119. } while (number != 0);
  120. length = i;
  121. } else {
  122. // fixed digits
  123. for (i = 0; i < length; i++) {
  124. digits[i] = number % 10;
  125. number /= 10;
  126. }
  127. }
  128. if (negative) {
  129. str.append(MINUS);
  130. }
  131. for (i = length - 1; i >= 0; i--) {
  132. str.append((UChar)(digits[i] + 0x0030));
  133. }
  134. return str;
  135. }
  136. static UnicodeString& appendMillis(UDate date, UnicodeString& str) {
  137. UBool negative = FALSE;
  138. int32_t digits[20]; // max int64_t is 20 decimal digits
  139. int32_t i;
  140. int64_t number;
  141. if (date < MIN_MILLIS) {
  142. number = (int64_t)MIN_MILLIS;
  143. } else if (date > MAX_MILLIS) {
  144. number = (int64_t)MAX_MILLIS;
  145. } else {
  146. number = (int64_t)date;
  147. }
  148. if (number < 0) {
  149. negative = TRUE;
  150. number *= -1;
  151. }
  152. i = 0;
  153. do {
  154. digits[i++] = (int32_t)(number % 10);
  155. number /= 10;
  156. } while (number != 0);
  157. if (negative) {
  158. str.append(MINUS);
  159. }
  160. i--;
  161. while (i >= 0) {
  162. str.append((UChar)(digits[i--] + 0x0030));
  163. }
  164. return str;
  165. }
  166. /*
  167. * Convert date/time to RFC2445 Date-Time form #1 DATE WITH LOCAL TIME
  168. */
  169. static UnicodeString& getDateTimeString(UDate time, UnicodeString& str) {
  170. int32_t year, month, dom, dow, doy, mid;
  171. Grego::timeToFields(time, year, month, dom, dow, doy, mid);
  172. str.remove();
  173. appendAsciiDigits(year, 4, str);
  174. appendAsciiDigits(month + 1, 2, str);
  175. appendAsciiDigits(dom, 2, str);
  176. str.append((UChar)0x0054 /*'T'*/);
  177. int32_t t = mid;
  178. int32_t hour = t / U_MILLIS_PER_HOUR;
  179. t %= U_MILLIS_PER_HOUR;
  180. int32_t min = t / U_MILLIS_PER_MINUTE;
  181. t %= U_MILLIS_PER_MINUTE;
  182. int32_t sec = t / U_MILLIS_PER_SECOND;
  183. appendAsciiDigits(hour, 2, str);
  184. appendAsciiDigits(min, 2, str);
  185. appendAsciiDigits(sec, 2, str);
  186. return str;
  187. }
  188. /*
  189. * Convert date/time to RFC2445 Date-Time form #2 DATE WITH UTC TIME
  190. */
  191. static UnicodeString& getUTCDateTimeString(UDate time, UnicodeString& str) {
  192. getDateTimeString(time, str);
  193. str.append((UChar)0x005A /*'Z'*/);
  194. return str;
  195. }
  196. /*
  197. * Parse RFC2445 Date-Time form #1 DATE WITH LOCAL TIME and
  198. * #2 DATE WITH UTC TIME
  199. */
  200. static UDate parseDateTimeString(const UnicodeString& str, int32_t offset, UErrorCode& status) {
  201. if (U_FAILURE(status)) {
  202. return 0.0;
  203. }
  204. int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0;
  205. UBool isUTC = FALSE;
  206. UBool isValid = FALSE;
  207. do {
  208. int length = str.length();
  209. if (length != 15 && length != 16) {
  210. // FORM#1 15 characters, such as "20060317T142115"
  211. // FORM#2 16 characters, such as "20060317T142115Z"
  212. break;
  213. }
  214. if (str.charAt(8) != 0x0054) {
  215. // charcter "T" must be used for separating date and time
  216. break;
  217. }
  218. if (length == 16) {
  219. if (str.charAt(15) != 0x005A) {
  220. // invalid format
  221. break;
  222. }
  223. isUTC = TRUE;
  224. }
  225. year = parseAsciiDigits(str, 0, 4, status);
  226. month = parseAsciiDigits(str, 4, 2, status) - 1; // 0-based
  227. day = parseAsciiDigits(str, 6, 2, status);
  228. hour = parseAsciiDigits(str, 9, 2, status);
  229. min = parseAsciiDigits(str, 11, 2, status);
  230. sec = parseAsciiDigits(str, 13, 2, status);
  231. if (U_FAILURE(status)) {
  232. break;
  233. }
  234. // check valid range
  235. int32_t maxDayOfMonth = Grego::monthLength(year, month);
  236. if (year < 0 || month < 0 || month > 11 || day < 1 || day > maxDayOfMonth ||
  237. hour < 0 || hour >= 24 || min < 0 || min >= 60 || sec < 0 || sec >= 60) {
  238. break;
  239. }
  240. isValid = TRUE;
  241. } while(false);
  242. if (!isValid) {
  243. status = U_INVALID_FORMAT_ERROR;
  244. return 0.0;
  245. }
  246. // Calculate the time
  247. UDate time = Grego::fieldsToDay(year, month, day) * U_MILLIS_PER_DAY;
  248. time += (hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE + sec * U_MILLIS_PER_SECOND);
  249. if (!isUTC) {
  250. time -= offset;
  251. }
  252. return time;
  253. }
  254. /*
  255. * Convert RFC2445 utc-offset string to milliseconds
  256. */
  257. static int32_t offsetStrToMillis(const UnicodeString& str, UErrorCode& status) {
  258. if (U_FAILURE(status)) {
  259. return 0;
  260. }
  261. UBool isValid = FALSE;
  262. int32_t sign = 0, hour = 0, min = 0, sec = 0;
  263. do {
  264. int length = str.length();
  265. if (length != 5 && length != 7) {
  266. // utf-offset must be 5 or 7 characters
  267. break;
  268. }
  269. // sign
  270. UChar s = str.charAt(0);
  271. if (s == PLUS) {
  272. sign = 1;
  273. } else if (s == MINUS) {
  274. sign = -1;
  275. } else {
  276. // utf-offset must start with "+" or "-"
  277. break;
  278. }
  279. hour = parseAsciiDigits(str, 1, 2, status);
  280. min = parseAsciiDigits(str, 3, 2, status);
  281. if (length == 7) {
  282. sec = parseAsciiDigits(str, 5, 2, status);
  283. }
  284. if (U_FAILURE(status)) {
  285. break;
  286. }
  287. isValid = true;
  288. } while(false);
  289. if (!isValid) {
  290. status = U_INVALID_FORMAT_ERROR;
  291. return 0;
  292. }
  293. int32_t millis = sign * ((hour * 60 + min) * 60 + sec) * 1000;
  294. return millis;
  295. }
  296. /*
  297. * Convert milliseconds to RFC2445 utc-offset string
  298. */
  299. static void millisToOffset(int32_t millis, UnicodeString& str) {
  300. str.remove();
  301. if (millis >= 0) {
  302. str.append(PLUS);
  303. } else {
  304. str.append(MINUS);
  305. millis = -millis;
  306. }
  307. int32_t hour, min, sec;
  308. int32_t t = millis / 1000;
  309. sec = t % 60;
  310. t = (t - sec) / 60;
  311. min = t % 60;
  312. hour = t / 60;
  313. appendAsciiDigits(hour, 2, str);
  314. appendAsciiDigits(min, 2, str);
  315. appendAsciiDigits(sec, 2, str);
  316. }
  317. /*
  318. * Create a default TZNAME from TZID
  319. */
  320. static void getDefaultTZName(const UnicodeString tzid, UBool isDST, UnicodeString& zonename) {
  321. zonename = tzid;
  322. if (isDST) {
  323. zonename += UNICODE_STRING_SIMPLE("(DST)");
  324. } else {
  325. zonename += UNICODE_STRING_SIMPLE("(STD)");
  326. }
  327. }
  328. /*
  329. * Parse individual RRULE
  330. *
  331. * On return -
  332. *
  333. * month calculated by BYMONTH-1, or -1 when not found
  334. * dow day of week in BYDAY, or 0 when not found
  335. * wim day of week ordinal number in BYDAY, or 0 when not found
  336. * dom an array of day of month
  337. * domCount number of availble days in dom (domCount is specifying the size of dom on input)
  338. * until time defined by UNTIL attribute or MIN_MILLIS if not available
  339. */
  340. static void parseRRULE(const UnicodeString& rrule, int32_t& month, int32_t& dow, int32_t& wim,
  341. int32_t* dom, int32_t& domCount, UDate& until, UErrorCode& status) {
  342. if (U_FAILURE(status)) {
  343. return;
  344. }
  345. int32_t numDom = 0;
  346. month = -1;
  347. dow = 0;
  348. wim = 0;
  349. until = MIN_MILLIS;
  350. UBool yearly = FALSE;
  351. //UBool parseError = FALSE;
  352. int32_t prop_start = 0;
  353. int32_t prop_end;
  354. UnicodeString prop, attr, value;
  355. UBool nextProp = TRUE;
  356. while (nextProp) {
  357. prop_end = rrule.indexOf(SEMICOLON, prop_start);
  358. if (prop_end == -1) {
  359. prop.setTo(rrule, prop_start);
  360. nextProp = FALSE;
  361. } else {
  362. prop.setTo(rrule, prop_start, prop_end - prop_start);
  363. prop_start = prop_end + 1;
  364. }
  365. int32_t eql = prop.indexOf(EQUALS_SIGN);
  366. if (eql != -1) {
  367. attr.setTo(prop, 0, eql);
  368. value.setTo(prop, eql + 1);
  369. } else {
  370. goto rruleParseError;
  371. }
  372. if (attr.compare(ICAL_FREQ, -1) == 0) {
  373. // only support YEARLY frequency type
  374. if (value.compare(ICAL_YEARLY, -1) == 0) {
  375. yearly = TRUE;
  376. } else {
  377. goto rruleParseError;
  378. }
  379. } else if (attr.compare(ICAL_UNTIL, -1) == 0) {
  380. // ISO8601 UTC format, for example, "20060315T020000Z"
  381. until = parseDateTimeString(value, 0, status);
  382. if (U_FAILURE(status)) {
  383. goto rruleParseError;
  384. }
  385. } else if (attr.compare(ICAL_BYMONTH, -1) == 0) {
  386. // Note: BYMONTH may contain multiple months, but only single month make sense for
  387. // VTIMEZONE property.
  388. if (value.length() > 2) {
  389. goto rruleParseError;
  390. }
  391. month = parseAsciiDigits(value, 0, value.length(), status) - 1;
  392. if (U_FAILURE(status) || month < 0 || month >= 12) {
  393. goto rruleParseError;
  394. }
  395. } else if (attr.compare(ICAL_BYDAY, -1) == 0) {
  396. // Note: BYDAY may contain multiple day of week separated by comma. It is unlikely used for
  397. // VTIMEZONE property. We do not support the case.
  398. // 2-letter format is used just for representing a day of week, for example, "SU" for Sunday
  399. // 3 or 4-letter format is used for represeinging Nth day of week, for example, "-1SA" for last Saturday
  400. int32_t length = value.length();
  401. if (length < 2 || length > 4) {
  402. goto rruleParseError;
  403. }
  404. if (length > 2) {
  405. // Nth day of week
  406. int32_t sign = 1;
  407. if (value.charAt(0) == PLUS) {
  408. sign = 1;
  409. } else if (value.charAt(0) == MINUS) {
  410. sign = -1;
  411. } else if (length == 4) {
  412. goto rruleParseError;
  413. }
  414. int32_t n = parseAsciiDigits(value, length - 3, 1, status);
  415. if (U_FAILURE(status) || n == 0 || n > 4) {
  416. goto rruleParseError;
  417. }
  418. wim = n * sign;
  419. value.remove(0, length - 2);
  420. }
  421. int32_t wday;
  422. for (wday = 0; wday < 7; wday++) {
  423. if (value.compare(ICAL_DOW_NAMES[wday], 2) == 0) {
  424. break;
  425. }
  426. }
  427. if (wday < 7) {
  428. // Sunday(1) - Saturday(7)
  429. dow = wday + 1;
  430. } else {
  431. goto rruleParseError;
  432. }
  433. } else if (attr.compare(ICAL_BYMONTHDAY, -1) == 0) {
  434. // Note: BYMONTHDAY may contain multiple days delimitted by comma
  435. //
  436. // A value of BYMONTHDAY could be negative, for example, -1 means
  437. // the last day in a month
  438. int32_t dom_idx = 0;
  439. int32_t dom_start = 0;
  440. int32_t dom_end;
  441. UBool nextDOM = TRUE;
  442. while (nextDOM) {
  443. dom_end = value.indexOf(COMMA, dom_start);
  444. if (dom_end == -1) {
  445. dom_end = value.length();
  446. nextDOM = FALSE;
  447. }
  448. if (dom_idx < domCount) {
  449. dom[dom_idx] = parseAsciiDigits(value, dom_start, dom_end - dom_start, status);
  450. if (U_FAILURE(status)) {
  451. goto rruleParseError;
  452. }
  453. dom_idx++;
  454. } else {
  455. status = U_BUFFER_OVERFLOW_ERROR;
  456. goto rruleParseError;
  457. }
  458. dom_start = dom_end + 1;
  459. }
  460. numDom = dom_idx;
  461. }
  462. }
  463. if (!yearly) {
  464. // FREQ=YEARLY must be set
  465. goto rruleParseError;
  466. }
  467. // Set actual number of parsed DOM (ICAL_BYMONTHDAY)
  468. domCount = numDom;
  469. return;
  470. rruleParseError:
  471. if (U_SUCCESS(status)) {
  472. // Set error status
  473. status = U_INVALID_FORMAT_ERROR;
  474. }
  475. }
  476. static TimeZoneRule* createRuleByRRULE(const UnicodeString& zonename, int rawOffset, int dstSavings, UDate start,
  477. UVector* dates, int fromOffset, UErrorCode& status) {
  478. if (U_FAILURE(status)) {
  479. return NULL;
  480. }
  481. if (dates == NULL || dates->size() == 0) {
  482. status = U_ILLEGAL_ARGUMENT_ERROR;
  483. return NULL;
  484. }
  485. int32_t i, j;
  486. DateTimeRule *adtr = NULL;
  487. // Parse the first rule
  488. UnicodeString rrule = *((UnicodeString*)dates->elementAt(0));
  489. int32_t month, dayOfWeek, nthDayOfWeek, dayOfMonth = 0;
  490. int32_t days[7];
  491. int32_t daysCount = UPRV_LENGTHOF(days);
  492. UDate until;
  493. parseRRULE(rrule, month, dayOfWeek, nthDayOfWeek, days, daysCount, until, status);
  494. if (U_FAILURE(status)) {
  495. return NULL;
  496. }
  497. if (dates->size() == 1) {
  498. // No more rules
  499. if (daysCount > 1) {
  500. // Multiple BYMONTHDAY values
  501. if (daysCount != 7 || month == -1 || dayOfWeek == 0) {
  502. // Only support the rule using 7 continuous days
  503. // BYMONTH and BYDAY must be set at the same time
  504. goto unsupportedRRule;
  505. }
  506. int32_t firstDay = 31; // max possible number of dates in a month
  507. for (i = 0; i < 7; i++) {
  508. // Resolve negative day numbers. A negative day number should
  509. // not be used in February, but if we see such case, we use 28
  510. // as the base.
  511. if (days[i] < 0) {
  512. days[i] = MONTHLENGTH[month] + days[i] + 1;
  513. }
  514. if (days[i] < firstDay) {
  515. firstDay = days[i];
  516. }
  517. }
  518. // Make sure days are continuous
  519. for (i = 1; i < 7; i++) {
  520. UBool found = FALSE;
  521. for (j = 0; j < 7; j++) {
  522. if (days[j] == firstDay + i) {
  523. found = TRUE;
  524. break;
  525. }
  526. }
  527. if (!found) {
  528. // days are not continuous
  529. goto unsupportedRRule;
  530. }
  531. }
  532. // Use DOW_GEQ_DOM rule with firstDay as the start date
  533. dayOfMonth = firstDay;
  534. }
  535. } else {
  536. // Check if BYMONTH + BYMONTHDAY + BYDAY rule with multiple RRULE lines.
  537. // Otherwise, not supported.
  538. if (month == -1 || dayOfWeek == 0 || daysCount == 0) {
  539. // This is not the case
  540. goto unsupportedRRule;
  541. }
  542. // Parse the rest of rules if number of rules is not exceeding 7.
  543. // We can only support 7 continuous days starting from a day of month.
  544. if (dates->size() > 7) {
  545. goto unsupportedRRule;
  546. }
  547. // Note: To check valid date range across multiple rule is a little
  548. // bit complicated. For now, this code is not doing strict range
  549. // checking across month boundary
  550. int32_t earliestMonth = month;
  551. int32_t earliestDay = 31;
  552. for (i = 0; i < daysCount; i++) {
  553. int32_t dom = days[i];
  554. dom = dom > 0 ? dom : MONTHLENGTH[month] + dom + 1;
  555. earliestDay = dom < earliestDay ? dom : earliestDay;
  556. }
  557. int32_t anotherMonth = -1;
  558. for (i = 1; i < dates->size(); i++) {
  559. rrule = *((UnicodeString*)dates->elementAt(i));
  560. UDate tmp_until;
  561. int32_t tmp_month, tmp_dayOfWeek, tmp_nthDayOfWeek;
  562. int32_t tmp_days[7];
  563. int32_t tmp_daysCount = UPRV_LENGTHOF(tmp_days);
  564. parseRRULE(rrule, tmp_month, tmp_dayOfWeek, tmp_nthDayOfWeek, tmp_days, tmp_daysCount, tmp_until, status);
  565. if (U_FAILURE(status)) {
  566. return NULL;
  567. }
  568. // If UNTIL is newer than previous one, use the one
  569. if (tmp_until > until) {
  570. until = tmp_until;
  571. }
  572. // Check if BYMONTH + BYMONTHDAY + BYDAY rule
  573. if (tmp_month == -1 || tmp_dayOfWeek == 0 || tmp_daysCount == 0) {
  574. goto unsupportedRRule;
  575. }
  576. // Count number of BYMONTHDAY
  577. if (daysCount + tmp_daysCount > 7) {
  578. // We cannot support BYMONTHDAY more than 7
  579. goto unsupportedRRule;
  580. }
  581. // Check if the same BYDAY is used. Otherwise, we cannot
  582. // support the rule
  583. if (tmp_dayOfWeek != dayOfWeek) {
  584. goto unsupportedRRule;
  585. }
  586. // Check if the month is same or right next to the primary month
  587. if (tmp_month != month) {
  588. if (anotherMonth == -1) {
  589. int32_t diff = tmp_month - month;
  590. if (diff == -11 || diff == -1) {
  591. // Previous month
  592. anotherMonth = tmp_month;
  593. earliestMonth = anotherMonth;
  594. // Reset earliest day
  595. earliestDay = 31;
  596. } else if (diff == 11 || diff == 1) {
  597. // Next month
  598. anotherMonth = tmp_month;
  599. } else {
  600. // The day range cannot exceed more than 2 months
  601. goto unsupportedRRule;
  602. }
  603. } else if (tmp_month != month && tmp_month != anotherMonth) {
  604. // The day range cannot exceed more than 2 months
  605. goto unsupportedRRule;
  606. }
  607. }
  608. // If ealier month, go through days to find the earliest day
  609. if (tmp_month == earliestMonth) {
  610. for (j = 0; j < tmp_daysCount; j++) {
  611. tmp_days[j] = tmp_days[j] > 0 ? tmp_days[j] : MONTHLENGTH[tmp_month] + tmp_days[j] + 1;
  612. earliestDay = tmp_days[j] < earliestDay ? tmp_days[j] : earliestDay;
  613. }
  614. }
  615. daysCount += tmp_daysCount;
  616. }
  617. if (daysCount != 7) {
  618. // Number of BYMONTHDAY entries must be 7
  619. goto unsupportedRRule;
  620. }
  621. month = earliestMonth;
  622. dayOfMonth = earliestDay;
  623. }
  624. // Calculate start/end year and missing fields
  625. int32_t startYear, startMonth, startDOM, startDOW, startDOY, startMID;
  626. Grego::timeToFields(start + fromOffset, startYear, startMonth, startDOM,
  627. startDOW, startDOY, startMID);
  628. if (month == -1) {
  629. // If BYMONTH is not set, use the month of DTSTART
  630. month = startMonth;
  631. }
  632. if (dayOfWeek == 0 && nthDayOfWeek == 0 && dayOfMonth == 0) {
  633. // If only YEARLY is set, use the day of DTSTART as BYMONTHDAY
  634. dayOfMonth = startDOM;
  635. }
  636. int32_t endYear;
  637. if (until != MIN_MILLIS) {
  638. int32_t endMonth, endDOM, endDOW, endDOY, endMID;
  639. Grego::timeToFields(until, endYear, endMonth, endDOM, endDOW, endDOY, endMID);
  640. } else {
  641. endYear = AnnualTimeZoneRule::MAX_YEAR;
  642. }
  643. // Create the AnnualDateTimeRule
  644. if (dayOfWeek == 0 && nthDayOfWeek == 0 && dayOfMonth != 0) {
  645. // Day in month rule, for example, 15th day in the month
  646. adtr = new DateTimeRule(month, dayOfMonth, startMID, DateTimeRule::WALL_TIME);
  647. } else if (dayOfWeek != 0 && nthDayOfWeek != 0 && dayOfMonth == 0) {
  648. // Nth day of week rule, for example, last Sunday
  649. adtr = new DateTimeRule(month, nthDayOfWeek, dayOfWeek, startMID, DateTimeRule::WALL_TIME);
  650. } else if (dayOfWeek != 0 && nthDayOfWeek == 0 && dayOfMonth != 0) {
  651. // First day of week after day of month rule, for example,
  652. // first Sunday after 15th day in the month
  653. adtr = new DateTimeRule(month, dayOfMonth, dayOfWeek, TRUE, startMID, DateTimeRule::WALL_TIME);
  654. }
  655. if (adtr == NULL) {
  656. goto unsupportedRRule;
  657. }
  658. return new AnnualTimeZoneRule(zonename, rawOffset, dstSavings, adtr, startYear, endYear);
  659. unsupportedRRule:
  660. status = U_INVALID_STATE_ERROR;
  661. return NULL;
  662. }
  663. /*
  664. * Create a TimeZoneRule by the RDATE definition
  665. */
  666. static TimeZoneRule* createRuleByRDATE(const UnicodeString& zonename, int32_t rawOffset, int32_t dstSavings,
  667. UDate start, UVector* dates, int32_t fromOffset, UErrorCode& status) {
  668. if (U_FAILURE(status)) {
  669. return NULL;
  670. }
  671. TimeArrayTimeZoneRule *retVal = NULL;
  672. if (dates == NULL || dates->size() == 0) {
  673. // When no RDATE line is provided, use start (DTSTART)
  674. // as the transition time
  675. retVal = new TimeArrayTimeZoneRule(zonename, rawOffset, dstSavings,
  676. &start, 1, DateTimeRule::UTC_TIME);
  677. } else {
  678. // Create an array of transition times
  679. int32_t size = dates->size();
  680. UDate* times = (UDate*)uprv_malloc(sizeof(UDate) * size);
  681. if (times == NULL) {
  682. status = U_MEMORY_ALLOCATION_ERROR;
  683. return NULL;
  684. }
  685. for (int32_t i = 0; i < size; i++) {
  686. UnicodeString *datestr = (UnicodeString*)dates->elementAt(i);
  687. times[i] = parseDateTimeString(*datestr, fromOffset, status);
  688. if (U_FAILURE(status)) {
  689. uprv_free(times);
  690. return NULL;
  691. }
  692. }
  693. retVal = new TimeArrayTimeZoneRule(zonename, rawOffset, dstSavings,
  694. times, size, DateTimeRule::UTC_TIME);
  695. uprv_free(times);
  696. }
  697. return retVal;
  698. }
  699. /*
  700. * Check if the DOW rule specified by month, weekInMonth and dayOfWeek is equivalent
  701. * to the DateTimerule.
  702. */
  703. static UBool isEquivalentDateRule(int32_t month, int32_t weekInMonth, int32_t dayOfWeek, const DateTimeRule *dtrule) {
  704. if (month != dtrule->getRuleMonth() || dayOfWeek != dtrule->getRuleDayOfWeek()) {
  705. return FALSE;
  706. }
  707. if (dtrule->getTimeRuleType() != DateTimeRule::WALL_TIME) {
  708. // Do not try to do more intelligent comparison for now.
  709. return FALSE;
  710. }
  711. if (dtrule->getDateRuleType() == DateTimeRule::DOW
  712. && dtrule->getRuleWeekInMonth() == weekInMonth) {
  713. return TRUE;
  714. }
  715. int32_t ruleDOM = dtrule->getRuleDayOfMonth();
  716. if (dtrule->getDateRuleType() == DateTimeRule::DOW_GEQ_DOM) {
  717. if (ruleDOM%7 == 1 && (ruleDOM + 6)/7 == weekInMonth) {
  718. return TRUE;
  719. }
  720. if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - ruleDOM)%7 == 6
  721. && weekInMonth == -1*((MONTHLENGTH[month]-ruleDOM+1)/7)) {
  722. return TRUE;
  723. }
  724. }
  725. if (dtrule->getDateRuleType() == DateTimeRule::DOW_LEQ_DOM) {
  726. if (ruleDOM%7 == 0 && ruleDOM/7 == weekInMonth) {
  727. return TRUE;
  728. }
  729. if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - ruleDOM)%7 == 0
  730. && weekInMonth == -1*((MONTHLENGTH[month] - ruleDOM)/7 + 1)) {
  731. return TRUE;
  732. }
  733. }
  734. return FALSE;
  735. }
  736. /*
  737. * Convert the rule to its equivalent rule using WALL_TIME mode.
  738. * This function returns NULL when the specified DateTimeRule is already
  739. * using WALL_TIME mode.
  740. */
  741. static DateTimeRule* toWallTimeRule(const DateTimeRule* rule, int32_t rawOffset, int32_t dstSavings) {
  742. if (rule->getTimeRuleType() == DateTimeRule::WALL_TIME) {
  743. return NULL;
  744. }
  745. int32_t wallt = rule->getRuleMillisInDay();
  746. if (rule->getTimeRuleType() == DateTimeRule::UTC_TIME) {
  747. wallt += (rawOffset + dstSavings);
  748. } else if (rule->getTimeRuleType() == DateTimeRule::STANDARD_TIME) {
  749. wallt += dstSavings;
  750. }
  751. int32_t month = -1, dom = 0, dow = 0;
  752. DateTimeRule::DateRuleType dtype;
  753. int32_t dshift = 0;
  754. if (wallt < 0) {
  755. dshift = -1;
  756. wallt += U_MILLIS_PER_DAY;
  757. } else if (wallt >= U_MILLIS_PER_DAY) {
  758. dshift = 1;
  759. wallt -= U_MILLIS_PER_DAY;
  760. }
  761. month = rule->getRuleMonth();
  762. dom = rule->getRuleDayOfMonth();
  763. dow = rule->getRuleDayOfWeek();
  764. dtype = rule->getDateRuleType();
  765. if (dshift != 0) {
  766. if (dtype == DateTimeRule::DOW) {
  767. // Convert to DOW_GEW_DOM or DOW_LEQ_DOM rule first
  768. int32_t wim = rule->getRuleWeekInMonth();
  769. if (wim > 0) {
  770. dtype = DateTimeRule::DOW_GEQ_DOM;
  771. dom = 7 * (wim - 1) + 1;
  772. } else {
  773. dtype = DateTimeRule::DOW_LEQ_DOM;
  774. dom = MONTHLENGTH[month] + 7 * (wim + 1);
  775. }
  776. }
  777. // Shift one day before or after
  778. dom += dshift;
  779. if (dom == 0) {
  780. month--;
  781. month = month < UCAL_JANUARY ? UCAL_DECEMBER : month;
  782. dom = MONTHLENGTH[month];
  783. } else if (dom > MONTHLENGTH[month]) {
  784. month++;
  785. month = month > UCAL_DECEMBER ? UCAL_JANUARY : month;
  786. dom = 1;
  787. }
  788. if (dtype != DateTimeRule::DOM) {
  789. // Adjust day of week
  790. dow += dshift;
  791. if (dow < UCAL_SUNDAY) {
  792. dow = UCAL_SATURDAY;
  793. } else if (dow > UCAL_SATURDAY) {
  794. dow = UCAL_SUNDAY;
  795. }
  796. }
  797. }
  798. // Create a new rule
  799. DateTimeRule *modifiedRule;
  800. if (dtype == DateTimeRule::DOM) {
  801. modifiedRule = new DateTimeRule(month, dom, wallt, DateTimeRule::WALL_TIME);
  802. } else {
  803. modifiedRule = new DateTimeRule(month, dom, dow,
  804. (dtype == DateTimeRule::DOW_GEQ_DOM), wallt, DateTimeRule::WALL_TIME);
  805. }
  806. return modifiedRule;
  807. }
  808. /*
  809. * Minumum implementations of stream writer/reader, writing/reading
  810. * UnicodeString. For now, we do not want to introduce the dependency
  811. * on the ICU I/O stream in this module. But we want to keep the code
  812. * equivalent to the ICU4J implementation, which utilizes java.io.Writer/
  813. * Reader.
  814. */
  815. class VTZWriter {
  816. public:
  817. VTZWriter(UnicodeString& out);
  818. ~VTZWriter();
  819. void write(const UnicodeString& str);
  820. void write(UChar ch);
  821. void write(const UChar* str);
  822. //void write(const UChar* str, int32_t length);
  823. private:
  824. UnicodeString* out;
  825. };
  826. VTZWriter::VTZWriter(UnicodeString& output) {
  827. out = &output;
  828. }
  829. VTZWriter::~VTZWriter() {
  830. }
  831. void
  832. VTZWriter::write(const UnicodeString& str) {
  833. out->append(str);
  834. }
  835. void
  836. VTZWriter::write(UChar ch) {
  837. out->append(ch);
  838. }
  839. void
  840. VTZWriter::write(const UChar* str) {
  841. out->append(str, -1);
  842. }
  843. /*
  844. void
  845. VTZWriter::write(const UChar* str, int32_t length) {
  846. out->append(str, length);
  847. }
  848. */
  849. class VTZReader {
  850. public:
  851. VTZReader(const UnicodeString& input);
  852. ~VTZReader();
  853. UChar read(void);
  854. private:
  855. const UnicodeString* in;
  856. int32_t index;
  857. };
  858. VTZReader::VTZReader(const UnicodeString& input) {
  859. in = &input;
  860. index = 0;
  861. }
  862. VTZReader::~VTZReader() {
  863. }
  864. UChar
  865. VTZReader::read(void) {
  866. UChar ch = 0xFFFF;
  867. if (index < in->length()) {
  868. ch = in->charAt(index);
  869. }
  870. index++;
  871. return ch;
  872. }
  873. UOBJECT_DEFINE_RTTI_IMPLEMENTATION(VTimeZone)
  874. VTimeZone::VTimeZone()
  875. : BasicTimeZone(), tz(NULL), vtzlines(NULL),
  876. lastmod(MAX_MILLIS) {
  877. }
  878. VTimeZone::VTimeZone(const VTimeZone& source)
  879. : BasicTimeZone(source), tz(NULL), vtzlines(NULL),
  880. tzurl(source.tzurl), lastmod(source.lastmod),
  881. olsonzid(source.olsonzid), icutzver(source.icutzver) {
  882. if (source.tz != NULL) {
  883. tz = (BasicTimeZone*)source.tz->clone();
  884. }
  885. if (source.vtzlines != NULL) {
  886. UErrorCode status = U_ZERO_ERROR;
  887. int32_t size = source.vtzlines->size();
  888. vtzlines = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, size, status);
  889. if (U_SUCCESS(status)) {
  890. for (int32_t i = 0; i < size; i++) {
  891. UnicodeString *line = (UnicodeString*)source.vtzlines->elementAt(i);
  892. vtzlines->addElement(line->clone(), status);
  893. if (U_FAILURE(status)) {
  894. break;
  895. }
  896. }
  897. }
  898. if (U_FAILURE(status) && vtzlines != NULL) {
  899. delete vtzlines;
  900. }
  901. }
  902. }
  903. VTimeZone::~VTimeZone() {
  904. if (tz != NULL) {
  905. delete tz;
  906. }
  907. if (vtzlines != NULL) {
  908. delete vtzlines;
  909. }
  910. }
  911. VTimeZone&
  912. VTimeZone::operator=(const VTimeZone& right) {
  913. if (this == &right) {
  914. return *this;
  915. }
  916. if (*this != right) {
  917. BasicTimeZone::operator=(right);
  918. if (tz != NULL) {
  919. delete tz;
  920. tz = NULL;
  921. }
  922. if (right.tz != NULL) {
  923. tz = (BasicTimeZone*)right.tz->clone();
  924. }
  925. if (vtzlines != NULL) {
  926. delete vtzlines;
  927. }
  928. if (right.vtzlines != NULL) {
  929. UErrorCode status = U_ZERO_ERROR;
  930. int32_t size = right.vtzlines->size();
  931. vtzlines = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, size, status);
  932. if (U_SUCCESS(status)) {
  933. for (int32_t i = 0; i < size; i++) {
  934. UnicodeString *line = (UnicodeString*)right.vtzlines->elementAt(i);
  935. vtzlines->addElement(line->clone(), status);
  936. if (U_FAILURE(status)) {
  937. break;
  938. }
  939. }
  940. }
  941. if (U_FAILURE(status) && vtzlines != NULL) {
  942. delete vtzlines;
  943. vtzlines = NULL;
  944. }
  945. }
  946. tzurl = right.tzurl;
  947. lastmod = right.lastmod;
  948. olsonzid = right.olsonzid;
  949. icutzver = right.icutzver;
  950. }
  951. return *this;
  952. }
  953. UBool
  954. VTimeZone::operator==(const TimeZone& that) const {
  955. if (this == &that) {
  956. return TRUE;
  957. }
  958. if (typeid(*this) != typeid(that) || !BasicTimeZone::operator==(that)) {
  959. return FALSE;
  960. }
  961. VTimeZone *vtz = (VTimeZone*)&that;
  962. if (*tz == *(vtz->tz)
  963. && tzurl == vtz->tzurl
  964. && lastmod == vtz->lastmod
  965. /* && olsonzid = that.olsonzid */
  966. /* && icutzver = that.icutzver */) {
  967. return TRUE;
  968. }
  969. return FALSE;
  970. }
  971. UBool
  972. VTimeZone::operator!=(const TimeZone& that) const {
  973. return !operator==(that);
  974. }
  975. VTimeZone*
  976. VTimeZone::createVTimeZoneByID(const UnicodeString& ID) {
  977. VTimeZone *vtz = new VTimeZone();
  978. vtz->tz = (BasicTimeZone*)TimeZone::createTimeZone(ID);
  979. vtz->tz->getID(vtz->olsonzid);
  980. // Set ICU tzdata version
  981. UErrorCode status = U_ZERO_ERROR;
  982. UResourceBundle *bundle = NULL;
  983. const UChar* versionStr = NULL;
  984. int32_t len = 0;
  985. bundle = ures_openDirect(NULL, "zoneinfo64", &status);
  986. versionStr = ures_getStringByKey(bundle, "TZVersion", &len, &status);
  987. if (U_SUCCESS(status)) {
  988. vtz->icutzver.setTo(versionStr, len);
  989. }
  990. ures_close(bundle);
  991. return vtz;
  992. }
  993. VTimeZone*
  994. VTimeZone::createVTimeZoneFromBasicTimeZone(const BasicTimeZone& basic_time_zone, UErrorCode &status) {
  995. if (U_FAILURE(status)) {
  996. return NULL;
  997. }
  998. VTimeZone *vtz = new VTimeZone();
  999. if (vtz == NULL) {
  1000. status = U_MEMORY_ALLOCATION_ERROR;
  1001. return NULL;
  1002. }
  1003. vtz->tz = (BasicTimeZone *)basic_time_zone.clone();
  1004. if (vtz->tz == NULL) {
  1005. status = U_MEMORY_ALLOCATION_ERROR;
  1006. delete vtz;
  1007. return NULL;
  1008. }
  1009. vtz->tz->getID(vtz->olsonzid);
  1010. // Set ICU tzdata version
  1011. UResourceBundle *bundle = NULL;
  1012. const UChar* versionStr = NULL;
  1013. int32_t len = 0;
  1014. bundle = ures_openDirect(NULL, "zoneinfo64", &status);
  1015. versionStr = ures_getStringByKey(bundle, "TZVersion", &len, &status);
  1016. if (U_SUCCESS(status)) {
  1017. vtz->icutzver.setTo(versionStr, len);
  1018. }
  1019. ures_close(bundle);
  1020. return vtz;
  1021. }
  1022. VTimeZone*
  1023. VTimeZone::createVTimeZone(const UnicodeString& vtzdata, UErrorCode& status) {
  1024. if (U_FAILURE(status)) {
  1025. return NULL;
  1026. }
  1027. VTZReader reader(vtzdata);
  1028. VTimeZone *vtz = new VTimeZone();
  1029. vtz->load(reader, status);
  1030. if (U_FAILURE(status)) {
  1031. delete vtz;
  1032. return NULL;
  1033. }
  1034. return vtz;
  1035. }
  1036. UBool
  1037. VTimeZone::getTZURL(UnicodeString& url) const {
  1038. if (tzurl.length() > 0) {
  1039. url = tzurl;
  1040. return TRUE;
  1041. }
  1042. return FALSE;
  1043. }
  1044. void
  1045. VTimeZone::setTZURL(const UnicodeString& url) {
  1046. tzurl = url;
  1047. }
  1048. UBool
  1049. VTimeZone::getLastModified(UDate& lastModified) const {
  1050. if (lastmod != MAX_MILLIS) {
  1051. lastModified = lastmod;
  1052. return TRUE;
  1053. }
  1054. return FALSE;
  1055. }
  1056. void
  1057. VTimeZone::setLastModified(UDate lastModified) {
  1058. lastmod = lastModified;
  1059. }
  1060. void
  1061. VTimeZone::write(UnicodeString& result, UErrorCode& status) const {
  1062. result.remove();
  1063. VTZWriter writer(result);
  1064. write(writer, status);
  1065. }
  1066. void
  1067. VTimeZone::write(UDate start, UnicodeString& result, UErrorCode& status) const {
  1068. result.remove();
  1069. VTZWriter writer(result);
  1070. write(start, writer, status);
  1071. }
  1072. void
  1073. VTimeZone::writeSimple(UDate time, UnicodeString& result, UErrorCode& status) const {
  1074. result.remove();
  1075. VTZWriter writer(result);
  1076. writeSimple(time, writer, status);
  1077. }
  1078. TimeZone*
  1079. VTimeZone::clone(void) const {
  1080. return new VTimeZone(*this);
  1081. }
  1082. int32_t
  1083. VTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
  1084. uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const {
  1085. return tz->getOffset(era, year, month, day, dayOfWeek, millis, status);
  1086. }
  1087. int32_t
  1088. VTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
  1089. uint8_t dayOfWeek, int32_t millis,
  1090. int32_t monthLength, UErrorCode& status) const {
  1091. return tz->getOffset(era, year, month, day, dayOfWeek, millis, monthLength, status);
  1092. }
  1093. void
  1094. VTimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
  1095. int32_t& dstOffset, UErrorCode& status) const {
  1096. return tz->getOffset(date, local, rawOffset, dstOffset, status);
  1097. }
  1098. void
  1099. VTimeZone::setRawOffset(int32_t offsetMillis) {
  1100. tz->setRawOffset(offsetMillis);
  1101. }
  1102. int32_t
  1103. VTimeZone::getRawOffset(void) const {
  1104. return tz->getRawOffset();
  1105. }
  1106. UBool
  1107. VTimeZone::useDaylightTime(void) const {
  1108. return tz->useDaylightTime();
  1109. }
  1110. UBool
  1111. VTimeZone::inDaylightTime(UDate date, UErrorCode& status) const {
  1112. return tz->inDaylightTime(date, status);
  1113. }
  1114. UBool
  1115. VTimeZone::hasSameRules(const TimeZone& other) const {
  1116. return tz->hasSameRules(other);
  1117. }
  1118. UBool
  1119. VTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
  1120. return tz->getNextTransition(base, inclusive, result);
  1121. }
  1122. UBool
  1123. VTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
  1124. return tz->getPreviousTransition(base, inclusive, result);
  1125. }
  1126. int32_t
  1127. VTimeZone::countTransitionRules(UErrorCode& status) const {
  1128. return tz->countTransitionRules(status);
  1129. }
  1130. void
  1131. VTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial,
  1132. const TimeZoneRule* trsrules[], int32_t& trscount,
  1133. UErrorCode& status) const {
  1134. tz->getTimeZoneRules(initial, trsrules, trscount, status);
  1135. }
  1136. void
  1137. VTimeZone::load(VTZReader& reader, UErrorCode& status) {
  1138. vtzlines = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, DEFAULT_VTIMEZONE_LINES, status);
  1139. if (U_FAILURE(status)) {
  1140. return;
  1141. }
  1142. UBool eol = FALSE;
  1143. UBool start = FALSE;
  1144. UBool success = FALSE;
  1145. UnicodeString line;
  1146. while (TRUE) {
  1147. UChar ch = reader.read();
  1148. if (ch == 0xFFFF) {
  1149. // end of file
  1150. if (start && line.startsWith(ICAL_END_VTIMEZONE, -1)) {
  1151. vtzlines->addElement(new UnicodeString(line), status);
  1152. if (U_FAILURE(status)) {
  1153. goto cleanupVtzlines;
  1154. }
  1155. success = TRUE;
  1156. }
  1157. break;
  1158. }
  1159. if (ch == 0x000D) {
  1160. // CR, must be followed by LF according to the definition in RFC2445
  1161. continue;
  1162. }
  1163. if (eol) {
  1164. if (ch != 0x0009 && ch != 0x0020) {
  1165. // NOT followed by TAB/SP -> new line
  1166. if (start) {
  1167. if (line.length() > 0) {
  1168. vtzlines->addElement(new UnicodeString(line), status);
  1169. if (U_FAILURE(status)) {
  1170. goto cleanupVtzlines;
  1171. }
  1172. }
  1173. }
  1174. line.remove();
  1175. if (ch != 0x000A) {
  1176. line.append(ch);
  1177. }
  1178. }
  1179. eol = FALSE;
  1180. } else {
  1181. if (ch == 0x000A) {
  1182. // LF
  1183. eol = TRUE;
  1184. if (start) {
  1185. if (line.startsWith(ICAL_END_VTIMEZONE, -1)) {
  1186. vtzlines->addElement(new UnicodeString(line), status);
  1187. if (U_FAILURE(status)) {
  1188. goto cleanupVtzlines;
  1189. }
  1190. success = TRUE;
  1191. break;
  1192. }
  1193. } else {
  1194. if (line.startsWith(ICAL_BEGIN_VTIMEZONE, -1)) {
  1195. vtzlines->addElement(new UnicodeString(line), status);
  1196. if (U_FAILURE(status)) {
  1197. goto cleanupVtzlines;
  1198. }
  1199. line.remove();
  1200. start = TRUE;
  1201. eol = FALSE;
  1202. }
  1203. }
  1204. } else {
  1205. line.append(ch);
  1206. }
  1207. }
  1208. }
  1209. if (!success) {
  1210. if (U_SUCCESS(status)) {
  1211. status = U_INVALID_STATE_ERROR;
  1212. }
  1213. goto cleanupVtzlines;
  1214. }
  1215. parse(status);
  1216. return;
  1217. cleanupVtzlines:
  1218. delete vtzlines;
  1219. vtzlines = NULL;
  1220. }
  1221. // parser state
  1222. #define INI 0 // Initial state
  1223. #define VTZ 1 // In VTIMEZONE
  1224. #define TZI 2 // In STANDARD or DAYLIGHT
  1225. #define DEF_DSTSAVINGS (60*60*1000)
  1226. #define DEF_TZSTARTTIME (0.0)
  1227. void
  1228. VTimeZone::parse(UErrorCode& status) {
  1229. if (U_FAILURE(status)) {
  1230. return;
  1231. }
  1232. if (vtzlines == NULL || vtzlines->size() == 0) {
  1233. status = U_INVALID_STATE_ERROR;
  1234. return;
  1235. }
  1236. InitialTimeZoneRule *initialRule = NULL;
  1237. RuleBasedTimeZone *rbtz = NULL;
  1238. // timezone ID
  1239. UnicodeString tzid;
  1240. int32_t state = INI;
  1241. int32_t n = 0;
  1242. UBool dst = FALSE; // current zone type
  1243. UnicodeString from; // current zone from offset
  1244. UnicodeString to; // current zone offset
  1245. UnicodeString zonename; // current zone name
  1246. UnicodeString dtstart; // current zone starts
  1247. UBool isRRULE = FALSE; // true if the rule is described by RRULE
  1248. int32_t initialRawOffset = 0; // initial offset
  1249. int32_t initialDSTSavings = 0; // initial offset
  1250. UDate firstStart = MAX_MILLIS; // the earliest rule start time
  1251. UnicodeString name; // RFC2445 prop name
  1252. UnicodeString value; // RFC2445 prop value
  1253. UVector *dates = NULL; // list of RDATE or RRULE strings
  1254. UVector *rules = NULL; // list of TimeZoneRule instances
  1255. int32_t finalRuleIdx = -1;
  1256. int32_t finalRuleCount = 0;
  1257. rules = new UVector(status);
  1258. if (U_FAILURE(status)) {
  1259. goto cleanupParse;
  1260. }
  1261. // Set the deleter to remove TimeZoneRule vectors to avoid memory leaks due to unowned TimeZoneRules.
  1262. rules->setDeleter(deleteTimeZoneRule);
  1263. dates = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status);
  1264. if (U_FAILURE(status)) {
  1265. goto cleanupParse;
  1266. }
  1267. if (rules == NULL || dates == NULL) {
  1268. status = U_MEMORY_ALLOCATION_ERROR;
  1269. goto cleanupParse;
  1270. }
  1271. for (n = 0; n < vtzlines->size(); n++) {
  1272. UnicodeString *line = (UnicodeString*)vtzlines->elementAt(n);
  1273. int32_t valueSep = line->indexOf(COLON);
  1274. if (valueSep < 0) {
  1275. continue;
  1276. }
  1277. name.setTo(*line, 0, valueSep);
  1278. value.setTo(*line, valueSep + 1);
  1279. switch (state) {
  1280. case INI:
  1281. if (name.compare(ICAL_BEGIN, -1) == 0
  1282. && value.compare(ICAL_VTIMEZONE, -1) == 0) {
  1283. state = VTZ;
  1284. }
  1285. break;
  1286. case VTZ:
  1287. if (name.compare(ICAL_TZID, -1) == 0) {
  1288. tzid = value;
  1289. } else if (name.compare(ICAL_TZURL, -1) == 0) {
  1290. tzurl = value;
  1291. } else if (name.compare(ICAL_LASTMOD, -1) == 0) {
  1292. // Always in 'Z' format, so the offset argument for the parse method
  1293. // can be any value.
  1294. lastmod = parseDateTimeString(value, 0, status);
  1295. if (U_FAILURE(status)) {
  1296. goto cleanupParse;
  1297. }
  1298. } else if (name.compare(ICAL_BEGIN, -1) == 0) {
  1299. UBool isDST = (value.compare(ICAL_DAYLIGHT, -1) == 0);
  1300. if (value.compare(ICAL_STANDARD, -1) == 0 || isDST) {
  1301. // tzid must be ready at this point
  1302. if (tzid.length() == 0) {
  1303. goto cleanupParse;
  1304. }
  1305. // initialize current zone properties
  1306. if (dates->size() != 0) {
  1307. dates->removeAllElements();
  1308. }
  1309. isRRULE = FALSE;
  1310. from.remove();
  1311. to.remove();
  1312. zonename.remove();
  1313. dst = isDST;
  1314. state = TZI;
  1315. } else {
  1316. // BEGIN property other than STANDARD/DAYLIGHT
  1317. // must not be there.
  1318. goto cleanupParse;
  1319. }
  1320. } else if (name.compare(ICAL_END, -1) == 0) {
  1321. break;
  1322. }
  1323. break;
  1324. case TZI:
  1325. if (name.compare(ICAL_DTSTART, -1) == 0) {
  1326. dtstart = value;
  1327. } else if (name.compare(ICAL_TZNAME, -1) == 0) {
  1328. zonename = value;
  1329. } else if (name.compare(ICAL_TZOFFSETFROM, -1) == 0) {
  1330. from = value;
  1331. } else if (name.compare(ICAL_TZOFFSETTO, -1) == 0) {
  1332. to = value;
  1333. } else if (name.compare(ICAL_RDATE, -1) == 0) {
  1334. // RDATE mixed with RRULE is not supported
  1335. if (isRRULE) {
  1336. goto cleanupParse;
  1337. }
  1338. // RDATE value may contain multiple date delimited
  1339. // by comma
  1340. UBool nextDate = TRUE;
  1341. int32_t dstart = 0;
  1342. UnicodeString *dstr;
  1343. while (nextDate) {
  1344. int32_t dend = value.indexOf(COMMA, dstart);
  1345. if (dend == -1) {
  1346. dstr = new UnicodeString(value, dstart);
  1347. nextDate = FALSE;
  1348. } else {
  1349. dstr = new UnicodeString(value, dstart, dend - dstart);
  1350. }
  1351. dates->addElement(dstr, status);
  1352. if (U_FAILURE(status)) {
  1353. goto cleanupParse;
  1354. }
  1355. dstart = dend + 1;
  1356. }
  1357. } else if (name.compare(ICAL_RRULE, -1) == 0) {
  1358. // RRULE mixed with RDATE is not supported
  1359. if (!isRRULE && dates->size() != 0) {
  1360. goto cleanupParse;
  1361. }
  1362. isRRULE = true;
  1363. dates->addElement(new UnicodeString(value), status);
  1364. if (U_FAILURE(status)) {
  1365. goto cleanupParse;
  1366. }
  1367. } else if (name.compare(ICAL_END, -1) == 0) {
  1368. // Mandatory properties
  1369. if (dtstart.length() == 0 || from.length() == 0 || to.length() == 0) {
  1370. goto cleanupParse;
  1371. }
  1372. // if zonename is not available, create one from tzid
  1373. if (zonename.length() == 0) {
  1374. getDefaultTZName(tzid, dst, zonename);
  1375. }
  1376. // create a time zone rule
  1377. TimeZoneRule *rule = NULL;
  1378. int32_t fromOffset = 0;
  1379. int32_t toOffset = 0;
  1380. int32_t rawOffset = 0;
  1381. int32_t dstSavings = 0;
  1382. UDate start = 0;
  1383. // Parse TZOFFSETFROM/TZOFFSETTO
  1384. fromOffset = offsetStrToMillis(from, status);
  1385. toOffset = offsetStrToMillis(to, status);
  1386. if (U_FAILURE(status)) {
  1387. goto cleanupParse;
  1388. }
  1389. if (dst) {
  1390. // If daylight, use the previous offset as rawoffset if positive
  1391. if (toOffset - fromOffset > 0) {
  1392. rawOffset = fromOffset;
  1393. dstSavings = toOffset - fromOffset;
  1394. } else {
  1395. // This is rare case.. just use 1 hour DST savings
  1396. rawOffset = toOffset - DEF_DSTSAVINGS;
  1397. dstSavings = DEF_DSTSAVINGS;
  1398. }
  1399. } else {
  1400. rawOffset = toOffset;
  1401. dstSavings = 0;
  1402. }
  1403. // start time
  1404. start = parseDateTimeString(dtstart, fromOffset, status);
  1405. if (U_FAILURE(status)) {
  1406. goto cleanupParse;
  1407. }
  1408. // Create the rule
  1409. UDate actualStart = MAX_MILLIS;
  1410. if (isRRULE) {
  1411. rule = createRuleByRRULE(zonename, rawOffset, dstSavings, start, dates, fromOffset, status);
  1412. } else {
  1413. rule = createRuleByRDATE(zonename, rawOffset, dstSavings, start, dates, fromOffset, status);
  1414. }
  1415. if (U_FAILURE(status) || rule == NULL) {
  1416. goto cleanupParse;
  1417. } else {
  1418. UBool startAvail = rule->getFirstStart(fromOffset, 0, actualStart);
  1419. if (startAvail && actualStart < firstStart) {
  1420. // save from offset information for the earliest rule
  1421. firstStart = actualStart;
  1422. // If this is STD, assume the time before this transtion
  1423. // is DST when the difference is 1 hour. This might not be
  1424. // accurate, but VTIMEZONE data does not have such info.
  1425. if (dstSavings > 0) {
  1426. initialRawOffset = fromOffset;
  1427. initialDSTSavings = 0;
  1428. } else {
  1429. if (fromOffset - toOffset == DEF_DSTSAVINGS) {
  1430. initialRawOffset = fromOffset - DEF_DSTSAVINGS;
  1431. initialDSTSavings = DEF_DSTSAVINGS;
  1432. } else {
  1433. initialRawOffset = fromOffset;
  1434. initialDSTSavings = 0;
  1435. }
  1436. }
  1437. }
  1438. }
  1439. rules->addElement(rule, status);
  1440. if (U_FAILURE(status)) {
  1441. goto cleanupParse;
  1442. }
  1443. state = VTZ;
  1444. }
  1445. break;
  1446. }
  1447. }
  1448. // Must have at least one rule
  1449. if (rules->size() == 0) {
  1450. goto cleanupParse;
  1451. }
  1452. // Create a initial rule
  1453. getDefaultTZName(tzid, FALSE, zonename);
  1454. initialRule = new InitialTimeZoneRule(zonename,
  1455. initialRawOffset, initialDSTSavings);
  1456. if (initialRule == NULL) {
  1457. status = U_MEMORY_ALLOCATION_ERROR;
  1458. goto cleanupParse;
  1459. }
  1460. // Finally, create the RuleBasedTimeZone
  1461. rbtz = new RuleBasedTimeZone(tzid, initialRule);
  1462. if (rbtz == NULL) {
  1463. status = U_MEMORY_ALLOCATION_ERROR;
  1464. goto cleanupParse;
  1465. }
  1466. initialRule = NULL; // already adopted by RBTZ, no need to delete
  1467. for (n = 0; n < rules->size(); n++) {
  1468. TimeZoneRule *r = (TimeZoneRule*)rules->elementAt(n);
  1469. AnnualTimeZoneRule *atzrule = dynamic_cast<AnnualTimeZoneRule *>(r);
  1470. if (atzrule != NULL) {
  1471. if (atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) {
  1472. finalRuleCount++;
  1473. finalRuleIdx = n;
  1474. }
  1475. }
  1476. }
  1477. if (finalRuleCount > 2) {
  1478. // Too many final rules
  1479. status = U_ILLEGAL_ARGUMENT_ERROR;
  1480. goto cleanupParse;
  1481. }
  1482. if (finalRuleCount == 1) {
  1483. if (rules->size() == 1) {
  1484. // Only one final rule, only governs the initial rule,
  1485. // which is already initialized, thus, we do not need to
  1486. // add this transition rule
  1487. rules->removeAllElements();
  1488. } else {
  1489. // Normalize the final rule
  1490. AnnualTimeZoneRule *finalRule = (AnnualTimeZoneRule*)rules->elementAt(finalRuleIdx);
  1491. int32_t tmpRaw = finalRule->getRawOffset();
  1492. int32_t tmpDST = finalRule->getDSTSavings();
  1493. // Find the last non-final rule
  1494. UDate finalStart, start;
  1495. finalRule->getFirstStart(initialRawOffset, initialDSTSavings, finalStart);
  1496. start = finalStart;
  1497. for (n = 0; n < rules->size(); n++) {
  1498. if (finalRuleIdx == n) {
  1499. continue;
  1500. }
  1501. TimeZoneRule *r = (TimeZoneRule*)rules->elementAt(n);
  1502. UDate lastStart;
  1503. r->getFinalStart(tmpRaw, tmpDST, lastStart);
  1504. if (lastStart > start) {
  1505. finalRule->getNextStart(lastStart,
  1506. r->getRawOffset(),
  1507. r->getDSTSavings(),
  1508. FALSE,
  1509. start);
  1510. }
  1511. }
  1512. TimeZoneRule *newRule;
  1513. UnicodeString tznam;
  1514. if (start == finalStart) {
  1515. // Transform this into a single transition
  1516. newRule = new TimeArrayTimeZoneRule(
  1517. finalRule->getName(tznam),
  1518. finalRule->getRawOffset(),
  1519. finalRule->getDSTSavings(),
  1520. &finalStart,
  1521. 1,
  1522. DateTimeRule::UTC_TIME);
  1523. } else {
  1524. // Update the end year
  1525. int32_t y, m, d, dow, doy, mid;
  1526. Grego::timeToFields(start, y, m, d, dow, doy, mid);
  1527. newRule = new AnnualTimeZoneRule(
  1528. finalRule->getName(tznam),
  1529. finalRule->getRawOffset(),
  1530. finalRule->getDSTSavings(),
  1531. *(finalRule->getRule()),
  1532. finalRule->getStartYear(),
  1533. y);
  1534. }
  1535. if (newRule == NULL) {
  1536. status = U_MEMORY_ALLOCATION_ERROR;
  1537. goto cleanupParse;
  1538. }
  1539. rules->removeElementAt(finalRuleIdx);
  1540. rules->addElement(newRule, status);
  1541. if (U_FAILURE(status)) {
  1542. delete newRule;
  1543. goto cleanupParse;
  1544. }
  1545. }
  1546. }
  1547. while (!rules->isEmpty()) {
  1548. TimeZoneRule *tzr = (TimeZoneRule*)rules->orphanElementAt(0);
  1549. rbtz->addTransitionRule(tzr, status);
  1550. if (U_FAILURE(status)) {
  1551. goto cleanupParse;
  1552. }
  1553. }
  1554. rbtz->complete(status);
  1555. if (U_FAILURE(status)) {
  1556. goto cleanupParse;
  1557. }
  1558. delete rules;
  1559. delete dates;
  1560. tz = rbtz;
  1561. setID(tzid);
  1562. return;
  1563. cleanupParse:
  1564. if (rules != NULL) {
  1565. while (!rules->isEmpty()) {
  1566. TimeZoneRule *r = (TimeZoneRule*)rules->orphanElementAt(0);
  1567. delete r;
  1568. }
  1569. delete rules;
  1570. }
  1571. if (dates != NULL) {
  1572. delete dates;
  1573. }
  1574. if (initialRule != NULL) {
  1575. delete initialRule;
  1576. }
  1577. if (rbtz != NULL) {
  1578. delete rbtz;
  1579. }
  1580. return;
  1581. }
  1582. void
  1583. VTimeZone::write(VTZWriter& writer, UErrorCode& status) const {
  1584. if (vtzlines != NULL) {
  1585. for (int32_t i = 0; i < vtzlines->size(); i++) {
  1586. UnicodeString *line = (UnicodeString*)vtzlines->elementAt(i);
  1587. if (line->startsWith(ICAL_TZURL, -1)
  1588. && line->charAt(u_strlen(ICAL_TZURL)) == COLON) {
  1589. writer.write(ICAL_TZURL);
  1590. writer.write(COLON);
  1591. writer.write(tzurl);
  1592. writer.write(ICAL_NEWLINE);
  1593. } else if (line->startsWith(ICAL_LASTMOD, -1)
  1594. && line->charAt(u_strlen(ICAL_LASTMOD)) == COLON) {
  1595. UnicodeString utcString;
  1596. writer.write(ICAL_LASTMOD);
  1597. writer.write(COLON);
  1598. writer.write(getUTCDateTimeString(lastmod, utcString));
  1599. writer.write(ICAL_NEWLINE);
  1600. } else {
  1601. writer.write(*line);
  1602. writer.write(ICAL_NEWLINE);
  1603. }
  1604. }
  1605. } else {
  1606. UVector *customProps = NULL;
  1607. if (olsonzid.length() > 0 && icutzver.length() > 0) {
  1608. customProps = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status);
  1609. if (U_FAILURE(status)) {
  1610. return;
  1611. }
  1612. UnicodeString *icutzprop = new UnicodeString(ICU_TZINFO_PROP);
  1613. icutzprop->append(olsonzid);
  1614. icutzprop->append((UChar)0x005B/*'['*/);
  1615. icutzprop->append(icutzver);
  1616. icutzprop->append((UChar)0x005D/*']'*/);
  1617. customProps->addElement(icutzprop, status);
  1618. if (U_FAILURE(status)) {
  1619. delete icutzprop;
  1620. delete customProps;
  1621. return;
  1622. }
  1623. }
  1624. writeZone(writer, *tz, customProps, status);
  1625. delete customProps;
  1626. }
  1627. }
  1628. void
  1629. VTimeZone::write(UDate start, VTZWriter& writer, UErrorCode& status) const {
  1630. if (U_FAILURE(status)) {
  1631. return;
  1632. }
  1633. InitialTimeZoneRule *initial = NULL;
  1634. UVector *transitionRules = NULL;
  1635. UVector customProps(uprv_deleteUObject, uhash_compareUnicodeString, status);
  1636. UnicodeString tzid;
  1637. // Extract rules applicable to dates after the start time
  1638. getTimeZoneRulesAfter(start, initial, transitionRules, status);
  1639. if (U_FAILURE(status)) {
  1640. return;
  1641. }
  1642. // Create a RuleBasedTimeZone with the subset rule
  1643. getID(tzid);
  1644. RuleBasedTimeZone rbtz(tzid, initial);
  1645. if (transitionRules != NULL) {
  1646. while (!transitionRules->isEmpty()) {
  1647. TimeZoneRule *tr = (TimeZoneRule*)transitionRules->orphanElementAt(0);
  1648. rbtz.addTransitionRule(tr, status);
  1649. if (U_FAILURE(status)) {
  1650. goto cleanupWritePartial;
  1651. }
  1652. }
  1653. delete transitionRules;
  1654. transitionRules = NULL;
  1655. }
  1656. rbtz.complete(status);
  1657. if (U_FAILURE(status)) {
  1658. goto cleanupWritePartial;
  1659. }
  1660. if (olsonzid.length() > 0 && icutzver.length() > 0) {
  1661. UnicodeString *icutzprop = new UnicodeString(ICU_TZINFO_PROP);
  1662. icutzprop->append(olsonzid);
  1663. icutzprop->append((UChar)0x005B/*'['*/);
  1664. icutzprop->append(icutzver);
  1665. icutzprop->append(ICU_TZINFO_PARTIAL, -1);
  1666. appendMillis(start, *icutzprop);
  1667. icutzprop->append((UChar)0x005D/*']'*/);
  1668. customProps.addElement(icutzprop, status);
  1669. if (U_FAILURE(status)) {
  1670. delete icutzprop;
  1671. goto cleanupWritePartial;
  1672. }
  1673. }
  1674. writeZone(writer, rbtz, &customProps, status);
  1675. return;
  1676. cleanupWritePartial:
  1677. if (initial != NULL) {
  1678. delete initial;
  1679. }
  1680. if (transitionRules != NULL) {
  1681. while (!transitionRules->isEmpty()) {
  1682. TimeZoneRule *tr = (TimeZoneRule*)transitionRules->orphanElementAt(0);
  1683. delete tr;
  1684. }
  1685. delete transitionRules;
  1686. }
  1687. }
  1688. void
  1689. VTimeZone::writeSimple(UDate time, VTZWriter& writer, UErrorCode& status) const {
  1690. if (U_FAILURE(status)) {
  1691. return;
  1692. }
  1693. UVector customProps(uprv_deleteUObject, uhash_compareUnicodeString, status);
  1694. UnicodeString tzid;
  1695. // Extract simple rules
  1696. InitialTimeZoneRule *initial = NULL;
  1697. AnnualTimeZoneRule *std = NULL, *dst = NULL;
  1698. getSimpleRulesNear(time, initial, std, dst, status);
  1699. if (U_SUCCESS(status)) {
  1700. // Create a RuleBasedTimeZone with the subset rule
  1701. getID(tzid);
  1702. RuleBasedTimeZone rbtz(tzid, initial);
  1703. if (std != NULL && dst != NULL) {
  1704. rbtz.addTransitionRule(std, status);
  1705. rbtz.addTransitionRule(dst, status);
  1706. }
  1707. if (U_FAILURE(status)) {
  1708. goto cleanupWriteSimple;
  1709. }
  1710. if (olsonzid.length() > 0 && icutzver.length() > 0) {
  1711. UnicodeString *icutzprop = new UnicodeString(ICU_TZINFO_PROP);
  1712. icutzprop->append(olsonzid);
  1713. icutzprop->append((UChar)0x005B/*'['*/);
  1714. icutzprop->append(icutzver);
  1715. icutzprop->append(ICU_TZINFO_SIMPLE, -1);
  1716. appendMillis(time, *icutzprop);
  1717. icutzprop->append((UChar)0x005D/*']'*/);
  1718. customProps.addElement(icutzprop, status);
  1719. if (U_FAILURE(status)) {
  1720. delete icutzprop;
  1721. goto cleanupWriteSimple;
  1722. }
  1723. }
  1724. writeZone(writer, rbtz, &customProps, status);
  1725. }
  1726. return;
  1727. cleanupWriteSimple:
  1728. if (initial != NULL) {
  1729. delete initial;
  1730. }
  1731. if (std != NULL) {
  1732. delete std;
  1733. }
  1734. if (dst != NULL) {
  1735. delete dst;
  1736. }
  1737. }
  1738. void
  1739. VTimeZone::writeZone(VTZWriter& w, BasicTimeZone& basictz,
  1740. UVector* customProps, UErrorCode& status) const {
  1741. if (U_FAILURE(status)) {
  1742. return;
  1743. }
  1744. writeHeaders(w, status);
  1745. if (U_FAILURE(status)) {
  1746. return;
  1747. }
  1748. if (customProps != NULL) {
  1749. for (int32_t i = 0; i < customProps->size(); i++) {
  1750. UnicodeString *custprop = (UnicodeString*)customProps->elementAt(i);
  1751. w.write(*custprop);
  1752. w.write(ICAL_NEWLINE);
  1753. }
  1754. }
  1755. UDate t = MIN_MILLIS;
  1756. UnicodeString dstName;
  1757. int32_t dstFromOffset = 0;
  1758. int32_t dstFromDSTSavings = 0;
  1759. int32_t dstToOffset = 0;
  1760. int32_t dstStartYear = 0;
  1761. int32_t dstMonth = 0;
  1762. int32_t dstDayOfWeek = 0;
  1763. int32_t dstWeekInMonth = 0;
  1764. int32_t dstMillisInDay = 0;
  1765. UDate dstStartTime = 0.0;
  1766. UDate dstUntilTime = 0.0;
  1767. int32_t dstCount = 0;
  1768. AnnualTimeZoneRule *finalDstRule = NULL;
  1769. UnicodeString stdName;
  1770. int32_t stdFromOffset = 0;
  1771. int32_t stdFromDSTSavings = 0;
  1772. int32_t stdToOffset = 0;
  1773. int32_t stdStartYear = 0;
  1774. int32_t stdMonth = 0;
  1775. int32_t stdDayOfWeek = 0;
  1776. int32_t stdWeekInMonth = 0;
  1777. int32_t stdMillisInDay = 0;
  1778. UDate stdStartTime = 0.0;
  1779. UDate stdUntilTime = 0.0;
  1780. int32_t stdCount = 0;
  1781. AnnualTimeZoneRule *finalStdRule = NULL;
  1782. int32_t year, month, dom, dow, doy, mid;
  1783. UBool hasTransitions = FALSE;
  1784. TimeZoneTransition tzt;
  1785. UBool tztAvail;
  1786. UnicodeString name;
  1787. UBool isDst;
  1788. // Going through all transitions
  1789. while (TRUE) {
  1790. tztAvail = basictz.getNextTransition(t, FALSE, tzt);
  1791. if (!tztAvail) {
  1792. break;
  1793. }
  1794. hasTransitions = TRUE;
  1795. t = tzt.getTime();
  1796. tzt.getTo()->getName(name);
  1797. isDst = (tzt.getTo()->getDSTSavings() != 0);
  1798. int32_t fromOffset = tzt.getFrom()->getRawOffset() + tzt.getFrom()->getDSTSavings();
  1799. int32_t fromDSTSavings = tzt.getFrom()->getDSTSavings();
  1800. int32_t toOffset = tzt.getTo()->getRawOffset() + tzt.getTo()->getDSTSavings();
  1801. Grego::timeToFields(tzt.getTime() + fromOffset, year, month, dom, dow, doy, mid);
  1802. int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
  1803. UBool sameRule = FALSE;
  1804. const AnnualTimeZoneRule *atzrule;
  1805. if (isDst) {
  1806. if (finalDstRule == NULL
  1807. && (atzrule = dynamic_cast<const AnnualTimeZoneRule *>(tzt.getTo())) != NULL
  1808. && atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR
  1809. ) {
  1810. finalDstRule = (AnnualTimeZoneRule*)tzt.getTo()->clone();
  1811. }
  1812. if (dstCount > 0) {
  1813. if (year == dstStartYear + dstCount
  1814. && name.compare(dstName) == 0
  1815. && dstFromOffset == fromOffset
  1816. && dstToOffset == toOffset
  1817. && dstMonth == month
  1818. && dstDayOfWeek == dow
  1819. && dstWeekInMonth == weekInMonth
  1820. && dstMillisInDay == mid) {
  1821. // Update until time
  1822. dstUntilTime = t;
  1823. dstCount++;
  1824. sameRule = TRUE;
  1825. }
  1826. if (!sameRule) {
  1827. if (dstCount == 1) {
  1828. writeZonePropsByTime(w, TRUE, dstName, dstFromOffset, dstToOffset, dstStartTime,
  1829. TRUE, status);
  1830. } else {
  1831. writeZonePropsByDOW(w, TRUE, dstName, dstFromOffset, dstToOffset,
  1832. dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, dstUntilTime, status);
  1833. }
  1834. if (U_FAILURE(status)) {
  1835. goto cleanupWriteZone;
  1836. }
  1837. }
  1838. }
  1839. if (!sameRule) {
  1840. // Reset this DST information
  1841. dstName = name;
  1842. dstFromOffset = fromOffset;
  1843. dstFromDSTSavings = fromDSTSavings;
  1844. dstToOffset = toOffset;
  1845. dstStartYear = year;
  1846. dstMonth = month;
  1847. dstDayOfWeek = dow;
  1848. dstWeekInMonth = weekInMonth;
  1849. dstMillisInDay = mid;
  1850. dstStartTime = dstUntilTime = t;
  1851. dstCount = 1;
  1852. }
  1853. if (finalStdRule != NULL && finalDstRule != NULL) {
  1854. break;
  1855. }
  1856. } else {
  1857. if (finalStdRule == NULL
  1858. && (atzrule = dynamic_cast<const AnnualTimeZoneRule *>(tzt.getTo())) != NULL
  1859. && atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR
  1860. ) {
  1861. finalStdRule = (AnnualTimeZoneRule*)tzt.getTo()->clone();
  1862. }
  1863. if (stdCount > 0) {
  1864. if (year == stdStartYear + stdCount
  1865. && name.compare(stdName) == 0
  1866. && stdFromOffset == fromOffset
  1867. && stdToOffset == toOffset
  1868. && stdMonth == month
  1869. && stdDayOfWeek == dow
  1870. && stdWeekInMonth == weekInMonth
  1871. && stdMillisInDay == mid) {
  1872. // Update until time
  1873. stdUntilTime = t;
  1874. stdCount++;
  1875. sameRule = TRUE;
  1876. }
  1877. if (!sameRule) {
  1878. if (stdCount == 1) {
  1879. writeZonePropsByTime(w, FALSE, stdName, stdFromOffset, stdToOffset, stdStartTime,
  1880. TRUE, status);
  1881. } else {
  1882. writeZonePropsByDOW(w, FALSE, stdName, stdFromOffset, stdToOffset,
  1883. stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, stdUntilTime, status);
  1884. }
  1885. if (U_FAILURE(status)) {
  1886. goto cleanupWriteZone;
  1887. }
  1888. }
  1889. }
  1890. if (!sameRule) {
  1891. // Reset this STD information
  1892. stdName = name;
  1893. stdFromOffset = fromOffset;
  1894. stdFromDSTSavings = fromDSTSavings;
  1895. stdToOffset = toOffset;
  1896. stdStartYear = year;
  1897. stdMonth = month;
  1898. stdDayOfWeek = dow;
  1899. stdWeekInMonth = weekInMonth;
  1900. stdMillisInDay = mid;
  1901. stdStartTime = stdUntilTime = t;
  1902. stdCount = 1;
  1903. }
  1904. if (finalStdRule != NULL && finalDstRule != NULL) {
  1905. break;
  1906. }
  1907. }
  1908. }
  1909. if (!hasTransitions) {
  1910. // No transition - put a single non transition RDATE
  1911. int32_t raw, dst, offset;
  1912. basictz.getOffset(0.0/*any time*/, FALSE, raw, dst, status);
  1913. if (U_FAILURE(status)) {
  1914. goto cleanupWriteZone;
  1915. }
  1916. offset = raw + dst;
  1917. isDst = (dst != 0);
  1918. UnicodeString tzid;
  1919. basictz.getID(tzid);
  1920. getDefaultTZName(tzid, isDst, name);
  1921. writeZonePropsByTime(w, isDst, name,
  1922. offset, offset, DEF_TZSTARTTIME - offset, FALSE, status);
  1923. if (U_FAILURE(status)) {
  1924. goto cleanupWriteZone;
  1925. }
  1926. } else {
  1927. if (dstCount > 0) {
  1928. if (finalDstRule == NULL) {
  1929. if (dstCount == 1) {
  1930. writeZonePropsByTime(w, TRUE, dstName, dstFromOffset, dstToOffset, dstStartTime,
  1931. TRUE, status);
  1932. } else {
  1933. writeZonePropsByDOW(w, TRUE, dstName, dstFromOffset, dstToOffset,
  1934. dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, dstUntilTime, status);
  1935. }
  1936. if (U_FAILURE(status)) {
  1937. goto cleanupWriteZone;
  1938. }
  1939. } else {
  1940. if (dstCount == 1) {
  1941. writeFinalRule(w, TRUE, finalDstRule,
  1942. dstFromOffset - dstFromDSTSavings, dstFromDSTSavings, dstStartTime, status);
  1943. } else {
  1944. // Use a single rule if possible
  1945. if (isEquivalentDateRule(dstMonth, dstWeekInMonth, dstDayOfWeek, finalDstRule->getRule())) {
  1946. writeZonePropsByDOW(w, TRUE, dstName, dstFromOffset, dstToOffset,
  1947. dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, MAX_MILLIS, status);
  1948. } else {
  1949. // Not equivalent rule - write out two different rules
  1950. writeZonePropsByDOW(w, TRUE, dstName, dstFromOffset, dstToOffset,
  1951. dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, dstUntilTime, status);
  1952. if (U_FAILURE(status)) {
  1953. goto cleanupWriteZone;
  1954. }
  1955. UDate nextStart;
  1956. UBool nextStartAvail = finalDstRule->getNextStart(dstUntilTime, dstFromOffset - dstFromDSTSavings, dstFromDSTSavings, false, nextStart);
  1957. U_ASSERT(nextStartAvail);
  1958. if (nextStartAvail) {
  1959. writeFinalRule(w, TRUE, finalDstRule,
  1960. dstFromOffset - dstFromDSTSavings, dstFromDSTSavings, nextStart, status);
  1961. }
  1962. }
  1963. }
  1964. if (U_FAILURE(status)) {
  1965. goto cleanupWriteZone;
  1966. }
  1967. }
  1968. }
  1969. if (stdCount > 0) {
  1970. if (finalStdRule == NULL) {
  1971. if (stdCount == 1) {
  1972. writeZonePropsByTime(w, FALSE, stdName, stdFromOffset, stdToOffset, stdStartTime,
  1973. TRUE, status);
  1974. } else {
  1975. writeZonePropsByDOW(w, FALSE, stdName, stdFromOffset, stdToOffset,
  1976. stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, stdUntilTime, status);
  1977. }
  1978. if (U_FAILURE(status)) {
  1979. goto cleanupWriteZone;
  1980. }
  1981. } else {
  1982. if (stdCount == 1) {
  1983. writeFinalRule(w, FALSE, finalStdRule,
  1984. stdFromOffset - stdFromDSTSavings, stdFromDSTSavings, stdStartTime, status);
  1985. } else {
  1986. // Use a single rule if possible
  1987. if (isEquivalentDateRule(stdMonth, stdWeekInMonth, stdDayOfWeek, finalStdRule->getRule())) {
  1988. writeZonePropsByDOW(w, FALSE, stdName, stdFromOffset, stdToOffset,
  1989. stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, MAX_MILLIS, status);
  1990. } else {
  1991. // Not equivalent rule - write out two different rules
  1992. writeZonePropsByDOW(w, FALSE, stdName, stdFromOffset, stdToOffset,
  1993. stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, stdUntilTime, status);
  1994. if (U_FAILURE(status)) {
  1995. goto cleanupWriteZone;
  1996. }
  1997. UDate nextStart;
  1998. UBool nextStartAvail = finalStdRule->getNextStart(stdUntilTime, stdFromOffset - stdFromDSTSavings, stdFromDSTSavings, false, nextStart);
  1999. U_ASSERT(nextStartAvail);
  2000. if (nextStartAvail) {
  2001. writeFinalRule(w, FALSE, finalStdRule,
  2002. stdFromOffset - stdFromDSTSavings, stdFromDSTSavings, nextStart, status);
  2003. }
  2004. }
  2005. }
  2006. if (U_FAILURE(status)) {
  2007. goto cleanupWriteZone;
  2008. }
  2009. }
  2010. }
  2011. }
  2012. writeFooter(w, status);
  2013. cleanupWriteZone:
  2014. if (finalStdRule != NULL) {
  2015. delete finalStdRule;
  2016. }
  2017. if (finalDstRule != NULL) {
  2018. delete finalDstRule;
  2019. }
  2020. }
  2021. void
  2022. VTimeZone::writeHeaders(VTZWriter& writer, UErrorCode& status) const {
  2023. if (U_FAILURE(status)) {
  2024. return;
  2025. }
  2026. UnicodeString tzid;
  2027. tz->getID(tzid);
  2028. writer.write(ICAL_BEGIN);
  2029. writer.write(COLON);
  2030. writer.write(ICAL_VTIMEZONE);
  2031. writer.write(ICAL_NEWLINE);
  2032. writer.write(ICAL_TZID);
  2033. writer.write(COLON);
  2034. writer.write(tzid);
  2035. writer.write(ICAL_NEWLINE);
  2036. if (tzurl.length() != 0) {
  2037. writer.write(ICAL_TZURL);
  2038. writer.write(COLON);
  2039. writer.write(tzurl);
  2040. writer.write(ICAL_NEWLINE);
  2041. }
  2042. if (lastmod != MAX_MILLIS) {
  2043. UnicodeString lastmodStr;
  2044. writer.write(ICAL_LASTMOD);
  2045. writer.write(COLON);
  2046. writer.write(getUTCDateTimeString(lastmod, lastmodStr));
  2047. writer.write(ICAL_NEWLINE);
  2048. }
  2049. }
  2050. /*
  2051. * Write the closing section of the VTIMEZONE definition block
  2052. */
  2053. void
  2054. VTimeZone::writeFooter(VTZWriter& writer, UErrorCode& status) const {
  2055. if (U_FAILURE(status)) {
  2056. return;
  2057. }
  2058. writer.write(ICAL_END);
  2059. writer.write(COLON);
  2060. writer.write(ICAL_VTIMEZONE);
  2061. writer.write(ICAL_NEWLINE);
  2062. }
  2063. /*
  2064. * Write a single start time
  2065. */
  2066. void
  2067. VTimeZone::writeZonePropsByTime(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
  2068. int32_t fromOffset, int32_t toOffset, UDate time, UBool withRDATE,
  2069. UErrorCode& status) const {
  2070. if (U_FAILURE(status)) {
  2071. return;
  2072. }
  2073. beginZoneProps(writer, isDst, zonename, fromOffset, toOffset, time, status);
  2074. if (U_FAILURE(status)) {
  2075. return;
  2076. }
  2077. if (withRDATE) {
  2078. writer.write(ICAL_RDATE);
  2079. writer.write(COLON);
  2080. UnicodeString timestr;
  2081. writer.write(getDateTimeString(time + fromOffset, timestr));
  2082. writer.write(ICAL_NEWLINE);
  2083. }
  2084. endZoneProps(writer, isDst, status);
  2085. if (U_FAILURE(status)) {
  2086. return;
  2087. }
  2088. }
  2089. /*
  2090. * Write start times defined by a DOM rule using VTIMEZONE RRULE
  2091. */
  2092. void
  2093. VTimeZone::writeZonePropsByDOM(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
  2094. int32_t fromOffset, int32_t toOffset,
  2095. int32_t month, int32_t dayOfMonth, UDate startTime, UDate untilTime,
  2096. UErrorCode& status) const {
  2097. if (U_FAILURE(status)) {
  2098. return;
  2099. }
  2100. beginZoneProps(writer, isDst, zonename, fromOffset, toOffset, startTime, status);
  2101. if (U_FAILURE(status)) {
  2102. return;
  2103. }
  2104. beginRRULE(writer, month, status);
  2105. if (U_FAILURE(status)) {
  2106. return;
  2107. }
  2108. writer.write(ICAL_BYMONTHDAY);
  2109. writer.write(EQUALS_SIGN);
  2110. UnicodeString dstr;
  2111. appendAsciiDigits(dayOfMonth, 0, dstr);
  2112. writer.write(dstr);
  2113. if (untilTime != MAX_MILLIS) {
  2114. appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr), status);
  2115. if (U_FAILURE(status)) {
  2116. return;
  2117. }
  2118. }
  2119. writer.write(ICAL_NEWLINE);
  2120. endZoneProps(writer, isDst, status);
  2121. }
  2122. /*
  2123. * Write start times defined by a DOW rule using VTIMEZONE RRULE
  2124. */
  2125. void
  2126. VTimeZone::writeZonePropsByDOW(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
  2127. int32_t fromOffset, int32_t toOffset,
  2128. int32_t month, int32_t weekInMonth, int32_t dayOfWeek,
  2129. UDate startTime, UDate untilTime, UErrorCode& status) const {
  2130. if (U_FAILURE(status)) {
  2131. return;
  2132. }
  2133. beginZoneProps(writer, isDst, zonename, fromOffset, toOffset, startTime, status);
  2134. if (U_FAILURE(status)) {
  2135. return;
  2136. }
  2137. beginRRULE(writer, month, status);
  2138. if (U_FAILURE(status)) {
  2139. return;
  2140. }
  2141. writer.write(ICAL_BYDAY);
  2142. writer.write(EQUALS_SIGN);
  2143. UnicodeString dstr;
  2144. appendAsciiDigits(weekInMonth, 0, dstr);
  2145. writer.write(dstr); // -4, -3, -2, -1, 1, 2, 3, 4
  2146. writer.write(ICAL_DOW_NAMES[dayOfWeek - 1]); // SU, MO, TU...
  2147. if (untilTime != MAX_MILLIS) {
  2148. appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr), status);
  2149. if (U_FAILURE(status)) {
  2150. return;
  2151. }
  2152. }
  2153. writer.write(ICAL_NEWLINE);
  2154. endZoneProps(writer, isDst, status);
  2155. }
  2156. /*
  2157. * Write start times defined by a DOW_GEQ_DOM rule using VTIMEZONE RRULE
  2158. */
  2159. void
  2160. VTimeZone::writeZonePropsByDOW_GEQ_DOM(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
  2161. int32_t fromOffset, int32_t toOffset,
  2162. int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
  2163. UDate startTime, UDate untilTime, UErrorCode& status) const {
  2164. if (U_FAILURE(status)) {
  2165. return;
  2166. }
  2167. // Check if this rule can be converted to DOW rule
  2168. if (dayOfMonth%7 == 1) {
  2169. // Can be represented by DOW rule
  2170. writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset,
  2171. month, (dayOfMonth + 6)/7, dayOfWeek, startTime, untilTime, status);
  2172. if (U_FAILURE(status)) {
  2173. return;
  2174. }
  2175. } else if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - dayOfMonth)%7 == 6) {
  2176. // Can be represented by DOW rule with negative week number
  2177. writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset,
  2178. month, -1*((MONTHLENGTH[month] - dayOfMonth + 1)/7), dayOfWeek, startTime, untilTime, status);
  2179. if (U_FAILURE(status)) {
  2180. return;
  2181. }
  2182. } else {
  2183. // Otherwise, use BYMONTHDAY to include all possible dates
  2184. beginZoneProps(writer, isDst, zonename, fromOffset, toOffset, startTime, status);
  2185. if (U_FAILURE(status)) {
  2186. return;
  2187. }
  2188. // Check if all days are in the same month
  2189. int32_t startDay = dayOfMonth;
  2190. int32_t currentMonthDays = 7;
  2191. if (dayOfMonth <= 0) {
  2192. // The start day is in previous month
  2193. int32_t prevMonthDays = 1 - dayOfMonth;
  2194. currentMonthDays -= prevMonthDays;
  2195. int32_t prevMonth = (month - 1) < 0 ? 11 : month - 1;
  2196. // Note: When a rule is separated into two, UNTIL attribute needs to be
  2197. // calculated for each of them. For now, we skip this, because we basically use this method
  2198. // only for final rules, which does not have the UNTIL attribute
  2199. writeZonePropsByDOW_GEQ_DOM_sub(writer, prevMonth, -prevMonthDays, dayOfWeek, prevMonthDays,
  2200. MAX_MILLIS /* Do not use UNTIL */, fromOffset, status);
  2201. if (U_FAILURE(status)) {
  2202. return;
  2203. }
  2204. // Start from 1 for the rest
  2205. startDay = 1;
  2206. } else if (dayOfMonth + 6 > MONTHLENGTH[month]) {
  2207. // Note: This code does not actually work well in February. For now, days in month in
  2208. // non-leap year.
  2209. int32_t nextMonthDays = dayOfMonth + 6 - MONTHLENGTH[month];
  2210. currentMonthDays -= nextMonthDays;
  2211. int32_t nextMonth = (month + 1) > 11 ? 0 : month + 1;
  2212. writeZonePropsByDOW_GEQ_DOM_sub(writer, nextMonth, 1, dayOfWeek, nextMonthDays,
  2213. MAX_MILLIS /* Do not use UNTIL */, fromOffset, status);
  2214. if (U_FAILURE(status)) {
  2215. return;
  2216. }
  2217. }
  2218. writeZonePropsByDOW_GEQ_DOM_sub(writer, month, startDay, dayOfWeek, currentMonthDays,
  2219. untilTime, fromOffset, status);
  2220. if (U_FAILURE(status)) {
  2221. return;
  2222. }
  2223. endZoneProps(writer, isDst, status);
  2224. }
  2225. }
  2226. /*
  2227. * Called from writeZonePropsByDOW_GEQ_DOM
  2228. */
  2229. void
  2230. VTimeZone::writeZonePropsByDOW_GEQ_DOM_sub(VTZWriter& writer, int32_t month, int32_t dayOfMonth,
  2231. int32_t dayOfWeek, int32_t numDays,
  2232. UDate untilTime, int32_t fromOffset, UErrorCode& status) const {
  2233. if (U_FAILURE(status)) {
  2234. return;
  2235. }
  2236. int32_t startDayNum = dayOfMonth;
  2237. UBool isFeb = (month == UCAL_FEBRUARY);
  2238. if (dayOfMonth < 0 && !isFeb) {
  2239. // Use positive number if possible
  2240. startDayNum = MONTHLENGTH[month] + dayOfMonth + 1;
  2241. }
  2242. beginRRULE(writer, month, status);
  2243. if (U_FAILURE(status)) {
  2244. return;
  2245. }
  2246. writer.write(ICAL_BYDAY);
  2247. writer.write(EQUALS_SIGN);
  2248. writer.write(ICAL_DOW_NAMES[dayOfWeek - 1]); // SU, MO, TU...
  2249. writer.write(SEMICOLON);
  2250. writer.write(ICAL_BYMONTHDAY);
  2251. writer.write(EQUALS_SIGN);
  2252. UnicodeString dstr;
  2253. appendAsciiDigits(startDayNum, 0, dstr);
  2254. writer.write(dstr);
  2255. for (int32_t i = 1; i < numDays; i++) {
  2256. writer.write(COMMA);
  2257. dstr.remove();
  2258. appendAsciiDigits(startDayNum + i, 0, dstr);
  2259. writer.write(dstr);
  2260. }
  2261. if (untilTime != MAX_MILLIS) {
  2262. appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr), status);
  2263. if (U_FAILURE(status)) {
  2264. return;
  2265. }
  2266. }
  2267. writer.write(ICAL_NEWLINE);
  2268. }
  2269. /*
  2270. * Write start times defined by a DOW_LEQ_DOM rule using VTIMEZONE RRULE
  2271. */
  2272. void
  2273. VTimeZone::writeZonePropsByDOW_LEQ_DOM(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
  2274. int32_t fromOffset, int32_t toOffset,
  2275. int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
  2276. UDate startTime, UDate untilTime, UErrorCode& status) const {
  2277. if (U_FAILURE(status)) {
  2278. return;
  2279. }
  2280. // Check if this rule can be converted to DOW rule
  2281. if (dayOfMonth%7 == 0) {
  2282. // Can be represented by DOW rule
  2283. writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset,
  2284. month, dayOfMonth/7, dayOfWeek, startTime, untilTime, status);
  2285. } else if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - dayOfMonth)%7 == 0){
  2286. // Can be represented by DOW rule with negative week number
  2287. writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset,
  2288. month, -1*((MONTHLENGTH[month] - dayOfMonth)/7 + 1), dayOfWeek, startTime, untilTime, status);
  2289. } else if (month == UCAL_FEBRUARY && dayOfMonth == 29) {
  2290. // Specical case for February
  2291. writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset,
  2292. UCAL_FEBRUARY, -1, dayOfWeek, startTime, untilTime, status);
  2293. } else {
  2294. // Otherwise, convert this to DOW_GEQ_DOM rule
  2295. writeZonePropsByDOW_GEQ_DOM(writer, isDst, zonename, fromOffset, toOffset,
  2296. month, dayOfMonth - 6, dayOfWeek, startTime, untilTime, status);
  2297. }
  2298. }
  2299. /*
  2300. * Write the final time zone rule using RRULE, with no UNTIL attribute
  2301. */
  2302. void
  2303. VTimeZone::writeFinalRule(VTZWriter& writer, UBool isDst, const AnnualTimeZoneRule* rule,
  2304. int32_t fromRawOffset, int32_t fromDSTSavings,
  2305. UDate startTime, UErrorCode& status) const {
  2306. if (U_FAILURE(status)) {
  2307. return;
  2308. }
  2309. UBool modifiedRule = TRUE;
  2310. const DateTimeRule *dtrule = toWallTimeRule(rule->getRule(), fromRawOffset, fromDSTSavings);
  2311. if (dtrule == NULL) {
  2312. modifiedRule = FALSE;
  2313. dtrule = rule->getRule();
  2314. }
  2315. // If the rule's mills in a day is out of range, adjust start time.
  2316. // Olson tzdata supports 24:00 of a day, but VTIMEZONE does not.
  2317. // See ticket#7008/#7518
  2318. int32_t timeInDay = dtrule->getRuleMillisInDay();
  2319. if (timeInDay < 0) {
  2320. startTime = startTime + (0 - timeInDay);
  2321. } else if (timeInDay >= U_MILLIS_PER_DAY) {
  2322. startTime = startTime - (timeInDay - (U_MILLIS_PER_DAY - 1));
  2323. }
  2324. int32_t toOffset = rule->getRawOffset() + rule->getDSTSavings();
  2325. UnicodeString name;
  2326. rule->getName(name);
  2327. switch (dtrule->getDateRuleType()) {
  2328. case DateTimeRule::DOM:
  2329. writeZonePropsByDOM(writer, isDst, name, fromRawOffset + fromDSTSavings, toOffset,
  2330. dtrule->getRuleMonth(), dtrule->getRuleDayOfMonth(), startTime, MAX_MILLIS, status);
  2331. break;
  2332. case DateTimeRule::DOW:
  2333. writeZonePropsByDOW(writer, isDst, name, fromRawOffset + fromDSTSavings, toOffset,
  2334. dtrule->getRuleMonth(), dtrule->getRuleWeekInMonth(), dtrule->getRuleDayOfWeek(), startTime, MAX_MILLIS, status);
  2335. break;
  2336. case DateTimeRule::DOW_GEQ_DOM:
  2337. writeZonePropsByDOW_GEQ_DOM(writer, isDst, name, fromRawOffset + fromDSTSavings, toOffset,
  2338. dtrule->getRuleMonth(), dtrule->getRuleDayOfMonth(), dtrule->getRuleDayOfWeek(), startTime, MAX_MILLIS, status);
  2339. break;
  2340. case DateTimeRule::DOW_LEQ_DOM:
  2341. writeZonePropsByDOW_LEQ_DOM(writer, isDst, name, fromRawOffset + fromDSTSavings, toOffset,
  2342. dtrule->getRuleMonth(), dtrule->getRuleDayOfMonth(), dtrule->getRuleDayOfWeek(), startTime, MAX_MILLIS, status);
  2343. break;
  2344. }
  2345. if (modifiedRule) {
  2346. delete dtrule;
  2347. }
  2348. }
  2349. /*
  2350. * Write the opening section of zone properties
  2351. */
  2352. void
  2353. VTimeZone::beginZoneProps(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
  2354. int32_t fromOffset, int32_t toOffset, UDate startTime, UErrorCode& status) const {
  2355. if (U_FAILURE(status)) {
  2356. return;
  2357. }
  2358. writer.write(ICAL_BEGIN);
  2359. writer.write(COLON);
  2360. if (isDst) {
  2361. writer.write(ICAL_DAYLIGHT);
  2362. } else {
  2363. writer.write(ICAL_STANDARD);
  2364. }
  2365. writer.write(ICAL_NEWLINE);
  2366. UnicodeString dstr;
  2367. // TZOFFSETTO
  2368. writer.write(ICAL_TZOFFSETTO);
  2369. writer.write(COLON);
  2370. millisToOffset(toOffset, dstr);
  2371. writer.write(dstr);
  2372. writer.write(ICAL_NEWLINE);
  2373. // TZOFFSETFROM
  2374. writer.write(ICAL_TZOFFSETFROM);
  2375. writer.write(COLON);
  2376. millisToOffset(fromOffset, dstr);
  2377. writer.write(dstr);
  2378. writer.write(ICAL_NEWLINE);
  2379. // TZNAME
  2380. writer.write(ICAL_TZNAME);
  2381. writer.write(COLON);
  2382. writer.write(zonename);
  2383. writer.write(ICAL_NEWLINE);
  2384. // DTSTART
  2385. writer.write(ICAL_DTSTART);
  2386. writer.write(COLON);
  2387. writer.write(getDateTimeString(startTime + fromOffset, dstr));
  2388. writer.write(ICAL_NEWLINE);
  2389. }
  2390. /*
  2391. * Writes the closing section of zone properties
  2392. */
  2393. void
  2394. VTimeZone::endZoneProps(VTZWriter& writer, UBool isDst, UErrorCode& status) const {
  2395. if (U_FAILURE(status)) {
  2396. return;
  2397. }
  2398. // END:STANDARD or END:DAYLIGHT
  2399. writer.write(ICAL_END);
  2400. writer.write(COLON);
  2401. if (isDst) {
  2402. writer.write(ICAL_DAYLIGHT);
  2403. } else {
  2404. writer.write(ICAL_STANDARD);
  2405. }
  2406. writer.write(ICAL_NEWLINE);
  2407. }
  2408. /*
  2409. * Write the beggining part of RRULE line
  2410. */
  2411. void
  2412. VTimeZone::beginRRULE(VTZWriter& writer, int32_t month, UErrorCode& status) const {
  2413. if (U_FAILURE(status)) {
  2414. return;
  2415. }
  2416. UnicodeString dstr;
  2417. writer.write(ICAL_RRULE);
  2418. writer.write(COLON);
  2419. writer.write(ICAL_FREQ);
  2420. writer.write(EQUALS_SIGN);
  2421. writer.write(ICAL_YEARLY);
  2422. writer.write(SEMICOLON);
  2423. writer.write(ICAL_BYMONTH);
  2424. writer.write(EQUALS_SIGN);
  2425. appendAsciiDigits(month + 1, 0, dstr);
  2426. writer.write(dstr);
  2427. writer.write(SEMICOLON);
  2428. }
  2429. /*
  2430. * Append the UNTIL attribute after RRULE line
  2431. */
  2432. void
  2433. VTimeZone::appendUNTIL(VTZWriter& writer, const UnicodeString& until, UErrorCode& status) const {
  2434. if (U_FAILURE(status)) {
  2435. return;
  2436. }
  2437. if (until.length() > 0) {
  2438. writer.write(SEMICOLON);
  2439. writer.write(ICAL_UNTIL);
  2440. writer.write(EQUALS_SIGN);
  2441. writer.write(until);
  2442. }
  2443. }
  2444. U_NAMESPACE_END
  2445. #endif /* #if !UCONFIG_NO_FORMATTING */
  2446. //eof