Source Code Cross Referenced for DateTimeValue.java in  » XML » saxonb » net » sf » saxon » value » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » XML » saxonb » net.sf.saxon.value 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        package net.sf.saxon.value;
002:
003:        import net.sf.saxon.Controller;
004:        import net.sf.saxon.expr.XPathContext;
005:        import net.sf.saxon.functions.Component;
006:        import net.sf.saxon.om.FastStringBuffer;
007:        import net.sf.saxon.trans.DynamicError;
008:        import net.sf.saxon.trans.IndependentContext;
009:        import net.sf.saxon.trans.XPathException;
010:        import net.sf.saxon.type.*;
011:
012:        import java.math.BigDecimal;
013:        import java.math.BigInteger;
014:        import java.util.*;
015:
016:        /**
017:         * A value of type DateTime
018:         */
019:
020:        public final class DateTimeValue extends CalendarValue {
021:
022:            private int year; // the year as written, +1 for BC years
023:            private byte month; // the month as written, range 1-12
024:            private byte day; // the day as written, range 1-31
025:            private byte hour; // the hour as written (except for midnight), range 0-23
026:            private byte minute; // the minutes as written, range 0-59
027:            private byte second; // the seconds as written, range 0-59 (no leap seconds)
028:            private int microsecond;
029:
030:            /**
031:             * Get the dateTime value representing the nominal
032:             * date/time of this transformation run. Two calls within the same
033:             * query or transformation will always return the same answer.
034:             */
035:
036:            public static DateTimeValue getCurrentDateTime(XPathContext context) {
037:                Controller c;
038:                if (context == null || (c = context.getController()) == null) {
039:                    // non-XSLT/XQuery environment
040:                    // We also take this path when evaluating compile-time expressions that require an implicit timezone.
041:                    return new DateTimeValue(new GregorianCalendar(), true);
042:                } else {
043:                    return c.getCurrentDateTime();
044:                }
045:            }
046:
047:            /**
048:             * Constructor: create a dateTime value given a Java calendar object
049:             * @param calendar holds the date and time
050:             * @param tzSpecified indicates whether the timezone is specified
051:             */
052:
053:            public DateTimeValue(Calendar calendar, boolean tzSpecified) {
054:                int era = calendar.get(GregorianCalendar.ERA);
055:                year = calendar.get(Calendar.YEAR);
056:                if (era == GregorianCalendar.BC) {
057:                    year = -year;
058:                }
059:                month = (byte) (calendar.get(Calendar.MONTH) + 1);
060:                day = (byte) (calendar.get(Calendar.DATE));
061:                hour = (byte) (calendar.get(Calendar.HOUR_OF_DAY));
062:                minute = (byte) (calendar.get(Calendar.MINUTE));
063:                second = (byte) (calendar.get(Calendar.SECOND));
064:                microsecond = calendar.get(Calendar.MILLISECOND) * 1000;
065:                if (tzSpecified) {
066:                    int tz = (calendar.get(Calendar.ZONE_OFFSET) + calendar
067:                            .get(Calendar.DST_OFFSET)) / 60000;
068:                    setTimezoneInMinutes(tz);
069:                }
070:            }
071:
072:            /**
073:             * Constructor: create a dateTime value given a date and a time.
074:             * @param date the date
075:             * @param time the time
076:             * @throws net.sf.saxon.trans.XPathException if the timezones are both present and inconsistent
077:             */
078:
079:            public DateTimeValue(DateValue date, TimeValue time)
080:                    throws XPathException {
081:                SecondsDurationValue tz1 = (SecondsDurationValue) date
082:                        .getComponent(Component.TIMEZONE);
083:                SecondsDurationValue tz2 = (SecondsDurationValue) time
084:                        .getComponent(Component.TIMEZONE);
085:                boolean zoneSpecified = (tz1 != null || tz2 != null);
086:                if (tz1 != null && tz2 != null && !tz1.equals(tz2)) {
087:                    DynamicError err = new DynamicError(
088:                            "Supplied date and time are in different timezones");
089:                    err.setErrorCode("FORG0008");
090:                    throw err;
091:                }
092:
093:                year = (int) ((IntegerValue) date.getComponent(Component.YEAR))
094:                        .longValue();
095:                month = (byte) ((IntegerValue) date
096:                        .getComponent(Component.MONTH)).longValue();
097:                day = (byte) ((IntegerValue) date.getComponent(Component.DAY))
098:                        .longValue();
099:                hour = (byte) ((IntegerValue) time
100:                        .getComponent(Component.HOURS)).longValue();
101:                minute = (byte) ((IntegerValue) time
102:                        .getComponent(Component.MINUTES)).longValue();
103:                final BigDecimal secs = ((DecimalValue) time
104:                        .getComponent(Component.SECONDS)).getValue();
105:                second = (byte) secs.intValue();
106:                microsecond = secs.multiply(BigDecimal.valueOf(1000000))
107:                        .intValue() % 1000000;
108:                if (zoneSpecified) {
109:                    if (tz1 == null) {
110:                        tz1 = tz2;
111:                    }
112:                    setTimezoneInMinutes((int) (tz1.getLengthInMicroseconds() / 60000000));
113:                }
114:            }
115:
116:            /**
117:             * Constructor: create a dateTime value from a supplied string, in
118:             * ISO 8601 format
119:             */
120:
121:            public DateTimeValue(CharSequence s) throws XPathException {
122:                // input must have format yyyy-mm-ddThh:mm:ss[.fff*][([+|-]hh:mm | Z)]
123:                StringTokenizer tok = new StringTokenizer(trimWhitespace(s)
124:                        .toString(), "-:.+TZ", true);
125:                try {
126:                    if (!tok.hasMoreElements())
127:                        badDate("too short");
128:                    String part = (String) tok.nextElement();
129:                    int era = +1;
130:                    if ("+".equals(part)) {
131:                        part = (String) tok.nextElement();
132:                    } else if ("-".equals(part)) {
133:                        era = -1;
134:                        part = (String) tok.nextElement();
135:                    }
136:                    year = Integer.parseInt(part) * era;
137:                    if (part.length() < 4)
138:                        badDate("Year is less than four digits");
139:                    if (year == 0)
140:                        badDate("Year zero is not allowed");
141:                    if (era < 0) {
142:                        year++; // internal representation allows a year zero.
143:                    }
144:                    if (!tok.hasMoreElements())
145:                        badDate("Too short");
146:                    if (!"-".equals(tok.nextElement()))
147:                        badDate("Wrong delimiter after year");
148:
149:                    if (!tok.hasMoreElements())
150:                        badDate("Too short");
151:                    part = (String) tok.nextElement();
152:                    if (part.length() != 2)
153:                        badDate("Month must be two digits");
154:                    month = (byte) Integer.parseInt(part);
155:                    if (month < 1 || month > 12)
156:                        badDate("Month is out of range");
157:
158:                    if (!tok.hasMoreElements())
159:                        badDate("Too short");
160:                    if (!"-".equals(tok.nextElement()))
161:                        badDate("Wrong delimiter after month");
162:                    if (!tok.hasMoreElements())
163:                        badDate("Too short");
164:                    part = (String) tok.nextElement();
165:                    if (part.length() != 2)
166:                        badDate("Day must be two digits");
167:                    day = (byte) Integer.parseInt(part);
168:                    if (day < 1 || day > 31)
169:                        badDate("Day is out of range");
170:
171:                    if (!tok.hasMoreElements())
172:                        badDate("Too short");
173:                    if (!"T".equals(tok.nextElement()))
174:                        badDate("Wrong delimiter after day");
175:
176:                    if (!tok.hasMoreElements())
177:                        badDate("Too short");
178:                    part = (String) tok.nextElement();
179:                    if (part.length() != 2)
180:                        badDate("Hour must be two digits");
181:                    hour = (byte) Integer.parseInt(part);
182:                    if (hour > 24)
183:                        badDate("Hour is out of range");
184:
185:                    if (!tok.hasMoreElements())
186:                        badDate("Too short");
187:                    if (!":".equals(tok.nextElement()))
188:                        badDate("Wrong delimiter after hour");
189:
190:                    if (!tok.hasMoreElements())
191:                        badDate("Too short");
192:                    part = (String) tok.nextElement();
193:                    if (part.length() != 2)
194:                        badDate("Minute must be two digits");
195:                    minute = (byte) Integer.parseInt(part);
196:                    if (minute > 59)
197:                        badDate("Minute is out of range");
198:                    if (hour == 24 && minute != 0)
199:                        badDate("If hour is 24, minute must be 00");
200:                    if (!tok.hasMoreElements())
201:                        badDate("Too short");
202:                    if (!":".equals(tok.nextElement()))
203:                        badDate("Wrong delimiter after minute");
204:
205:                    if (!tok.hasMoreElements())
206:                        badDate("Too short");
207:                    part = (String) tok.nextElement();
208:                    if (part.length() != 2)
209:                        badDate("Second must be two digits");
210:                    second = (byte) Integer.parseInt(part);
211:
212:                    if (second > 59)
213:                        badDate("Second is out of range");
214:                    if (hour == 24 && second != 0)
215:                        badDate("If hour is 24, second must be 00");
216:
217:                    int tz = 0;
218:
219:                    int state = 0;
220:                    while (tok.hasMoreElements()) {
221:                        if (state == 9) {
222:                            badDate("Characters after the end");
223:                        }
224:                        String delim = (String) tok.nextElement();
225:                        if (".".equals(delim)) {
226:                            if (state != 0) {
227:                                badDate("Decimal separator occurs twice");
228:                            }
229:                            part = (String) tok.nextElement();
230:                            double fractionalSeconds = Double
231:                                    .parseDouble('.' + part);
232:                            microsecond = (int) (Math
233:                                    .round(fractionalSeconds * 1000000));
234:                            if (hour == 24 && microsecond != 0) {
235:                                badDate("If hour is 24, fractional seconds must be 0");
236:                            }
237:                            state = 1;
238:                        } else if ("Z".equals(delim)) {
239:                            if (state > 1) {
240:                                badDate("Z cannot occur here");
241:                            }
242:                            tz = 0;
243:                            state = 9; // we've finished
244:                            setTimezoneInMinutes(0);
245:                        } else if ("+".equals(delim) || "-".equals(delim)) {
246:                            if (state > 1) {
247:                                badDate(delim + " cannot occur here");
248:                            }
249:                            state = 2;
250:                            if (!tok.hasMoreElements())
251:                                badDate("Missing timezone");
252:                            part = (String) tok.nextElement();
253:                            if (part.length() != 2)
254:                                badDate("Timezone hour must be two digits");
255:
256:                            tz = Integer.parseInt(part);
257:                            if (tz > 14)
258:                                badDate("Timezone is out of range (-14:00 to +14:00)");
259:                            tz *= 60;
260:
261:                            //if (tz > 12*60) badDate("Because of Java limitations, Saxon currently limits the timezone to +/- 12 hours");
262:                            if ("-".equals(delim))
263:                                tz = -tz;
264:
265:                        } else if (":".equals(delim)) {
266:                            if (state != 2) {
267:                                badDate("Misplaced ':'");
268:                            }
269:                            state = 9;
270:                            part = (String) tok.nextElement();
271:                            int tzminute = Integer.parseInt(part);
272:                            if (part.length() != 2)
273:                                badDate("Timezone minute must be two digits");
274:                            if (tzminute > 59)
275:                                badDate("Timezone minute is out of range");
276:                            if (tz < 0)
277:                                tzminute = -tzminute;
278:                            if (Math.abs(tz) == 14 * 60 && tzminute != 0) {
279:                                badDate("Timezone is out of range (-14:00 to +14:00)");
280:                            }
281:                            tz += tzminute;
282:                            setTimezoneInMinutes(tz);
283:                        } else {
284:                            badDate("Timezone format is incorrect");
285:                        }
286:                    }
287:
288:                    if (state == 2 || state == 3) {
289:                        badDate("Timezone incomplete");
290:                    }
291:
292:                    boolean midnight = false;
293:                    if (hour == 24) {
294:                        hour = 0;
295:                        midnight = true;
296:                    }
297:
298:                    // Check that this is a valid calendar date
299:                    if (!DateValue.isValidDate(year, month, day)) {
300:                        badDate("Non-existent date");
301:                    }
302:
303:                    // Adjust midnight to 00:00:00 on the next day
304:                    if (midnight) {
305:                        DateValue t = DateValue.tomorrow(year, month, day);
306:                        year = t.getYear();
307:                        month = t.getMonth();
308:                        day = t.getDay();
309:                    }
310:
311:                } catch (NumberFormatException err) {
312:                    badDate("Non-numeric component");
313:                }
314:            }
315:
316:            private void badDate(String msg) throws XPathException {
317:                DynamicError err = new DynamicError("Invalid dateTime value. "
318:                        + msg);
319:                err.setErrorCode("FORG0001");
320:                throw err;
321:            }
322:
323:            /**
324:             * Constructor: construct a DateTimeValue from its components.
325:             * This constructor performs no validation.
326:             */
327:
328:            public DateTimeValue(int year, byte month, byte day, byte hour,
329:                    byte minute, byte second, int microsecond, int tz) {
330:                this .year = year;
331:                this .month = month;
332:                this .day = day;
333:                this .hour = hour;
334:                this .minute = minute;
335:                this .second = second;
336:                this .microsecond = microsecond;
337:                setTimezoneInMinutes(tz);
338:            }
339:
340:            /**
341:             * Get the year component
342:             */
343:
344:            public int getYear() {
345:                return year;
346:            }
347:
348:            /**
349:             * Get the month component
350:             */
351:
352:            public byte getMonth() {
353:                return month;
354:            }
355:
356:            /**
357:             * Get the day component
358:             */
359:
360:            public byte getDay() {
361:                return day;
362:            }
363:
364:            /**
365:             * Get the hour component
366:             */
367:
368:            public byte getHour() {
369:                return hour;
370:            }
371:
372:            /**
373:             * Get the minute component
374:             */
375:
376:            public byte getMinute() {
377:                return minute;
378:            }
379:
380:            /**
381:             * Get the second component
382:             */
383:
384:            public byte getSecond() {
385:                return second;
386:            }
387:
388:            /**
389:             * Get the year component
390:             */
391:
392:            public int getMicrosecond() {
393:                return microsecond;
394:            }
395:
396:            /**
397:             * Convert the value to a DateTime, retaining all the components that are actually present, and
398:             * substituting conventional values for components that are missing
399:             */
400:
401:            public DateTimeValue toDateTime() {
402:                return this ;
403:            }
404:
405:            /**
406:             * Normalize the date and time to be in timezone Z.
407:             * @param cc used to supply the implicit timezone, used when the value has
408:             * no explicit timezone
409:             * @return in general, a new DateTimeValue in timezone Z, representing the same instant in time.
410:             * Returns the original DateTimeValue if this is already in timezone Z.
411:             */
412:
413:            public DateTimeValue normalize(XPathContext cc) {
414:                if (hasTimezone()) {
415:                    return (DateTimeValue) adjustTimezone(0);
416:                } else {
417:                    DateTimeValue dt = (DateTimeValue) copy();
418:                    dt.setTimezoneInMinutes(cc.getConfiguration()
419:                            .getImplicitTimezone());
420:                    return (DateTimeValue) dt.adjustTimezone(0);
421:                }
422:            }
423:
424:            /**
425:             * Get the Julian instant: a decimal value whose integer part is the Julian day number
426:             * multiplied by the number of seconds per day,
427:             * and whose fractional part is the fraction of the second.
428:             * This method operates on the local time, ignoring the timezone. The caller should call normalize()
429:             * before calling this method to get a normalized time.
430:             */
431:
432:            public BigDecimal toJulianInstant() {
433:                int julianDay = DateValue.getJulianDayNumber(year, month, day);
434:                long julianSecond = julianDay * (24L * 60L * 60L);
435:                julianSecond += (((hour * 60L + minute) * 60L) + second);
436:                BigDecimal j = new BigDecimal(julianSecond);
437:                if (microsecond == 0) {
438:                    return j;
439:                } else {
440:                    return j.add(new BigDecimal(microsecond).divide(
441:                            DecimalValue.ONE_MILLION, 6,
442:                            BigDecimal.ROUND_HALF_EVEN));
443:                }
444:            }
445:
446:            /**
447:             * Get the DateTimeValue corresponding to a given Julian instant
448:             */
449:
450:            public static DateTimeValue fromJulianInstant(BigDecimal instant) {
451:                BigInteger julianSecond = instant.toBigInteger();
452:                BigDecimal microseconds = instant.subtract(
453:                        new BigDecimal(julianSecond)).multiply(
454:                        DecimalValue.ONE_MILLION);
455:                long js = julianSecond.longValue();
456:                long jd = js / (24L * 60L * 60L);
457:                DateValue date = DateValue.dateFromJulianDayNumber((int) jd);
458:                js = js % (24L * 60L * 60L);
459:                byte hour = (byte) (js / (60L * 60L));
460:                js = js % (60L * 60L);
461:                byte minute = (byte) (js / (60L));
462:                js = js % (60L);
463:                return new DateTimeValue(date.getYear(), date.getMonth(), date
464:                        .getDay(), hour, minute, (byte) js, microseconds
465:                        .intValue(), 0);
466:            }
467:
468:            /**
469:             * Get a Calendar object representing the value of this DateTime. This will respect the timezone
470:             * if there is one, or be in GMT otherwise.
471:             */
472:
473:            public GregorianCalendar getCalendar() {
474:                int tz = (hasTimezone() ? getTimezoneInMinutes() * 60000 : 0);
475:                TimeZone zone = new SimpleTimeZone(tz, "LLL");
476:                GregorianCalendar calendar = new GregorianCalendar(zone);
477:                calendar.setLenient(false);
478:                calendar.set(Math.abs(year), month - 1, day, hour, minute,
479:                        second);
480:                if (year < 0) {
481:                    calendar.set(Calendar.ERA, GregorianCalendar.BC);
482:                }
483:                calendar.set(Calendar.MILLISECOND, microsecond / 1000); // loses precision unavoidably
484:                calendar.set(Calendar.ZONE_OFFSET, tz);
485:                calendar.set(Calendar.DST_OFFSET, 0);
486:                return calendar;
487:            }
488:
489:            /**
490:             * Convert to target data type
491:             * @param requiredType an integer identifying the required atomic type
492:             * @param context
493:             * @return an AtomicValue, a value of the required type; or an ErrorValue
494:             */
495:
496:            public AtomicValue convertPrimitive(BuiltInAtomicType requiredType,
497:                    boolean validate, XPathContext context) {
498:                switch (requiredType.getPrimitiveType()) {
499:                case Type.DATE_TIME:
500:                case Type.ANY_ATOMIC:
501:                case Type.ITEM:
502:                    return this ;
503:
504:                case Type.DATE:
505:                    return new DateValue(year, month, day,
506:                            getTimezoneInMinutes());
507:
508:                case Type.TIME:
509:                    return new TimeValue(hour, minute, second, microsecond,
510:                            getTimezoneInMinutes());
511:
512:                case Type.G_YEAR:
513:                    return new GYearValue(year, getTimezoneInMinutes());
514:
515:                case Type.G_YEAR_MONTH:
516:                    return new GYearMonthValue(year, month,
517:                            getTimezoneInMinutes());
518:
519:                case Type.G_MONTH:
520:                    return new GMonthValue(month, getTimezoneInMinutes());
521:
522:                case Type.G_MONTH_DAY:
523:                    return new GMonthDayValue(month, day,
524:                            getTimezoneInMinutes());
525:
526:                case Type.G_DAY:
527:                    return new GDayValue(day, getTimezoneInMinutes());
528:
529:                case Type.STRING:
530:                    return new StringValue(getStringValueCS());
531:
532:                case Type.UNTYPED_ATOMIC:
533:                    return new UntypedAtomicValue(getStringValueCS());
534:
535:                default:
536:                    ValidationException err = new ValidationException(
537:                            "Cannot convert dateTime to "
538:                                    + requiredType.getDisplayName());
539:                    err.setErrorCode("XPTY0004");
540:                    err.setIsTypeError(true);
541:                    return new ValidationErrorValue(err);
542:                }
543:            }
544:
545:            /**
546:             * Convert to string
547:             * @return ISO 8601 representation. The value returned is the localized representation,
548:             * that is it uses the timezone contained within the value itself.
549:             */
550:
551:            public CharSequence getStringValueCS() {
552:
553:                FastStringBuffer sb = new FastStringBuffer(30);
554:                int yr = year;
555:                if (year <= 0) {
556:                    sb.append('-');
557:                    yr = -yr + 1; // no year zero in lexical space
558:                }
559:                appendString(sb, yr, (yr > 9999 ? (yr + "").length() : 4));
560:                sb.append('-');
561:                appendTwoDigits(sb, month);
562:                sb.append('-');
563:                appendTwoDigits(sb, day);
564:                sb.append('T');
565:                appendTwoDigits(sb, hour);
566:                sb.append(':');
567:                appendTwoDigits(sb, minute);
568:                sb.append(':');
569:                appendTwoDigits(sb, second);
570:                if (microsecond != 0) {
571:                    sb.append('.');
572:                    int ms = microsecond;
573:                    int div = 100000;
574:                    while (ms > 0) {
575:                        int d = ms / div;
576:                        sb.append((char) (d + '0'));
577:                        ms = ms % div;
578:                        div /= 10;
579:                    }
580:                }
581:
582:                if (hasTimezone()) {
583:                    appendTimezone(sb);
584:                }
585:
586:                return sb;
587:
588:            }
589:
590:            /**
591:             * Determine the data type of the exprssion
592:             * @return Type.DATE_TIME,
593:             * @param th
594:             */
595:
596:            public ItemType getItemType(TypeHierarchy th) {
597:                return Type.DATE_TIME_TYPE;
598:            }
599:
600:            /**
601:             * Make a copy of this date, time, or dateTime value
602:             */
603:
604:            public CalendarValue copy() {
605:                return new DateTimeValue(year, month, day, hour, minute,
606:                        second, microsecond, getTimezoneInMinutes());
607:            }
608:
609:            /**
610:             * Return a new dateTime with the same normalized value, but
611:             * in a different timezone. This is called only for a DateTimeValue that has an explicit timezone
612:             * @param timezone the new timezone offset, in minutes
613:             * @return the date/time in the new timezone. This will be a new DateTimeValue unless no change
614:             * was required to the original value
615:             */
616:
617:            public CalendarValue adjustTimezone(int timezone) {
618:                if (!hasTimezone()) {
619:                    CalendarValue in = copy();
620:                    in.setTimezoneInMinutes(timezone);
621:                    return in;
622:                }
623:                int oldtz = getTimezoneInMinutes();
624:                if (oldtz == timezone) {
625:                    return this ;
626:                }
627:                int tz = timezone - oldtz;
628:                int h = hour;
629:                int mi = minute;
630:                mi += tz;
631:                if (mi < 0 || mi > 59) {
632:                    h += Math.floor(mi / 60.0);
633:                    mi = (mi + 60 * 24) % 60;
634:                }
635:
636:                if (h >= 0 && h < 24) {
637:                    return new DateTimeValue(year, month, day, (byte) h,
638:                            (byte) mi, second, microsecond, timezone);
639:                }
640:
641:                // Following code is designed to handle the corner case of adjusting from -14:00 to +14:00 or
642:                // vice versa, which can cause a change of two days in the date
643:                DateTimeValue dt = this ;
644:                while (h < 0) {
645:                    h += 24;
646:                    DateValue t = DateValue.yesterday(dt.getYear(), dt
647:                            .getMonth(), dt.getDay());
648:                    dt = new DateTimeValue(t.getYear(), t.getMonth(), t
649:                            .getDay(), (byte) h, (byte) mi, second,
650:                            microsecond, timezone);
651:                }
652:                while (h > 23) {
653:                    h -= 24;
654:                    DateValue t = DateValue.tomorrow(year, month, day);
655:                    return new DateTimeValue(t.getYear(), t.getMonth(), t
656:                            .getDay(), (byte) h, (byte) mi, second,
657:                            microsecond, timezone);
658:                }
659:                return dt;
660:            }
661:
662:            /**
663:             * Add a duration to a dateTime
664:             * @param duration the duration to be added (may be negative)
665:             * @return the new date
666:             * @throws net.sf.saxon.trans.XPathException if the duration is an xs:duration, as distinct from
667:             * a subclass thereof
668:             */
669:
670:            public CalendarValue add(DurationValue duration)
671:                    throws XPathException {
672:                if (duration instanceof  SecondsDurationValue) {
673:                    long microseconds = ((SecondsDurationValue) duration)
674:                            .getLengthInMicroseconds();
675:                    BigDecimal seconds = new BigDecimal(microseconds).divide(
676:                            DecimalValue.ONE_MILLION, 6,
677:                            BigDecimal.ROUND_HALF_EVEN);
678:                    BigDecimal julian = toJulianInstant();
679:                    julian = julian.add(seconds);
680:                    DateTimeValue dt = fromJulianInstant(julian);
681:                    dt.setTimezoneInMinutes(getTimezoneInMinutes());
682:                    return dt;
683:                } else if (duration instanceof  MonthDurationValue) {
684:                    int months = ((MonthDurationValue) duration)
685:                            .getLengthInMonths();
686:                    int m = (month - 1) + months;
687:                    int y = year + m / 12;
688:                    m = m % 12;
689:                    if (m < 0) {
690:                        m += 12;
691:                        y -= 1;
692:                    }
693:                    m++;
694:                    int d = day;
695:                    while (!DateValue.isValidDate(y, m, d)) {
696:                        d -= 1;
697:                    }
698:                    return new DateTimeValue(y, (byte) m, (byte) d, hour,
699:                            minute, second, microsecond, getTimezoneInMinutes());
700:                } else {
701:                    DynamicError err = new DynamicError(
702:                            "DateTime arithmetic is not supported on xs:duration, only on its subtypes");
703:                    err.setIsTypeError(true);
704:                    throw err;
705:                }
706:            }
707:
708:            /**
709:             * Determine the difference between two points in time, as a duration
710:             * @param other the other point in time
711:             * @param context
712:             * @return the duration as an xdt:dayTimeDuration
713:             * @throws net.sf.saxon.trans.XPathException for example if one value is a date and the other is a time
714:             */
715:
716:            public SecondsDurationValue subtract(CalendarValue other,
717:                    XPathContext context) throws XPathException {
718:                if (!(other instanceof  DateTimeValue)) {
719:                    DynamicError err = new DynamicError(
720:                            "First operand of '-' is a dateTime, but the second is not");
721:                    err.setIsTypeError(true);
722:                    throw err;
723:                }
724:                return super .subtract(other, context);
725:            }
726:
727:            /**
728:             * Convert to Java object (for passing to external functions)
729:             */
730:
731:            public Object convertToJava(Class target, XPathContext context)
732:                    throws XPathException {
733:                if (target.isAssignableFrom(Date.class)) {
734:                    return getCalendar().getTime();
735:                } else if (target.isAssignableFrom(GregorianCalendar.class)) {
736:                    return getCalendar();
737:                } else if (target.isAssignableFrom(DateTimeValue.class)) {
738:                    return this ;
739:                } else if (target == String.class
740:                        || target == CharSequence.class) {
741:                    return getStringValue();
742:                } else if (target == Object.class) {
743:                    return getStringValue();
744:                } else {
745:                    Object o = super .convertToJava(target, context);
746:                    if (o == null) {
747:                        throw new DynamicError("Conversion of dateTime to "
748:                                + target.getName() + " is not supported");
749:                    }
750:                    return o;
751:                }
752:            }
753:
754:            /**
755:             * Get a component of the value. Returns null if the timezone component is
756:             * requested and is not present.
757:             */
758:
759:            public AtomicValue getComponent(int component)
760:                    throws XPathException {
761:                switch (component) {
762:                case Component.YEAR:
763:                    return new IntegerValue((year > 0 ? year : year - 1));
764:                case Component.MONTH:
765:                    return new IntegerValue(month);
766:                case Component.DAY:
767:                    return new IntegerValue(day);
768:                case Component.HOURS:
769:                    return new IntegerValue(hour);
770:                case Component.MINUTES:
771:                    return new IntegerValue(minute);
772:                case Component.SECONDS:
773:                    BigDecimal d = new BigDecimal(microsecond);
774:                    d = d.divide(DecimalValue.ONE_MILLION, 6,
775:                            BigDecimal.ROUND_HALF_UP);
776:                    d = d.add(new BigDecimal(second));
777:                    return new DecimalValue(d);
778:                case Component.MICROSECONDS:
779:                    // internal use only
780:                    return new IntegerValue(microsecond);
781:                case Component.TIMEZONE:
782:                    if (hasTimezone()) {
783:                        return SecondsDurationValue
784:                                .fromMilliseconds(getTimezoneInMinutes() * 60 * 1000);
785:                    } else {
786:                        return null;
787:                    }
788:                default:
789:                    throw new IllegalArgumentException(
790:                            "Unknown component for dateTime: " + component);
791:                }
792:            }
793:
794:            /**
795:             * Compare the value to another dateTime value.
796:             * <p>
797:             * This method is not used for XPath comparisons because it does not have access to the implicitTimezone
798:             * from the dynamic context. It is available for schema comparisons, although it does not currently
799:             * implement the XML Schema semantics for timezone comparison (which involve partial ordering)
800:             * @param other The other dateTime value
801:             * @return negative value if this one is the earler, 0 if they are chronologically equal,
802:             * positive value if this one is the later. For this purpose, dateTime values with an unknown
803:             * timezone are considered to be values in the implicit timezone (the Comparable interface requires
804:             * a total ordering).
805:             * @throws ClassCastException if the other value is not a DateTimeValue (the parameter
806:             * is declared as Object to satisfy the Comparable interface)
807:             */
808:
809:            public int compareTo(Object other) {
810:                // TODO: implement the XML Schema comparison semantics (and remove the gross inefficiency)
811:                if (!(other instanceof  DateTimeValue)) {
812:                    throw new ClassCastException(
813:                            "DateTime values are not comparable to "
814:                                    + other.getClass());
815:                }
816:                return compareTo((DateTimeValue) other,
817:                        new IndependentContext().makeEarlyEvaluationContext());
818:            }
819:
820:            /**
821:             * Compare the value to another dateTime value, following the XPath comparison semantics
822:             * @param other The other dateTime value
823:             * @param cc A ConversionContext used to supply the implicit timezone
824:             * @return negative value if this one is the earler, 0 if they are chronologically equal,
825:             * positive value if this one is the later. For this purpose, dateTime values with an unknown
826:             * timezone are considered to be values in the implicit timezone (the Comparable interface requires
827:             * a total ordering).
828:             * @throws ClassCastException if the other value is not a DateTimeValue (the parameter
829:             * is declared as Object to satisfy the Comparable interface)
830:             */
831:
832:            public int compareTo(CalendarValue other, XPathContext cc) {
833:                if (!(other instanceof  DateTimeValue)) {
834:                    throw new ClassCastException(
835:                            "DateTime values are not comparable to "
836:                                    + other.getClass());
837:                }
838:                DateTimeValue v2 = (DateTimeValue) other;
839:                if (getTimezoneInMinutes() == v2.getTimezoneInMinutes()) {
840:                    // both values are in the same timezone (explicitly or implicitly)
841:                    if (year != v2.year) {
842:                        return year - v2.year;
843:                    }
844:                    if (month != v2.month) {
845:                        return month - v2.month;
846:                    }
847:                    if (day != v2.day) {
848:                        return day - v2.day;
849:                    }
850:                    if (hour != v2.hour) {
851:                        return hour - v2.hour;
852:                    }
853:                    if (minute != v2.minute) {
854:                        return minute - v2.minute;
855:                    }
856:                    if (second != v2.second) {
857:                        return second - v2.second;
858:                    }
859:                    if (microsecond != v2.microsecond) {
860:                        return microsecond - v2.microsecond;
861:                    }
862:                    return 0;
863:                }
864:                return normalize(cc).compareTo(v2.normalize(cc), cc);
865:            }
866:
867:            public boolean equals(Object other) {
868:                return compareTo(other) == 0;
869:            }
870:
871:            public int hashCode() {
872:                if (hasTimezone()) {
873:                    return getCalendar().getTime().hashCode();
874:                } else {
875:                    GregorianCalendar cal = new GregorianCalendar();
876:                    int tz = (cal.get(Calendar.ZONE_OFFSET) + cal
877:                            .get(Calendar.DST_OFFSET)) / 60000;
878:                    DateTimeValue v1 = (DateTimeValue) adjustTimezone(tz);
879:                    return v1.getCalendar().getTime().hashCode();
880:                }
881:            }
882:
883:        }
884:
885:        //
886:        // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
887:        // you may not use this file except in compliance with the License. You may obtain a copy of the
888:        // License at http://www.mozilla.org/MPL/
889:        //
890:        // Software distributed under the License is distributed on an "AS IS" basis,
891:        // WITHOUT WARRANTY OF ANY KIND, either express or implied.
892:        // See the License for the specific language governing rights and limitations under the License.
893:        //
894:        // The Original Code is: all this file.
895:        //
896:        // The Initial Developer of the Original Code is Michael H. Kay
897:        //
898:        // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
899:        //
900:        // Contributor(s): none.
901:        //
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.