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