1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xalan.lib;
  58. import java.util.Date;
  59. import java.util.TimeZone;
  60. import java.util.Calendar;
  61. import java.text.SimpleDateFormat;
  62. import java.text.DateFormat;
  63. import java.text.ParseException;
  64. import org.apache.xpath.objects.XString;
  65. import org.apache.xpath.objects.XNumber;
  66. import org.apache.xpath.objects.XBoolean;
  67. import org.apache.xpath.objects.XObject;
  68. /**
  69. * <meta name="usage" content="general"/>
  70. * This class contains EXSLT dates and times extension functions.
  71. * It is accessed by specifying a namespace URI as follows:
  72. * <pre>
  73. * xmlns:datetime="http://exslt.org/dates-and-times"
  74. * </pre>
  75. *
  76. * The documentation for each function has been copied from the relevant
  77. * EXSLT Implementer page.
  78. *
  79. * @see <a href="http://www.exslt.org/">EXSLT</a>
  80. */
  81. public class ExsltDatetime
  82. {
  83. // Datetime formats (era and zone handled separately).
  84. static final String dt = "yyyy-MM-dd'T'HH:mm:ss";
  85. static final String d = "yyyy-MM-dd";
  86. static final String gym = "yyyy-MM";
  87. static final String gy = "yyyy";
  88. static final String gmd = "MM-dd";
  89. static final String gm = "MM";
  90. static final String gd = "dd";
  91. static final String t = "HH:mm:ss";
  92. /**
  93. * The date:date-time function returns the current date and time as a date/time string.
  94. * The date/time string that's returned must be a string in the format defined as the
  95. * lexical representation of xs:dateTime in
  96. * <a href="http://www.w3.org/TR/xmlschema-2/#dateTime">[3.2.7 dateTime]</a> of
  97. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  98. * The date/time format is basically CCYY-MM-DDThh:mm:ss, although implementers should consult
  99. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a> and
  100. * <a href="http://www.iso.ch/markete/8601.pdf">[ISO 8601]</a> for details.
  101. * The date/time string format must include a time zone, either a Z to indicate Coordinated
  102. * Universal Time or a + or - followed by the difference between the difference from UTC
  103. * represented as hh:mm.
  104. */
  105. public static XString dateTime()
  106. {
  107. Calendar cal = Calendar.getInstance();
  108. Date datetime = cal.getTime();
  109. // Format for date and time.
  110. SimpleDateFormat dateFormat = new SimpleDateFormat(dt);
  111. StringBuffer buff = new StringBuffer(dateFormat.format(datetime));
  112. // Must also include offset from UTF.
  113. // Get the offset (in milliseconds).
  114. int offset = cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET);
  115. // If there is no offset, we have "Coordinated
  116. // Universal Time."
  117. if (offset == 0)
  118. buff.append("Z");
  119. else
  120. {
  121. // Convert milliseconds to hours and minutes
  122. int hrs = offset(60*60*1000);
  123. // In a few cases, the time zone may be +/-hh:30.
  124. int min = offset%(60*60*1000);
  125. char posneg = hrs < 0? '-': '+';
  126. buff.append(posneg + formatDigits(hrs) + ':' + formatDigits(min));
  127. }
  128. return new XString(buff.toString());
  129. }
  130. /**
  131. * Represent the hours and minutes with two-digit strings.
  132. * @param q hrs or minutes.
  133. * @return two-digit String representation of hrs or minutes.
  134. */
  135. private static String formatDigits(int q)
  136. {
  137. String dd = String.valueOf(Math.abs(q));
  138. return dd.length() == 1 ? '0' + dd : dd;
  139. }
  140. /**
  141. * The date:date function returns the date specified in the date/time string given
  142. * as the argument. If no argument is given, then the current local date/time, as
  143. * returned by date:date-time is used as a default argument.
  144. * The date/time string that's returned must be a string in the format defined as the
  145. * lexical representation of xs:dateTime in
  146. * <a href="http://www.w3.org/TR/xmlschema-2/#dateTime">[3.2.7 dateTime]</a> of
  147. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  148. * If the argument is not in either of these formats, date:date returns an empty string ('').
  149. * The date/time format is basically CCYY-MM-DDThh:mm:ss, although implementers should consult
  150. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a> and
  151. * <a href="http://www.iso.ch/markete/8601.pdf">[ISO 8601]</a> for details.
  152. * The date is returned as a string with a lexical representation as defined for xs:date in
  153. * [3.2.9 date] of [XML Schema Part 2: Datatypes]. The date format is basically CCYY-MM-DD,
  154. * although implementers should consult [XML Schema Part 2: Datatypes] and [ISO 8601] for details.
  155. * If no argument is given or the argument date/time specifies a time zone, then the date string
  156. * format must include a time zone, either a Z to indicate Coordinated Universal Time or a + or -
  157. * followed by the difference between the difference from UTC represented as hh:mm. If an argument
  158. * is specified and it does not specify a time zone, then the date string format must not include
  159. * a time zone.
  160. */
  161. public static XString date(String datetimeIn)
  162. throws ParseException
  163. {
  164. String[] edz = getEraDatetimeZone(datetimeIn);
  165. String leader = edz[0];
  166. String datetime = edz[1];
  167. String zone = edz[2];
  168. if (datetime == null || zone == null)
  169. return new XString("");
  170. String[] formatsIn = {dt, d};
  171. String formatOut = d;
  172. Date date = testFormats(datetime, formatsIn);
  173. if (date == null) return new XString("");
  174. SimpleDateFormat dateFormat = new SimpleDateFormat(formatOut);
  175. dateFormat.setLenient(false);
  176. String dateOut = dateFormat.format(date);
  177. if (dateOut.length() == 0)
  178. return new XString("");
  179. else
  180. return new XString(leader + dateOut + zone);
  181. }
  182. /**
  183. * See above.
  184. */
  185. public static XString date()
  186. {
  187. String datetime = dateTime().toString();
  188. String date = datetime.substring(0, datetime.indexOf("T"));
  189. String zone = datetime.substring(getZoneStart(datetime));
  190. return new XString(date + zone);
  191. }
  192. /**
  193. * The date:time function returns the time specified in the date/time string given
  194. * as the argument. If no argument is given, then the current local date/time, as
  195. * returned by date:date-time is used as a default argument.
  196. * The date/time string that's returned must be a string in the format defined as the
  197. * lexical representation of xs:dateTime in
  198. * <a href="http://www.w3.org/TR/xmlschema-2/#dateTime">[3.2.7 dateTime]</a> of
  199. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  200. * If the argument string is not in this format, date:time returns an empty string ('').
  201. * The date/time format is basically CCYY-MM-DDThh:mm:ss, although implementers should consult
  202. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a> and
  203. * <a href="http://www.iso.ch/markete/8601.pdf">[ISO 8601]</a> for details.
  204. * The date is returned as a string with a lexical representation as defined for xs:time in
  205. * <a href="http://www.w3.org/TR/xmlschema-2/#time">[3.2.8 time]</a> of [XML Schema Part 2: Datatypes].
  206. * The time format is basically hh:mm:ss, although implementers should consult [XML Schema Part 2:
  207. * Datatypes] and [ISO 8601] for details.
  208. * If no argument is given or the argument date/time specifies a time zone, then the time string
  209. * format must include a time zone, either a Z to indicate Coordinated Universal Time or a + or -
  210. * followed by the difference between the difference from UTC represented as hh:mm. If an argument
  211. * is specified and it does not specify a time zone, then the time string format must not include
  212. * a time zone.
  213. */
  214. public static XString time(String timeIn)
  215. throws ParseException
  216. {
  217. String[] edz = getEraDatetimeZone(timeIn);
  218. String time = edz[1];
  219. String zone = edz[2];
  220. if (time == null || zone == null)
  221. return new XString("");
  222. String[] formatsIn = {dt, d};
  223. String formatOut = t;
  224. Date date = testFormats(time, formatsIn);
  225. if (date == null) return new XString("");
  226. SimpleDateFormat dateFormat = new SimpleDateFormat(formatOut);
  227. String out = dateFormat.format(date);
  228. return new XString(out + zone);
  229. }
  230. /**
  231. * See above.
  232. */
  233. public static XString time()
  234. {
  235. String datetime = dateTime().toString();
  236. String time = datetime.substring(datetime.indexOf("T")+1);
  237. String zone = datetime.substring(getZoneStart(datetime));
  238. return new XString(time + zone);
  239. }
  240. /**
  241. * The date:year function returns the year of a date as a number. If no
  242. * argument is given, then the current local date/time, as returned by
  243. * date:date-time is used as a default argument.
  244. * The date/time string specified as the first argument must be a right-truncated
  245. * string in the format defined as the lexical representation of xs:dateTime in one
  246. * of the formats defined in
  247. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  248. * The permitted formats are as follows:
  249. * xs:dateTime (CCYY-MM-DDThh:mm:ss)
  250. * xs:date (CCYY-MM-DD)
  251. * xs:gYearMonth (CCYY-MM)
  252. * xs:gYear (CCYY)
  253. * If the date/time string is not in one of these formats, then NaN is returned.
  254. */
  255. public static XNumber year(String datetimeIn)
  256. throws ParseException
  257. {
  258. String[] edz = getEraDatetimeZone(datetimeIn);
  259. boolean ad = edz[0].length() == 0; // AD (Common Era -- empty leader)
  260. String datetime = edz[1];
  261. if (datetime == null)
  262. return new XNumber(Double.NaN);
  263. String[] formats = {dt, d, gym, gy};
  264. double yr = getNumber(datetime, formats, Calendar.YEAR);
  265. if (ad || yr == Double.NaN)
  266. return new XNumber(yr);
  267. else
  268. return new XNumber(-yr);
  269. }
  270. /**
  271. * See above.
  272. */
  273. public static XNumber year()
  274. {
  275. Calendar cal = Calendar.getInstance();
  276. return new XNumber(cal.get(Calendar.YEAR));
  277. }
  278. /**
  279. * The date:year function returns the month of a date as a number. If no argument
  280. * is given, then the current local date/time, as returned by date:date-time is used
  281. * as a default argument.
  282. * The date/time string specified as the first argument is a left or right-truncated
  283. * string in the format defined as the lexical representation of xs:dateTime in one of
  284. * the formats defined in
  285. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  286. * The permitted formats are as follows:
  287. * xs:dateTime (CCYY-MM-DDThh:mm:ss)
  288. * xs:date (CCYY-MM-DD)
  289. * xs:gYearMonth (CCYY-MM)
  290. * If the date/time string is not in one of these formats, then NaN is returned.
  291. */
  292. public static XNumber monthInYear(String datetimeIn)
  293. throws ParseException
  294. {
  295. String[] edz = getEraDatetimeZone(datetimeIn);
  296. String datetime = edz[1];
  297. if (datetime == null)
  298. return new XNumber(Double.NaN);
  299. String[] formats = {dt, d, gym};
  300. return new XNumber(getNumber(datetime, formats, Calendar.MONTH));
  301. }
  302. /**
  303. * See above.
  304. */
  305. public static XNumber monthInYear()
  306. {
  307. Calendar cal = Calendar.getInstance();
  308. return new XNumber(cal.get(Calendar.MONTH));
  309. }
  310. /**
  311. * The date:week-in-year function returns the week of the year as a number. If no argument
  312. * is given, then the current local date/time, as returned by date:date-time is used as the
  313. * default argument. For the purposes of numbering, counting follows ISO 8601: week 1 in a year
  314. * is the week containing the first Thursday of the year, with new weeks beginning on a Monday.
  315. * The date/time string specified as the argument is a right-truncated string in the format
  316. * defined as the lexical representation of xs:dateTime in one of the formats defined in
  317. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. The
  318. * permitted formats are as follows:
  319. * xs:dateTime (CCYY-MM-DDThh:mm:ss)
  320. * xs:date (CCYY-MM-DD)
  321. * If the date/time string is not in one of these formats, then NaN is returned.
  322. */
  323. public static XNumber weekInYear(String datetimeIn)
  324. throws ParseException
  325. {
  326. String[] edz = getEraDatetimeZone(datetimeIn);
  327. String datetime = edz[1];
  328. if (datetime == null)
  329. return new XNumber(Double.NaN);
  330. String[] formats = {dt, d};
  331. return new XNumber(getNumber(datetime, formats, Calendar.WEEK_OF_YEAR));
  332. }
  333. /**
  334. * See above.
  335. */
  336. public static XNumber weekInYear()
  337. {
  338. Calendar cal = Calendar.getInstance();
  339. return new XNumber(cal.get(Calendar.WEEK_OF_YEAR));
  340. }
  341. /**
  342. * The date:day-in-year function returns the day of a date in a year
  343. * as a number. If no argument is given, then the current local
  344. * date/time, as returned by date:date-time is used the default argument.
  345. * The date/time string specified as the argument is a right-truncated
  346. * string in the format defined as the lexical representation of xs:dateTime
  347. * in one of the formats defined in
  348. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  349. * The permitted formats are as follows:
  350. * xs:dateTime (CCYY-MM-DDThh:mm:ss)
  351. * xs:date (CCYY-MM-DD)
  352. * If the date/time string is not in one of these formats, then NaN is returned.
  353. */
  354. public static XNumber dayInYear(String datetimeIn)
  355. throws ParseException
  356. {
  357. String[] edz = getEraDatetimeZone(datetimeIn);
  358. String datetime = edz[1];
  359. if (datetime == null)
  360. return new XNumber(Double.NaN);
  361. String[] formats = {dt, d};
  362. return new XNumber(getNumber(datetime, formats, Calendar.DAY_OF_YEAR));
  363. }
  364. /**
  365. * See above.
  366. */
  367. public static XNumber dayInYear()
  368. {
  369. Calendar cal = Calendar.getInstance();
  370. return new XNumber(cal.get(Calendar.DAY_OF_YEAR));
  371. }
  372. /**
  373. * The date:day-in-month function returns the day of a date as a number.
  374. * If no argument is given, then the current local date/time, as returned
  375. * by date:date-time is used the default argument.
  376. * The date/time string specified as the argument is a left or right-truncated
  377. * string in the format defined as the lexical representation of xs:dateTime
  378. * in one of the formats defined in
  379. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  380. * The permitted formats are as follows:
  381. * xs:dateTime (CCYY-MM-DDThh:mm:ss)
  382. * xs:date (CCYY-MM-DD)
  383. * xs:gMonthDay (--MM-DD)
  384. * xs:gDay (---DD)
  385. * If the date/time string is not in one of these formats, then NaN is returned.
  386. */
  387. public static XNumber dayInMonth(String datetimeIn)
  388. throws ParseException
  389. {
  390. String[] edz = getEraDatetimeZone(datetimeIn);
  391. String datetime = edz[1];
  392. String[] formats = {dt, d, gmd, gd};
  393. double day = getNumber(datetime, formats, Calendar.DAY_OF_MONTH);
  394. return new XNumber(day);
  395. }
  396. /**
  397. * See above.
  398. */
  399. public static XNumber dayInMonth()
  400. {
  401. Calendar cal = Calendar.getInstance();
  402. return new XNumber(cal.get(Calendar.DAY_OF_MONTH));
  403. }
  404. /**
  405. * The date:day-of-week-in-month function returns the day-of-the-week
  406. * in a month of a date as a number (e.g. 3 for the 3rd Tuesday in May).
  407. * If no argument is given, then the current local date/time, as returned
  408. * by date:date-time is used the default argument.
  409. * The date/time string specified as the argument is a right-truncated string
  410. * in the format defined as the lexical representation of xs:dateTime in one
  411. * of the formats defined in
  412. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  413. * The permitted formats are as follows:
  414. * xs:dateTime (CCYY-MM-DDThh:mm:ss)
  415. * xs:date (CCYY-MM-DD)
  416. * If the date/time string is not in one of these formats, then NaN is returned.
  417. */
  418. public static XNumber dayOfWeekInMonth(String datetimeIn)
  419. throws ParseException
  420. {
  421. String[] edz = getEraDatetimeZone(datetimeIn);
  422. String datetime = edz[1];
  423. if (datetime == null)
  424. return new XNumber(Double.NaN);
  425. String[] formats = {dt, d};
  426. return new XNumber(getNumber(datetime, formats, Calendar.DAY_OF_WEEK_IN_MONTH));
  427. }
  428. /**
  429. * See above.
  430. */
  431. public static XNumber dayOfWeekInMonth()
  432. {
  433. Calendar cal = Calendar.getInstance();
  434. return new XNumber(cal.get(Calendar.DAY_OF_WEEK_IN_MONTH));
  435. }
  436. /**
  437. * The date:day-in-week function returns the day of the week given in a
  438. * date as a number. If no argument is given, then the current local date/time,
  439. * as returned by date:date-time is used the default argument.
  440. * The date/time string specified as the argument is a right-truncated string
  441. * in the format defined as the lexical representation of xs:dateTime in one
  442. * of the formats defined in
  443. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  444. * The permitted formats are as follows:
  445. * xs:dateTime (CCYY-MM-DDThh:mm:ss)
  446. * xs:date (CCYY-MM-DD)
  447. * If the date/time string is not in one of these formats, then NaN is returned.
  448. The numbering of days of the week starts at 1 for Sunday, 2 for Monday and so on up to 7 for Saturday.
  449. */
  450. public static XNumber dayInWeek(String datetimeIn)
  451. throws ParseException
  452. {
  453. String[] edz = getEraDatetimeZone(datetimeIn);
  454. String datetime = edz[1];
  455. if (datetime == null)
  456. return new XNumber(Double.NaN);
  457. String[] formats = {dt, d};
  458. return new XNumber(getNumber(datetime, formats, Calendar.DAY_OF_WEEK));
  459. }
  460. /**
  461. * See above.
  462. */
  463. public static XNumber dayInWeek()
  464. {
  465. Calendar cal = Calendar.getInstance();
  466. return new XNumber(cal.get(Calendar.DAY_OF_WEEK));
  467. }
  468. /**
  469. * The date:hour-in-day function returns the hour of the day as a number.
  470. * If no argument is given, then the current local date/time, as returned
  471. * by date:date-time is used the default argument.
  472. * The date/time string specified as the argument is a right-truncated
  473. * string in the format defined as the lexical representation of xs:dateTime
  474. * in one of the formats defined in
  475. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  476. * The permitted formats are as follows:
  477. * xs:dateTime (CCYY-MM-DDThh:mm:ss)
  478. * xs:time (hh:mm:ss)
  479. * If the date/time string is not in one of these formats, then NaN is returned.
  480. */
  481. public static XNumber hourInDay(String datetimeIn)
  482. throws ParseException
  483. {
  484. String[] edz = getEraDatetimeZone(datetimeIn);
  485. String datetime = edz[1];
  486. if (datetime == null)
  487. return new XNumber(Double.NaN);
  488. String[] formats = {d, t};
  489. return new XNumber(getNumber(datetime, formats, Calendar.HOUR_OF_DAY));
  490. }
  491. /**
  492. * See above.
  493. */
  494. public static XNumber hourInDay()
  495. {
  496. Calendar cal = Calendar.getInstance();
  497. return new XNumber(cal.get(Calendar.HOUR_OF_DAY));
  498. }
  499. /**
  500. * The date:minute-in-hour function returns the minute of the hour
  501. * as a number. If no argument is given, then the current local
  502. * date/time, as returned by date:date-time is used the default argument.
  503. * The date/time string specified as the argument is a right-truncated
  504. * string in the format defined as the lexical representation of xs:dateTime
  505. * in one of the formats defined in
  506. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  507. * The permitted formats are as follows:
  508. * xs:dateTime (CCYY-MM-DDThh:mm:ss)
  509. * xs:time (hh:mm:ss)
  510. * If the date/time string is not in one of these formats, then NaN is returned.
  511. */
  512. public static XNumber minuteInHour(String datetimeIn)
  513. throws ParseException
  514. {
  515. String[] edz = getEraDatetimeZone(datetimeIn);
  516. String datetime = edz[1];
  517. if (datetime == null)
  518. return new XNumber(Double.NaN);
  519. String[] formats = {dt,t};
  520. return new XNumber(getNumber(datetime, formats, Calendar.MINUTE));
  521. }
  522. /**
  523. * See above.
  524. */
  525. public static XNumber minuteInHour()
  526. {
  527. Calendar cal = Calendar.getInstance();
  528. return new XNumber(cal.get(Calendar.MINUTE));
  529. }
  530. /**
  531. * The date:second-in-minute function returns the second of the minute
  532. * as a number. If no argument is given, then the current local
  533. * date/time, as returned by date:date-time is used the default argument.
  534. * The date/time string specified as the argument is a right-truncated
  535. * string in the format defined as the lexical representation of xs:dateTime
  536. * in one of the formats defined in
  537. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  538. * The permitted formats are as follows:
  539. * xs:dateTime (CCYY-MM-DDThh:mm:ss)
  540. * xs:time (hh:mm:ss)
  541. * If the date/time string is not in one of these formats, then NaN is returned.
  542. */
  543. public static XNumber secondInMinute(String datetimeIn)
  544. throws ParseException
  545. {
  546. String[] edz = getEraDatetimeZone(datetimeIn);
  547. String datetime = edz[1];
  548. if (datetime == null)
  549. return new XNumber(Double.NaN);
  550. String[] formats = {dt, t};
  551. return new XNumber(getNumber(datetime, formats, Calendar.SECOND));
  552. }
  553. /**
  554. * See above.
  555. */
  556. public static XNumber secondInMinute()
  557. {
  558. Calendar cal = Calendar.getInstance();
  559. return new XNumber(cal.get(Calendar.SECOND));
  560. }
  561. /**
  562. * The date:leap-year function returns true if the year given in a date
  563. * is a leap year. If no argument is given, then the current local
  564. * date/time, as returned by date:date-time is used as a default argument.
  565. * The date/time string specified as the first argument must be a
  566. * right-truncated string in the format defined as the lexical representation
  567. * of xs:dateTime in one of the formats defined in
  568. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  569. * The permitted formats are as follows:
  570. * xs:dateTime (CCYY-MM-DDThh:mm:ss)
  571. * xs:date (CCYY-MM-DD)
  572. * xs:gYearMonth (CCYY-MM)
  573. * xs:gYear (CCYY)
  574. * If the date/time string is not in one of these formats, then NaN is returned.
  575. */
  576. public static XObject leapYear(String datetimeIn)
  577. throws ParseException
  578. {
  579. String[] edz = getEraDatetimeZone(datetimeIn);
  580. String datetime = edz[1];
  581. if (datetime == null)
  582. return new XNumber(Double.NaN);
  583. String[] formats = {dt, d, gym, gy};
  584. double dbl = getNumber(datetime, formats, Calendar.YEAR);
  585. if (dbl == Double.NaN)
  586. return new XNumber(Double.NaN);
  587. int yr = (int)dbl;
  588. return new XBoolean(yr % 400 == 0 || (yr % 100 != 0 && yr % 4 == 0));
  589. }
  590. /**
  591. * See above.
  592. */
  593. public static XBoolean leapYear()
  594. {
  595. Calendar cal = Calendar.getInstance();
  596. int yr = (int)cal.get(Calendar.YEAR);
  597. return new XBoolean(yr % 400 == 0 || (yr % 100 != 0 && yr % 4 == 0));
  598. }
  599. /**
  600. * The date:month-name function returns the full name of the month of a date.
  601. * If no argument is given, then the current local date/time, as returned by
  602. * date:date-time is used the default argument.
  603. * The date/time string specified as the argument is a left or right-truncated
  604. * string in the format defined as the lexical representation of xs:dateTime in
  605. * one of the formats defined in
  606. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  607. * The permitted formats are as follows:
  608. * xs:dateTime (CCYY-MM-DDThh:mm:ss)
  609. * xs:date (CCYY-MM-DD)
  610. * xs:gYearMonth (CCYY-MM)
  611. * xs:gMonth (--MM--)
  612. * If the date/time string is not in one of these formats, then an empty string ('')
  613. * is returned.
  614. * The result is an English month name: one of 'January', 'February', 'March',
  615. * 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November'
  616. * or 'December'.
  617. */
  618. public static XString monthName(String datetimeIn)
  619. throws ParseException
  620. {
  621. String[] edz = getEraDatetimeZone(datetimeIn);
  622. String datetime = edz[1];
  623. if (datetime == null)
  624. return new XString("");
  625. String[] formatsIn = {dt, d, gym, gm};
  626. String formatOut = "MMMM";
  627. return new XString (getNameOrAbbrev(datetimeIn, formatsIn, formatOut));
  628. }
  629. /**
  630. * See above.
  631. */
  632. public static XString monthName()
  633. {
  634. Calendar cal = Calendar.getInstance();
  635. String format = "MMMM";
  636. return new XString(getNameOrAbbrev(format));
  637. }
  638. /**
  639. * The date:month-abbreviation function returns the abbreviation of the month of
  640. * a date. If no argument is given, then the current local date/time, as returned
  641. * by date:date-time is used the default argument.
  642. * The date/time string specified as the argument is a left or right-truncated
  643. * string in the format defined as the lexical representation of xs:dateTime in
  644. * one of the formats defined in
  645. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  646. * The permitted formats are as follows:
  647. * xs:dateTime (CCYY-MM-DDThh:mm:ss)
  648. * xs:date (CCYY-MM-DD)
  649. * xs:gYearMonth (CCYY-MM)
  650. * xs:gMonth (--MM--)
  651. * If the date/time string is not in one of these formats, then an empty string ('')
  652. * is returned.
  653. * The result is a three-letter English month abbreviation: one of 'Jan', 'Feb', 'Mar',
  654. * 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov' or 'Dec'.
  655. * An implementation of this extension function in the EXSLT date namespace must conform
  656. * to the behaviour described in this document.
  657. */
  658. public static XString monthAbbreviation(String datetimeIn)
  659. throws ParseException
  660. {
  661. String[] edz = getEraDatetimeZone(datetimeIn);
  662. String datetime = edz[1];
  663. if (datetime == null)
  664. return new XString("");
  665. String[] formatsIn = {dt, d, gym, gm};
  666. String formatOut = "MMM";
  667. return new XString (getNameOrAbbrev(datetimeIn, formatsIn, formatOut));
  668. }
  669. /**
  670. * See above.
  671. */
  672. public static XString monthAbbreviation()
  673. {
  674. String format = "MMM";
  675. return new XString(getNameOrAbbrev(format));
  676. }
  677. /**
  678. * The date:day-name function returns the full name of the day of the week
  679. * of a date. If no argument is given, then the current local date/time,
  680. * as returned by date:date-time is used the default argument.
  681. * The date/time string specified as the argument is a left or right-truncated
  682. * string in the format defined as the lexical representation of xs:dateTime
  683. * in one of the formats defined in
  684. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  685. * The permitted formats are as follows:
  686. * xs:dateTime (CCYY-MM-DDThh:mm:ss)
  687. * xs:date (CCYY-MM-DD)
  688. * If the date/time string is not in one of these formats, then the empty string ('')
  689. * is returned.
  690. * The result is an English day name: one of 'Sunday', 'Monday', 'Tuesday', 'Wednesday',
  691. * 'Thursday' or 'Friday'.
  692. * An implementation of this extension function in the EXSLT date namespace must conform
  693. * to the behaviour described in this document.
  694. */
  695. public static XString dayName(String datetimeIn)
  696. throws ParseException
  697. {
  698. String[] edz = getEraDatetimeZone(datetimeIn);
  699. String datetime = edz[1];
  700. if (datetime == null)
  701. return new XString("");
  702. String[] formatsIn = {dt, d};
  703. String formatOut = "EEEE";
  704. return new XString (getNameOrAbbrev(datetimeIn, formatsIn, formatOut));
  705. }
  706. /**
  707. * See above.
  708. */
  709. public static XString dayName()
  710. {
  711. String format = "EEEE";
  712. return new XString(getNameOrAbbrev(format));
  713. }
  714. /**
  715. * The date:day-abbreviation function returns the abbreviation of the day
  716. * of the week of a date. If no argument is given, then the current local
  717. * date/time, as returned by date:date-time is used the default argument.
  718. * The date/time string specified as the argument is a left or right-truncated
  719. * string in the format defined as the lexical representation of xs:dateTime
  720. * in one of the formats defined in
  721. * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>.
  722. * The permitted formats are as follows:
  723. * xs:dateTime (CCYY-MM-DDThh:mm:ss)
  724. * xs:date (CCYY-MM-DD)
  725. * If the date/time string is not in one of these formats, then the empty string
  726. * ('') is returned.
  727. * The result is a three-letter English day abbreviation: one of 'Sun', 'Mon', 'Tue',
  728. * 'Wed', 'Thu' or 'Fri'.
  729. * An implementation of this extension function in the EXSLT date namespace must conform
  730. * to the behaviour described in this document.
  731. */
  732. public static XString dayAbbreviation(String datetimeIn)
  733. throws ParseException
  734. {
  735. String[] edz = getEraDatetimeZone(datetimeIn);
  736. String datetime = edz[1];
  737. if (datetime == null)
  738. return new XString("");
  739. String[] formatsIn = {dt, d};
  740. String formatOut = "EEE";
  741. return new XString (getNameOrAbbrev(datetimeIn, formatsIn, formatOut));
  742. }
  743. /**
  744. * See above.
  745. */
  746. public static XString dayAbbreviation()
  747. {
  748. String format = "EEE";
  749. return new XString(getNameOrAbbrev(format));
  750. }
  751. /**
  752. * Returns an array with the 3 components that a datetime input string
  753. * may contain: - (for BC era), datetime, and zone. If the zone is not
  754. * valid, return null for that component.
  755. */
  756. private static String[] getEraDatetimeZone(String in)
  757. {
  758. String leader = "";
  759. String datetime = in;
  760. String zone = "";
  761. if (in.charAt(0)=='-')
  762. {
  763. leader = "-"; // '+' is implicit , not allowed
  764. datetime = in.substring(1);
  765. }
  766. int z = getZoneStart(datetime);
  767. if (z > 0)
  768. {
  769. zone = datetime.substring(z);
  770. datetime = datetime.substring(0, z);
  771. }
  772. else if (z == -2)
  773. zone = null;
  774. //System.out.println("'" + leader + "' " + datetime + " " + zone);
  775. return new String[]{leader, datetime, zone};
  776. }
  777. /**
  778. * Get the start of zone information if the input ends
  779. * with 'Z' or +/-hh:mm. If a zone string is not
  780. * found, return -1; if the zone string is invalid,
  781. * return -2.
  782. */
  783. private static int getZoneStart (String datetime)
  784. {
  785. if (datetime.indexOf("Z") == datetime.length()-1)
  786. return datetime.indexOf("Z");
  787. else if (
  788. (datetime.lastIndexOf("-") == datetime.length()-6 &&
  789. datetime.charAt(datetime.length()-3) == ':')
  790. ||
  791. (datetime.indexOf("+") == datetime.length() -6)
  792. )
  793. {
  794. try
  795. {
  796. SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm");
  797. dateFormat.setLenient(false);
  798. Date d = dateFormat.parse(datetime.substring(datetime.length() -5));
  799. return datetime.length()-6;
  800. }
  801. catch (ParseException pe)
  802. {
  803. System.out.println("ParseException " + pe.getErrorOffset());
  804. return -2; // Invalid.
  805. }
  806. }
  807. return -1; // No zone information.
  808. }
  809. /**
  810. * Attempt to parse an input string with the allowed formats, returning
  811. * null if none of the formats work. Input formats are passed in longest to shortest,
  812. * so if any parse operation fails with a parse error in the string, can
  813. * immediately return null.
  814. */
  815. private static Date testFormats (String in, String[] formats)
  816. throws ParseException
  817. {
  818. for (int i = 0; i <formats.length; i++)
  819. {
  820. try
  821. {
  822. SimpleDateFormat dateFormat = new SimpleDateFormat(formats[i]);
  823. dateFormat.setLenient(false);
  824. return dateFormat.parse(in);
  825. }
  826. catch (ParseException pe)
  827. {
  828. if (pe.getErrorOffset() < in.length())
  829. return null;
  830. }
  831. }
  832. return null;
  833. }
  834. /**
  835. * Parse the input string and return the corresponding calendar field
  836. * number.
  837. */
  838. private static double getNumber(String in, String[] formats, int calField)
  839. throws ParseException
  840. {
  841. Calendar cal = Calendar.getInstance();
  842. cal.setLenient(false);
  843. // Try the allowed formats, from longest to shortest.
  844. Date date = testFormats(in, formats);
  845. if (date == null) return Double.NaN;
  846. cal.setTime(date);
  847. return cal.get(calField);
  848. }
  849. /**
  850. * Get the full name or abbreviation of the month or day.
  851. */
  852. private static String getNameOrAbbrev(String in,
  853. String[] formatsIn,
  854. String formatOut)
  855. throws ParseException
  856. {
  857. for (int i = 0; i <formatsIn.length; i++) // from longest to shortest.
  858. {
  859. try
  860. {
  861. SimpleDateFormat dateFormat = new SimpleDateFormat(formatsIn[i]);
  862. dateFormat.setLenient(false);
  863. Date dt = dateFormat.parse(in);
  864. dateFormat.applyPattern(formatOut);
  865. return dateFormat.format(dt);
  866. }
  867. catch (ParseException pe)
  868. {
  869. // If ParseException occurred during input string, input is invalid.
  870. // If the ParseException occurred at the end of the input string,
  871. // another format may work.
  872. if (pe.getErrorOffset() < in.length())
  873. return "";
  874. }
  875. }
  876. return "";
  877. }
  878. /**
  879. * Get the full name or abbreviation for the current month or day
  880. * (no input string).
  881. */
  882. private static String getNameOrAbbrev(String format)
  883. {
  884. Calendar cal = Calendar.getInstance();
  885. SimpleDateFormat dateFormat = new SimpleDateFormat(format);
  886. return dateFormat.format(cal.getTime());
  887. }
  888. }