DateTime.java 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. // Copyright (c) 2006 Per M.A. Bothner.
  2. // This is free software; for terms and warranty disclaimer see ../../COPYING.
  3. package gnu.math;
  4. import java.util.Date;
  5. import java.util.Calendar;
  6. import java.util.TimeZone;
  7. import java.util.GregorianCalendar;
  8. import gnu.math.IntNum;
  9. /**
  10. * Represents a date and/or time.
  11. * Similar functionality as java.util.Calendar (and uses GregorianCalendar
  12. * internally) but supports arithmetic.
  13. * Can be for XML Schema date/time types, specifically as used in XPath/Xquery..
  14. */
  15. public class DateTime extends Quantity implements Cloneable
  16. {
  17. Unit unit = Unit.date;
  18. /** Fractional seconds, in units of nanoseconds. */
  19. int nanoSeconds;
  20. GregorianCalendar calendar;
  21. int mask;
  22. /*
  23. static final int REFERENCE_YEAR = 1972;
  24. static final int REFERENCE_MONTH = 0; // January
  25. static final int REFERENCE_DAY = 0; // January 1
  26. */
  27. static final int YEAR_COMPONENT = 1;
  28. static final int MONTH_COMPONENT = 2;
  29. static final int DAY_COMPONENT = 3;
  30. static final int HOURS_COMPONENT = 4;
  31. static final int MINUTES_COMPONENT = 5;
  32. static final int SECONDS_COMPONENT = 6;
  33. static final int TIMEZONE_COMPONENT = 7;
  34. public static final int YEAR_MASK = 1 << YEAR_COMPONENT;
  35. public static final int MONTH_MASK = 1 << MONTH_COMPONENT;
  36. public static final int DAY_MASK = 1 << DAY_COMPONENT;
  37. public static final int HOURS_MASK = 1 << HOURS_COMPONENT;
  38. public static final int MINUTES_MASK = 1 << MINUTES_COMPONENT;
  39. public static final int SECONDS_MASK = 1 << SECONDS_COMPONENT;
  40. public static final int TIMEZONE_MASK = 1 << TIMEZONE_COMPONENT;
  41. public static final int DATE_MASK = YEAR_MASK|MONTH_MASK|DAY_MASK;
  42. public static final int TIME_MASK = HOURS_MASK|MINUTES_MASK|SECONDS_MASK;
  43. public int components() { return mask & ~TIMEZONE_MASK; }
  44. public DateTime cast (int newComponents)
  45. {
  46. int oldComponents = mask & ~TIMEZONE_MASK;
  47. if (newComponents == oldComponents)
  48. return this;
  49. DateTime copy
  50. = new DateTime(newComponents, (GregorianCalendar) calendar.clone());
  51. if ((newComponents & ~oldComponents) != 0
  52. // Special case: Casting xs:date to xs:dateTime *is* allowed.
  53. && ! (oldComponents == DATE_MASK
  54. && newComponents == (DATE_MASK|TIME_MASK)))
  55. throw new ClassCastException("cannot cast DateTime - missing conponents");
  56. if (isZoneUnspecified())
  57. copy.mask &= ~TIMEZONE_MASK;
  58. else
  59. copy.mask |= TIMEZONE_MASK;
  60. int extraComponents = oldComponents & ~newComponents;
  61. if ((extraComponents & TIME_MASK) != 0)
  62. {
  63. copy.calendar.clear(Calendar.HOUR_OF_DAY);
  64. copy.calendar.clear(Calendar.MINUTE);
  65. copy.calendar.clear(Calendar.SECOND);
  66. }
  67. else
  68. copy.nanoSeconds = nanoSeconds;
  69. if ((extraComponents & YEAR_MASK) != 0)
  70. {
  71. copy.calendar.clear(Calendar.YEAR);
  72. copy.calendar.clear(Calendar.ERA);
  73. }
  74. if ((extraComponents & MONTH_MASK) != 0)
  75. copy.calendar.clear(Calendar.MONTH);
  76. if ((extraComponents & DAY_MASK) != 0)
  77. copy.calendar.clear(Calendar.DATE);
  78. return copy;
  79. }
  80. private static final Date minDate = new Date(Long.MIN_VALUE);
  81. public DateTime (int mask)
  82. {
  83. calendar = new GregorianCalendar();
  84. // Never use Julian calendar.
  85. calendar.setGregorianChange(minDate);
  86. calendar.clear();
  87. this.mask = mask;
  88. }
  89. public DateTime (int mask, GregorianCalendar calendar)
  90. {
  91. this.calendar = calendar;
  92. this.mask = mask;
  93. }
  94. public static DateTime parse (String value, int mask)
  95. {
  96. DateTime result = new DateTime(mask);
  97. value = value.trim();
  98. int len = value.length();
  99. int pos = 0;
  100. boolean wantDate = (mask & DATE_MASK) != 0;
  101. boolean wantTime = (mask & TIME_MASK) != 0;
  102. if (wantDate)
  103. {
  104. pos = result.parseDate(value, pos, mask);
  105. if (wantTime)
  106. {
  107. if (pos < 0 || pos >= len || value.charAt(pos) != 'T')
  108. pos = -1;
  109. else
  110. pos++;
  111. }
  112. }
  113. if (wantTime)
  114. pos = result.parseTime(value, pos);
  115. pos = result.parseZone(value, pos);
  116. if (pos != len)
  117. throw new NumberFormatException("Unrecognized date/time '"+value+'\'');
  118. return result;
  119. }
  120. int parseDate(String str, int start, int mask)
  121. {
  122. if (start < 0)
  123. return start;
  124. int len = str.length();
  125. boolean negYear = false;
  126. if (start < len && str.charAt(start) == '-')
  127. {
  128. start++;
  129. negYear = true;
  130. }
  131. int pos = start;
  132. int part, year, month;
  133. if ((mask & YEAR_MASK) == 0)
  134. {
  135. if (! negYear)
  136. return -1;
  137. year = -1;
  138. }
  139. else
  140. {
  141. part = parseDigits(str, pos);
  142. year = part >> 16;
  143. pos = part & 0xffff;
  144. if (pos != start+4 && (pos <=start+4 || str.charAt(start) == '0'))
  145. return -1;
  146. if (negYear || year == 0)
  147. {
  148. calendar.set(Calendar.ERA, GregorianCalendar.BC);
  149. calendar.set(Calendar.YEAR, year+1);
  150. }
  151. else
  152. calendar.set(Calendar.YEAR, year);
  153. }
  154. if ((mask & (MONTH_MASK|DAY_MASK)) == 0)
  155. return pos;
  156. if (pos >= len || str.charAt(pos) != '-')
  157. return -1;
  158. start = ++pos;
  159. if ((mask & MONTH_MASK) != 0)
  160. {
  161. part = parseDigits(str, start);
  162. month = part >> 16;
  163. pos = part & 0xffff;
  164. if (month <= 0 || month > 12 || pos != start + 2)
  165. return -1;
  166. calendar.set(Calendar.MONTH, month-1);
  167. if ((mask & DAY_MASK) == 0)
  168. return pos;
  169. }
  170. else
  171. month = -1;
  172. if (pos >= len || str.charAt(pos) != '-')
  173. return -1;
  174. start = pos+1;
  175. part = parseDigits(str, start);
  176. int day = part >> 16;
  177. pos = part & 0xffff;
  178. if (day > 0 && pos == start+2)
  179. {
  180. int maxDay;
  181. if ((mask & MONTH_MASK) == 0)
  182. maxDay = 31;
  183. else
  184. maxDay = daysInMonth(month-1, (mask & YEAR_MASK) != 0 ? year : 2000);
  185. if (day <= maxDay)
  186. {
  187. calendar.set(Calendar.DATE, day);
  188. return pos;
  189. }
  190. }
  191. return -1;
  192. }
  193. public static boolean isLeapYear (int year)
  194. {
  195. return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0);
  196. }
  197. public static int daysInMonth (int month, int year)
  198. {
  199. switch (month)
  200. {
  201. case Calendar.APRIL:
  202. case Calendar.JUNE:
  203. case Calendar.SEPTEMBER:
  204. case Calendar.NOVEMBER:
  205. return 30;
  206. case Calendar.FEBRUARY:
  207. return isLeapYear(year) ? 29 : 28;
  208. default:
  209. return 31;
  210. }
  211. }
  212. public static TimeZone GMT = TimeZone.getTimeZone("GMT");
  213. int parseZone(String str, int start)
  214. {
  215. if (start < 0)
  216. return start;
  217. int part = parseZoneMinutes(str, start);
  218. if (part == 0)
  219. return -1;
  220. if (part == start)
  221. return start;
  222. int minutes = part >> 16;
  223. TimeZone zone;
  224. int pos = part & 0xffff;
  225. if (minutes == 0)
  226. zone = GMT;
  227. else
  228. zone = TimeZone.getTimeZone("GMT"+ str.substring(start, pos));
  229. calendar.setTimeZone(zone);
  230. mask |= TIMEZONE_MASK;
  231. return pos;
  232. }
  233. /** Return (MINUTES<<16)|END_POS if time-zone indicator was seen.
  234. * Returns START otherwise, or 0 on an error. */
  235. int parseZoneMinutes(String str, int start)
  236. {
  237. int len = str.length();
  238. if (start == len || start < 0)
  239. return start;
  240. char ch = str.charAt(start);
  241. if (ch == 'Z')
  242. return start+1;
  243. if (ch != '+' && ch != '-')
  244. return start;
  245. start++;
  246. int part = parseDigits(str, start);
  247. int hour = part >> 16;
  248. if (hour > 14)
  249. return 0;
  250. int minute = 60 * hour;
  251. int pos = part & 0xffff;
  252. if (pos != start+2)
  253. return 0;
  254. if (pos < len)
  255. {
  256. if (str.charAt(pos) == ':')
  257. {
  258. start = pos+1;
  259. part = parseDigits(str, start);
  260. pos = part & 0xffff;
  261. part >>= 16;
  262. if (part > 0 && (part >= 60 || hour == 14))
  263. return 0;
  264. minute += part;
  265. if (pos!=start+2)
  266. return 0;
  267. }
  268. }
  269. else // The minutes part is not optional.
  270. return 0;
  271. if (minute > 840)
  272. return 0;
  273. if (ch == '-')
  274. minute = -minute;
  275. return (minute << 16)|pos;
  276. }
  277. int parseTime(String str, int start)
  278. {
  279. if (start < 0)
  280. return start;
  281. int len = str.length();
  282. int pos = start;
  283. int part = parseDigits(str, start);
  284. int hour = part >> 16;
  285. pos = part & 0xffff;
  286. if (hour <= 24 && pos == start+2 && pos != len && str.charAt(pos) == ':')
  287. {
  288. start = pos + 1;
  289. part = parseDigits(str, start);
  290. int minute = part >> 16;
  291. pos = part & 0xffff;
  292. if (minute < 60 && pos == start+2
  293. && pos != len && str.charAt(pos) == ':')
  294. {
  295. start = pos + 1;
  296. part = parseDigits(str, start);
  297. int second = part >> 16;
  298. pos = part & 0xffff;
  299. // We don't allow/handle leap seconds.
  300. if (second < 60 && pos == start+2)
  301. {
  302. if (pos + 1 < len && str.charAt(pos) == '.'
  303. && Character.digit(str.charAt(pos+1), 10) >= 0)
  304. {
  305. start = pos + 1;
  306. pos = start;
  307. int nanos = 0;
  308. int nfrac = 0;
  309. for (; pos < len; nfrac++, pos++)
  310. {
  311. int dig = Character.digit(str.charAt(pos), 10);
  312. if (dig < 0)
  313. break;
  314. if (nfrac < 9)
  315. nanos = 10 * nanos + dig;
  316. else if (nfrac == 9 && dig >= 5)
  317. nanos++;
  318. }
  319. while (nfrac++ < 9)
  320. nanos = 10 * nanos;
  321. nanoSeconds = nanos;
  322. }
  323. if (hour == 24
  324. && (minute != 0 || second != 0 || nanoSeconds != 0))
  325. return -1;
  326. calendar.set(Calendar.HOUR_OF_DAY, hour);
  327. calendar.set(Calendar.MINUTE, minute);
  328. calendar.set(Calendar.SECOND, second);
  329. return pos;
  330. }
  331. }
  332. }
  333. return -1;
  334. }
  335. /** Return (VALUE << 16)|END. */
  336. private static int parseDigits(String str, int start)
  337. {
  338. int i = start;
  339. int val = -1;
  340. int len = str.length();
  341. while (i < len)
  342. {
  343. char ch = str.charAt(i);
  344. int dig = Character.digit(ch, 10);
  345. if (dig < 0)
  346. break;
  347. if (val > 20000)
  348. return 0; // possible overflow
  349. val = val < 0 ? dig : 10 * val + dig;
  350. i++;
  351. }
  352. return val < 0 ? i : (val << 16) | i;
  353. }
  354. public int getYear()
  355. {
  356. int year = calendar.get(Calendar.YEAR);
  357. if (calendar.get(Calendar.ERA) == GregorianCalendar.BC)
  358. year = 1 - year;
  359. return year;
  360. }
  361. public int getMonth()
  362. {
  363. return calendar.get(Calendar.MONTH) + 1;
  364. }
  365. public int getDay()
  366. {
  367. return calendar.get(Calendar.DATE);
  368. }
  369. public int getHours()
  370. {
  371. return calendar.get(Calendar.HOUR_OF_DAY);
  372. }
  373. public int getMinutes()
  374. {
  375. return calendar.get(Calendar.MINUTE);
  376. }
  377. public int getSecondsOnly ()
  378. {
  379. return calendar.get(Calendar.SECOND);
  380. }
  381. public int getWholeSeconds () // deprecated
  382. {
  383. return calendar.get(Calendar.SECOND);
  384. }
  385. public int getNanoSecondsOnly ()
  386. {
  387. return nanoSeconds;
  388. }
  389. /*
  390. public Object getSecondsObject ()
  391. {
  392. return IntNum.make(getWholeSeconds());
  393. }
  394. */
  395. /** Return -1, 0, or 1, depending on which value is greater. */
  396. public static int compare (DateTime date1, DateTime date2)
  397. {
  398. long millis1 = date1.calendar.getTimeInMillis();
  399. long millis2 = date2.calendar.getTimeInMillis();
  400. if (((date1.mask | date2.mask) & DATE_MASK) == 0)
  401. {
  402. if (millis1 < 0) millis1 += 24 * 60 * 60 * 1000;
  403. if (millis2 < 0) millis2 += 24 * 60 * 60 * 1000;
  404. }
  405. int nanos1 = date1.nanoSeconds;
  406. int nanos2 = date2.nanoSeconds;
  407. millis1 += nanos1 / 1000000;
  408. millis2 += nanos2 / 1000000;
  409. nanos1 = nanos1 % 1000000;
  410. nanos2 = nanos2 % 1000000;
  411. return millis1 < millis2 ? -1 : millis1 > millis2 ? 1
  412. : nanos1 < nanos2 ? -1 : nanos1 > nanos2 ? 1 : 0;
  413. }
  414. public int compare (Object obj)
  415. {
  416. if (obj instanceof DateTime)
  417. return compare (this, (DateTime) obj);
  418. return ((Numeric) obj).compareReversed (this);
  419. }
  420. public static Duration sub (DateTime date1, DateTime date2)
  421. {
  422. long millis1 = date1.calendar.getTimeInMillis();
  423. long millis2 = date2.calendar.getTimeInMillis();
  424. int nanos1 = date1.nanoSeconds;
  425. int nanos2 = date2.nanoSeconds;
  426. millis1 += nanos1 / 1000000;
  427. millis2 += nanos2 / 1000000;
  428. nanos1 = nanos1 % 1000000;
  429. nanos2 = nanos2 % 1000000;
  430. long millis = millis1 - millis2;
  431. long seconds = millis / 1000;
  432. int nanos = (int) ((millis % 1000) * 1000000 + nanos2 - nanos2);
  433. seconds += nanos / 1000000000;
  434. nanos = nanos % 1000000000;
  435. return Duration.make(0, seconds, nanos, Unit.second);
  436. }
  437. public DateTime withZoneUnspecified ()
  438. {
  439. if (isZoneUnspecified())
  440. return this;
  441. DateTime r = new DateTime(mask, (GregorianCalendar) calendar.clone());
  442. r.calendar.setTimeZone(TimeZone.getDefault());
  443. r.mask &= ~TIMEZONE_MASK;
  444. return r;
  445. }
  446. public DateTime adjustTimezone (int newOffset)
  447. {
  448. DateTime r = new DateTime(mask, (GregorianCalendar) calendar.clone());
  449. TimeZone zone;
  450. if (newOffset == 0)
  451. zone = GMT;
  452. else
  453. {
  454. StringBuffer sbuf = new StringBuffer("GMT");
  455. toStringZone(newOffset, sbuf);
  456. zone = TimeZone.getTimeZone(sbuf.toString());
  457. }
  458. r.calendar.setTimeZone(zone);
  459. if ((r.mask & TIMEZONE_MASK) != 0)
  460. {
  461. long millis = calendar.getTimeInMillis();
  462. r.calendar.setTimeInMillis(millis);
  463. if ((mask & TIME_MASK) == 0)
  464. {
  465. r.calendar.set(Calendar.HOUR_OF_DAY, 0);
  466. r.calendar.set(Calendar.MINUTE, 0);
  467. r.calendar.set(Calendar.SECOND, 0);
  468. r.nanoSeconds = 0;
  469. }
  470. }
  471. else
  472. r.mask |= TIMEZONE_MASK;
  473. return r;
  474. }
  475. public static DateTime add (DateTime x, Duration y, int k)
  476. {
  477. if (y.unit == Unit.duration
  478. || (y.unit == Unit.month && (x.mask & DATE_MASK) != DATE_MASK))
  479. throw new IllegalArgumentException("invalid date/time +/- duration combinatuion");
  480. DateTime r = new DateTime(x.mask, (GregorianCalendar) x.calendar.clone());
  481. if (y.months != 0)
  482. {
  483. int month = 12 * r.getYear() + r.calendar.get(Calendar.MONTH);
  484. month += k * y.months;
  485. int day = r.calendar.get(Calendar.DATE);
  486. int year, daysInMonth;
  487. if (month >= 12)
  488. {
  489. year = month / 12;
  490. month = month % 12;
  491. r.calendar.set(Calendar.ERA, GregorianCalendar.AD);
  492. daysInMonth = daysInMonth(month, year);
  493. }
  494. else
  495. {
  496. month = 11 - month;
  497. r.calendar.set(Calendar.ERA, GregorianCalendar.BC);
  498. year = (month / 12) + 1;
  499. month = 11 - (month % 12);
  500. daysInMonth = daysInMonth(month, 1);
  501. }
  502. if (day > daysInMonth)
  503. day = daysInMonth;
  504. r.calendar.set(year, month, day);
  505. }
  506. long nanos = x.nanoSeconds + k * (y.seconds * 1000000000L + y.nanos);
  507. if (nanos != 0)
  508. {
  509. if ((x.mask & TIME_MASK) == 0)
  510. { // Truncate to 00:00:00
  511. long nanosPerDay = 1000000000L * 24 * 60 * 60;
  512. long mod = nanos % nanosPerDay;
  513. if (mod < 0)
  514. mod += nanosPerDay;
  515. nanos -= mod;
  516. }
  517. long millis = r.calendar.getTimeInMillis();
  518. millis += (nanos / 1000000000L) * 1000;
  519. r.calendar.setTimeInMillis(millis);
  520. r.nanoSeconds = (int) (nanos % 1000000000L);
  521. }
  522. return r;
  523. }
  524. public static DateTime addMinutes (DateTime x, int y)
  525. {
  526. return addSeconds (x, 60 * y);
  527. }
  528. public static DateTime addSeconds (DateTime x, int y)
  529. {
  530. DateTime r = new DateTime(x.mask, (GregorianCalendar) x.calendar.clone());
  531. long nanos = y * 1000000000L;
  532. if (nanos != 0)
  533. {
  534. nanos = x.nanoSeconds + nanos;
  535. long millis = x.calendar.getTimeInMillis();
  536. millis += (nanos / 1000000L);
  537. r.calendar.setTimeInMillis(millis);
  538. r.nanoSeconds = (int) (nanos % 1000000L);
  539. }
  540. return r;
  541. }
  542. public Numeric add (Object y, int k)
  543. {
  544. if (y instanceof Duration)
  545. return DateTime.add(this, (Duration) y, k);
  546. if (y instanceof DateTime && k == -1)
  547. return DateTime.sub(this, (DateTime) y);
  548. throw new IllegalArgumentException ();
  549. }
  550. public Numeric addReversed (Numeric x, int k)
  551. {
  552. if (x instanceof Duration && k == 1)
  553. return DateTime.add(this, (Duration) x, k);
  554. throw new IllegalArgumentException ();
  555. }
  556. private static void append (int value, StringBuffer sbuf, int minWidth)
  557. {
  558. int start = sbuf.length();
  559. sbuf.append(value);
  560. int padding = start + minWidth - sbuf.length();
  561. while (--padding >= 0)
  562. sbuf.insert(start, '0');
  563. }
  564. public void toStringDate(StringBuffer sbuf)
  565. {
  566. int mask = components();
  567. if ((mask & YEAR_MASK) != 0)
  568. {
  569. int year = calendar.get(Calendar.YEAR);
  570. if (calendar.get(Calendar.ERA) == GregorianCalendar.BC)
  571. {
  572. year--;
  573. if (year != 0)
  574. sbuf.append('-');
  575. }
  576. append(year, sbuf, 4);
  577. }
  578. else
  579. sbuf.append('-');
  580. if ((mask & (MONTH_MASK|DAY_MASK)) != 0)
  581. {
  582. sbuf.append('-');
  583. if ((mask & MONTH_MASK) != 0)
  584. append(getMonth(), sbuf, 2);
  585. if ((mask & DAY_MASK) != 0)
  586. {
  587. sbuf.append('-');
  588. append(getDay(), sbuf, 2);
  589. }
  590. }
  591. }
  592. public void toStringTime(StringBuffer sbuf)
  593. {
  594. append(getHours(), sbuf, 2);
  595. sbuf.append(':');
  596. append(getMinutes(), sbuf, 2);
  597. sbuf.append(':');
  598. append(getWholeSeconds(), sbuf, 2);
  599. Duration.appendNanoSeconds(nanoSeconds, sbuf);
  600. }
  601. public boolean isZoneUnspecified ()
  602. {
  603. //TimeZone zone = calendar.getTimeZone();
  604. //return zone.equals(TimeZone.getDefault()); // FIXME?
  605. return (mask & TIMEZONE_MASK) == 0;
  606. }
  607. public int getZoneMinutes ()
  608. {
  609. return calendar.getTimeZone().getRawOffset() / 60000;
  610. }
  611. /** Get a TimeZone object for a given offset.
  612. * @param minutes timezone offset in minutes.
  613. */
  614. public static TimeZone minutesToTimeZone (int minutes)
  615. {
  616. if (minutes == 0)
  617. return DateTime.GMT;
  618. StringBuffer sbuf = new StringBuffer("GMT");
  619. toStringZone(minutes, sbuf);
  620. return TimeZone.getTimeZone(sbuf.toString());
  621. }
  622. public void setTimeZone (TimeZone timeZone)
  623. {
  624. calendar.setTimeZone(timeZone);
  625. }
  626. public void toStringZone(StringBuffer sbuf)
  627. {
  628. if (isZoneUnspecified())
  629. return;
  630. toStringZone(getZoneMinutes(), sbuf);
  631. }
  632. public static void toStringZone(int minutes, StringBuffer sbuf)
  633. {
  634. if (minutes == 0)
  635. sbuf.append('Z');
  636. else
  637. {
  638. if (minutes < 0)
  639. {
  640. sbuf.append('-');
  641. minutes = -minutes;
  642. }
  643. else
  644. sbuf.append('+');
  645. append(minutes/60, sbuf, 2);
  646. sbuf.append(':');
  647. append(minutes%60, sbuf, 2);
  648. }
  649. }
  650. public void toString (StringBuffer sbuf)
  651. {
  652. int mask = components();
  653. boolean hasDate = (mask & DATE_MASK) != 0;
  654. boolean hasTime = (mask & TIME_MASK) != 0;
  655. if (hasDate)
  656. {
  657. toStringDate(sbuf);
  658. if (hasTime)
  659. sbuf.append('T');
  660. }
  661. if (hasTime)
  662. toStringTime(sbuf);
  663. toStringZone(sbuf);
  664. }
  665. public String toString ()
  666. {
  667. StringBuffer sbuf = new StringBuffer();
  668. toString(sbuf);
  669. return sbuf.toString();
  670. }
  671. public boolean isExact ()
  672. {
  673. return (mask & TIME_MASK) == 0;
  674. }
  675. public boolean isZero ()
  676. {
  677. throw new UnsupportedOperationException("DateTime.isZero not meaningful!");
  678. }
  679. public Unit unit() { return unit; }
  680. public Complex number() {
  681. throw new UnsupportedOperationException
  682. ("number needs to be implemented!");
  683. }
  684. }