001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 package org.apache.directory.shared.ldap.util; 021 022 023 import java.text.DecimalFormat; 024 import java.text.NumberFormat; 025 import java.text.ParseException; 026 import java.util.Calendar; 027 import java.util.TimeZone; 028 029 import org.apache.directory.shared.i18n.I18n; 030 031 032 /** 033 * <p>This class represents the generalized time syntax as defined in 034 * RFC 4517 section 3.3.13.</p> 035 * 036 * <p>The date, time and time zone information is internally backed 037 * by an {@link java.util.Calendar} object</p> 038 * 039 * <p>Leap seconds are not supported, as {@link java.util.Calendar} 040 * does not support leap seconds.</p> 041 * 042 * <pre> 043 * 3.3.13. Generalized Time 044 * 045 * A value of the Generalized Time syntax is a character string 046 * representing a date and time. The LDAP-specific encoding of a value 047 * of this syntax is a restriction of the format defined in [ISO8601], 048 * and is described by the following ABNF: 049 * 050 * GeneralizedTime = century year month day hour 051 * [ minute [ second / leap-second ] ] 052 * [ fraction ] 053 * g-time-zone 054 * 055 * century = 2(%x30-39) ; "00" to "99" 056 * year = 2(%x30-39) ; "00" to "99" 057 * month = ( %x30 %x31-39 ) ; "01" (January) to "09" 058 * / ( %x31 %x30-32 ) ; "10" to "12" 059 * day = ( %x30 %x31-39 ) ; "01" to "09" 060 * / ( %x31-32 %x30-39 ) ; "10" to "29" 061 * / ( %x33 %x30-31 ) ; "30" to "31" 062 * hour = ( %x30-31 %x30-39 ) / ( %x32 %x30-33 ) ; "00" to "23" 063 * minute = %x30-35 %x30-39 ; "00" to "59" 064 * 065 * second = ( %x30-35 %x30-39 ) ; "00" to "59" 066 * leap-second = ( %x36 %x30 ) ; "60" 067 * 068 * fraction = ( DOT / COMMA ) 1*(%x30-39) 069 * g-time-zone = %x5A ; "Z" 070 * / g-differential 071 * g-differential = ( MINUS / PLUS ) hour [ minute ] 072 * MINUS = %x2D ; minus sign ("-") 073 * 074 * The <DOT>, <COMMA>, and <PLUS> rules are defined in [RFC4512]. 075 * 076 * The above ABNF allows character strings that do not represent valid 077 * dates (in the Gregorian calendar) and/or valid times (e.g., February 078 * 31, 1994). Such character strings SHOULD be considered invalid for 079 * this syntax. 080 * 081 * The time value represents coordinated universal time (equivalent to 082 * Greenwich Mean Time) if the "Z" form of <g-time-zone> is used; 083 * otherwise, the value represents a local time in the time zone 084 * indicated by <g-differential>. In the latter case, coordinated 085 * universal time can be calculated by subtracting the differential from 086 * the local time. The "Z" form of <g-time-zone> SHOULD be used in 087 * preference to <g-differential>. 088 * 089 * If <minute> is omitted, then <fraction> represents a fraction of an 090 * hour; otherwise, if <second> and <leap-second> are omitted, then 091 * <fraction> represents a fraction of a minute; otherwise, <fraction> 092 * represents a fraction of a second. 093 * 094 * Examples: 095 * 199412161032Z 096 * 199412160532-0500 097 * 098 * Both example values represent the same coordinated universal time: 099 * 10:32 AM, December 16, 1994. 100 * 101 * The LDAP definition for the Generalized Time syntax is: 102 * 103 * ( 1.3.6.1.4.1.1466.115.121.1.24 DESC 'Generalized Time' ) 104 * 105 * This syntax corresponds to the GeneralizedTime ASN.1 type from 106 * [ASN.1], with the constraint that local time without a differential 107 * SHALL NOT be used. 108 * 109 * </pre> 110 */ 111 public class GeneralizedTime implements Comparable<GeneralizedTime> 112 { 113 114 public enum Format 115 { 116 YEAR_MONTH_DAY_HOUR_MIN_SEC, YEAR_MONTH_DAY_HOUR_MIN_SEC_FRACTION, 117 118 YEAR_MONTH_DAY_HOUR_MIN, YEAR_MONTH_DAY_HOUR_MIN_FRACTION, 119 120 YEAR_MONTH_DAY_HOUR, YEAR_MONTH_DAY_HOUR_FRACTION, ; 121 } 122 123 public enum FractionDelimiter 124 { 125 DOT, COMMA 126 } 127 128 public enum TimeZoneFormat 129 { 130 Z, DIFF_HOUR, DIFF_HOUR_MINUTE; 131 } 132 133 private static final TimeZone GMT = TimeZone.getTimeZone( "GMT" ); 134 135 /** The user provided value */ 136 private String upGeneralizedTime; 137 138 /** The user provided format */ 139 private Format upFormat; 140 141 /** The user provided time zone format */ 142 private TimeZoneFormat upTimeZoneFormat; 143 144 /** The user provided fraction delimiter */ 145 private FractionDelimiter upFractionDelimiter; 146 147 /** the user provided fraction length */ 148 private int upFractionLength; 149 150 /** The calendar */ 151 private Calendar calendar; 152 153 154 /** 155 * Creates a new instance of GeneralizedTime, based on the given Calendar object. 156 * Uses <pre>Format.YEAR_MONTH_DAY_HOUR_MIN_SEC</pre> as default format and 157 * <pre>TimeZoneFormat.Z</pre> as default time zone format. 158 * 159 * @param calendar the calendar containing the date, time and timezone information 160 */ 161 public GeneralizedTime( Calendar calendar ) 162 { 163 if ( calendar == null ) 164 { 165 throw new IllegalArgumentException( I18n.err( I18n.ERR_04358 ) ); 166 } 167 168 this.calendar = calendar; 169 upGeneralizedTime = null; 170 upFormat = Format.YEAR_MONTH_DAY_HOUR_MIN_SEC; 171 upTimeZoneFormat = TimeZoneFormat.Z; 172 upFractionDelimiter = FractionDelimiter.DOT; 173 upFractionLength = 3; 174 } 175 176 177 /** 178 * Creates a new instance of GeneralizedTime, based on the 179 * given generalized time string. 180 * 181 * @param generalizedTime the generalized time 182 * 183 * @throws ParseException if the given generalized time can't be parsed. 184 */ 185 public GeneralizedTime( String generalizedTime ) throws ParseException 186 { 187 if ( generalizedTime == null ) 188 { 189 throw new ParseException( I18n.err( I18n.ERR_04359 ), 0 ); 190 } 191 192 this.upGeneralizedTime = generalizedTime; 193 194 calendar = Calendar.getInstance(); 195 calendar.setTimeInMillis( 0 ); 196 calendar.setLenient( false ); 197 198 parseYear(); 199 parseMonth(); 200 parseDay(); 201 parseHour(); 202 203 if ( upGeneralizedTime.length() < 11 ) 204 { 205 throw new ParseException( I18n.err( I18n.ERR_04360 ), 10 ); 206 } 207 208 // pos 10: 209 // if digit => minute field 210 // if . or , => fraction of hour field 211 // if Z or + or - => timezone field 212 // else error 213 int pos = 10; 214 char c = upGeneralizedTime.charAt( pos ); 215 if ( '0' <= c && c <= '9' ) 216 { 217 parseMinute(); 218 219 if ( upGeneralizedTime.length() < 13 ) 220 { 221 throw new ParseException( I18n.err( I18n.ERR_04361 ), 12 ); 222 } 223 224 // pos 12: 225 // if digit => second field 226 // if . or , => fraction of minute field 227 // if Z or + or - => timezone field 228 // else error 229 pos = 12; 230 c = upGeneralizedTime.charAt( pos ); 231 if ( '0' <= c && c <= '9' ) 232 { 233 parseSecond(); 234 235 if ( upGeneralizedTime.length() < 15 ) 236 { 237 throw new ParseException( I18n.err( I18n.ERR_04362 ), 14 ); 238 } 239 240 // pos 14: 241 // if . or , => fraction of second field 242 // if Z or + or - => timezone field 243 // else error 244 pos = 14; 245 c = upGeneralizedTime.charAt( pos ); 246 if ( c == '.' || c == ',' ) 247 { 248 // read fraction of second 249 parseFractionOfSecond(); 250 pos += 1 + upFractionLength; 251 252 parseTimezone( pos ); 253 upFormat = Format.YEAR_MONTH_DAY_HOUR_MIN_SEC_FRACTION; 254 } 255 else if ( c == 'Z' || c == '+' || c == '-' ) 256 { 257 // read timezone 258 parseTimezone( pos ); 259 upFormat = Format.YEAR_MONTH_DAY_HOUR_MIN_SEC; 260 } 261 else 262 { 263 throw new ParseException( I18n.err( I18n.ERR_04363 ), 14 ); 264 } 265 } 266 else if ( c == '.' || c == ',' ) 267 { 268 // read fraction of minute 269 parseFractionOfMinute(); 270 pos += 1 + upFractionLength; 271 272 parseTimezone( pos ); 273 upFormat = Format.YEAR_MONTH_DAY_HOUR_MIN_FRACTION; 274 } 275 else if ( c == 'Z' || c == '+' || c == '-' ) 276 { 277 // read timezone 278 parseTimezone( pos ); 279 upFormat = Format.YEAR_MONTH_DAY_HOUR_MIN; 280 } 281 else 282 { 283 throw new ParseException( I18n.err( I18n.ERR_04364 ), 12 ); 284 } 285 } 286 else if ( c == '.' || c == ',' ) 287 { 288 // read fraction of hour 289 parseFractionOfHour(); 290 pos += 1 + upFractionLength; 291 292 parseTimezone( pos ); 293 upFormat = Format.YEAR_MONTH_DAY_HOUR_FRACTION; 294 } 295 else if ( c == 'Z' || c == '+' || c == '-' ) 296 { 297 // read timezone 298 parseTimezone( pos ); 299 upFormat = Format.YEAR_MONTH_DAY_HOUR; 300 } 301 else 302 { 303 throw new ParseException( I18n.err( I18n.ERR_04365 ), 10 ); 304 } 305 306 // this calculates and verifies the calendar 307 try 308 { 309 calendar.getTimeInMillis(); 310 } 311 catch ( IllegalArgumentException iae ) 312 { 313 throw new ParseException( I18n.err( I18n.ERR_04366 ), 0 ); 314 } 315 316 calendar.setLenient( true ); 317 } 318 319 320 private void parseTimezone( int pos ) throws ParseException 321 { 322 if ( upGeneralizedTime.length() < pos + 1 ) 323 { 324 throw new ParseException( I18n.err( I18n.ERR_04367 ), pos ); 325 } 326 327 char c = upGeneralizedTime.charAt( pos ); 328 if ( c == 'Z' ) 329 { 330 calendar.setTimeZone( GMT ); 331 upTimeZoneFormat = TimeZoneFormat.Z; 332 333 if ( upGeneralizedTime.length() > pos + 1 ) 334 { 335 throw new ParseException( I18n.err( I18n.ERR_04368 ), pos + 1 ); 336 } 337 } 338 else if ( c == '+' || c == '-' ) 339 { 340 StringBuilder sb = new StringBuilder( "GMT" ); 341 sb.append( c ); 342 343 String digits = getAllDigits( pos + 1 ); 344 sb.append( digits ); 345 346 if ( digits.length() == 2 && digits.matches( "^([01]\\d|2[0-3])$" ) ) 347 { 348 TimeZone timeZone = TimeZone.getTimeZone( sb.toString() ); 349 calendar.setTimeZone( timeZone ); 350 upTimeZoneFormat = TimeZoneFormat.DIFF_HOUR; 351 } 352 else if ( digits.length() == 4 && digits.matches( "^([01]\\d|2[0-3])([0-5]\\d)$" ) ) 353 { 354 TimeZone timeZone = TimeZone.getTimeZone( sb.toString() ); 355 calendar.setTimeZone( timeZone ); 356 upTimeZoneFormat = TimeZoneFormat.DIFF_HOUR_MINUTE; 357 } 358 else 359 { 360 throw new ParseException( I18n.err( I18n.ERR_04369 ), pos ); 361 } 362 363 if ( upGeneralizedTime.length() > pos + 1 + digits.length() ) 364 { 365 throw new ParseException( I18n.err( I18n.ERR_04370 ), pos + 1 + digits.length() ); 366 } 367 } 368 } 369 370 371 private void parseFractionOfSecond() throws ParseException 372 { 373 parseFractionDelmiter( 14 ); 374 String fraction = getFraction( 14 + 1 ); 375 upFractionLength = fraction.length(); 376 377 double fract = Double.parseDouble( "0." + fraction ); 378 int millisecond = ( int ) Math.round( fract * 1000 ); 379 380 calendar.set( Calendar.MILLISECOND, millisecond ); 381 } 382 383 384 private void parseFractionOfMinute() throws ParseException 385 { 386 parseFractionDelmiter( 12 ); 387 String fraction = getFraction( 12 + 1 ); 388 upFractionLength = fraction.length(); 389 390 double fract = Double.parseDouble( "0." + fraction ); 391 int milliseconds = ( int ) Math.round( fract * 1000 * 60 ); 392 int second = milliseconds / 1000; 393 int millisecond = milliseconds - ( second * 1000 ); 394 395 calendar.set( Calendar.SECOND, second ); 396 calendar.set( Calendar.MILLISECOND, millisecond ); 397 } 398 399 400 private void parseFractionOfHour() throws ParseException 401 { 402 parseFractionDelmiter( 10 ); 403 String fraction = getFraction( 10 + 1 ); 404 upFractionLength = fraction.length(); 405 406 double fract = Double.parseDouble( "0." + fraction ); 407 int milliseconds = ( int ) Math.round( fract * 1000 * 60 * 60 ); 408 int minute = milliseconds / ( 1000 * 60 ); 409 int second = ( milliseconds - ( minute * 60 * 1000 ) ) / 1000; 410 int millisecond = milliseconds - ( minute * 60 * 1000 ) - ( second * 1000 ); 411 412 calendar.set( Calendar.MINUTE, minute ); 413 calendar.set( Calendar.SECOND, second ); 414 calendar.set( Calendar.MILLISECOND, millisecond ); 415 } 416 417 418 private void parseFractionDelmiter( int fractionDelimiterPos ) 419 { 420 char c = upGeneralizedTime.charAt( fractionDelimiterPos ); 421 upFractionDelimiter = c == '.' ? FractionDelimiter.DOT : FractionDelimiter.COMMA; 422 } 423 424 425 private String getFraction( int startIndex ) throws ParseException 426 { 427 String fraction = getAllDigits( startIndex ); 428 429 // minimum one digit 430 if ( fraction.length() == 0 ) 431 { 432 throw new ParseException( I18n.err( I18n.ERR_04371 ), startIndex ); 433 } 434 435 return fraction; 436 } 437 438 439 private String getAllDigits( int startIndex ) 440 { 441 StringBuilder sb = new StringBuilder(); 442 while ( upGeneralizedTime.length() > startIndex ) 443 { 444 char c = upGeneralizedTime.charAt( startIndex ); 445 if ( '0' <= c && c <= '9' ) 446 { 447 sb.append( c ); 448 startIndex++; 449 } 450 else 451 { 452 break; 453 } 454 } 455 return sb.toString(); 456 } 457 458 459 private void parseSecond() throws ParseException 460 { 461 // read minute 462 if ( upGeneralizedTime.length() < 14 ) 463 { 464 throw new ParseException( I18n.err( I18n.ERR_04372 ), 12 ); 465 } 466 try 467 { 468 int second = Integer.parseInt( upGeneralizedTime.substring( 12, 14 ) ); 469 calendar.set( Calendar.SECOND, second ); 470 } 471 catch ( NumberFormatException e ) 472 { 473 throw new ParseException( I18n.err( I18n.ERR_04373 ), 12 ); 474 } 475 } 476 477 478 private void parseMinute() throws ParseException 479 { 480 // read minute 481 if ( upGeneralizedTime.length() < 12 ) 482 { 483 throw new ParseException( I18n.err( I18n.ERR_04374 ), 10 ); 484 } 485 try 486 { 487 int minute = Integer.parseInt( upGeneralizedTime.substring( 10, 12 ) ); 488 calendar.set( Calendar.MINUTE, minute ); 489 } 490 catch ( NumberFormatException e ) 491 { 492 throw new ParseException( I18n.err( I18n.ERR_04375 ), 10 ); 493 } 494 } 495 496 497 private void parseHour() throws ParseException 498 { 499 if ( upGeneralizedTime.length() < 10 ) 500 { 501 throw new ParseException( I18n.err( I18n.ERR_04376 ), 8 ); 502 } 503 try 504 { 505 int hour = Integer.parseInt( upGeneralizedTime.substring( 8, 10 ) ); 506 calendar.set( Calendar.HOUR_OF_DAY, hour ); 507 } 508 catch ( NumberFormatException e ) 509 { 510 throw new ParseException( I18n.err( I18n.ERR_04377 ), 8 ); 511 } 512 } 513 514 515 private void parseDay() throws ParseException 516 { 517 if ( upGeneralizedTime.length() < 8 ) 518 { 519 throw new ParseException( I18n.err( I18n.ERR_04378 ), 6 ); 520 } 521 try 522 { 523 int day = Integer.parseInt( upGeneralizedTime.substring( 6, 8 ) ); 524 calendar.set( Calendar.DAY_OF_MONTH, day ); 525 } 526 catch ( NumberFormatException e ) 527 { 528 throw new ParseException( I18n.err( I18n.ERR_04379 ), 6 ); 529 } 530 } 531 532 533 private void parseMonth() throws ParseException 534 { 535 if ( upGeneralizedTime.length() < 6 ) 536 { 537 throw new ParseException( I18n.err( I18n.ERR_04380 ), 4 ); 538 } 539 try 540 { 541 int month = Integer.parseInt( upGeneralizedTime.substring( 4, 6 ) ); 542 calendar.set( Calendar.MONTH, month - 1 ); 543 } 544 catch ( NumberFormatException e ) 545 { 546 throw new ParseException( I18n.err( I18n.ERR_04381 ), 4 ); 547 } 548 } 549 550 551 private void parseYear() throws ParseException 552 { 553 if ( upGeneralizedTime.length() < 4 ) 554 { 555 throw new ParseException( I18n.err( I18n.ERR_04382 ), 0 ); 556 } 557 try 558 { 559 int year = Integer.parseInt( upGeneralizedTime.substring( 0, 4 ) ); 560 calendar.set( Calendar.YEAR, year ); 561 } 562 catch ( NumberFormatException e ) 563 { 564 throw new ParseException( I18n.err( I18n.ERR_04383 ), 0 ); 565 } 566 } 567 568 569 /** 570 * Returns the string representation of this generalized time. 571 * This method uses the same format as the user provided format. 572 * 573 * @return the string representation of this generalized time 574 */ 575 public String toGeneralizedTime() 576 { 577 return toGeneralizedTime( upFormat, upFractionDelimiter, upFractionLength, upTimeZoneFormat ); 578 } 579 580 581 /** 582 * Returns the string representation of this generalized time. 583 * 584 * @param format the target format 585 * @param fractionDelimiter the target fraction delimiter, may be null 586 * @param fractionLenth the fraction length 587 * @param timeZoneFormat the target time zone format 588 * 589 * @return the string 590 */ 591 public String toGeneralizedTime( Format format, FractionDelimiter fractionDelimiter, int fractionLength, 592 TimeZoneFormat timeZoneFormat ) 593 { 594 Calendar calendar = ( Calendar ) this.calendar.clone(); 595 if ( timeZoneFormat == TimeZoneFormat.Z ) 596 { 597 calendar.setTimeZone( GMT ); 598 } 599 600 NumberFormat twoDigits = new DecimalFormat( "00" ); 601 NumberFormat fourDigits = new DecimalFormat( "00" ); 602 String fractionFormat = ""; 603 for ( int i = 0; i < fractionLength && i < 3; i++ ) 604 { 605 fractionFormat += "0"; 606 } 607 608 StringBuilder sb = new StringBuilder(); 609 sb.append( fourDigits.format( calendar.get( Calendar.YEAR ) ) ); 610 sb.append( twoDigits.format( calendar.get( Calendar.MONTH ) + 1 ) ); 611 sb.append( twoDigits.format( calendar.get( Calendar.DAY_OF_MONTH ) ) ); 612 sb.append( twoDigits.format( calendar.get( Calendar.HOUR_OF_DAY ) ) ); 613 614 switch ( format ) 615 { 616 case YEAR_MONTH_DAY_HOUR_MIN_SEC: 617 sb.append( twoDigits.format( calendar.get( Calendar.MINUTE ) ) ); 618 sb.append( twoDigits.format( calendar.get( Calendar.SECOND ) ) ); 619 break; 620 621 case YEAR_MONTH_DAY_HOUR_MIN_SEC_FRACTION: 622 sb.append( twoDigits.format( calendar.get( Calendar.MINUTE ) ) ); 623 sb.append( twoDigits.format( calendar.get( Calendar.SECOND ) ) ); 624 625 NumberFormat fractionDigits = new DecimalFormat( fractionFormat ); 626 sb.append( fractionDelimiter == FractionDelimiter.COMMA ? ',' : '.' ); 627 sb.append( fractionDigits.format( calendar.get( Calendar.MILLISECOND ) ) ); 628 break; 629 630 case YEAR_MONTH_DAY_HOUR_MIN: 631 sb.append( twoDigits.format( calendar.get( Calendar.MINUTE ) ) ); 632 break; 633 634 case YEAR_MONTH_DAY_HOUR_MIN_FRACTION: 635 sb.append( twoDigits.format( calendar.get( Calendar.MINUTE ) ) ); 636 637 // sec + millis => fraction of minute 638 double millisec = 1000 * calendar.get( Calendar.SECOND ) + calendar.get( Calendar.MILLISECOND ); 639 double fraction = millisec / ( 1000 * 60 ); 640 fractionDigits = new DecimalFormat( "0." + fractionFormat ); 641 sb.append( fractionDelimiter == FractionDelimiter.COMMA ? ',' : '.' ); 642 sb.append( fractionDigits.format( fraction ).substring( 2 ) ); 643 break; 644 645 case YEAR_MONTH_DAY_HOUR_FRACTION: 646 // min + sec + millis => fraction of minute 647 millisec = 1000 * 60 * calendar.get( Calendar.MINUTE ) + 1000 * calendar.get( Calendar.SECOND ) 648 + calendar.get( Calendar.MILLISECOND ); 649 fraction = millisec / ( 1000 * 60 * 60 ); 650 fractionDigits = new DecimalFormat( "0." + fractionFormat ); 651 sb.append( fractionDelimiter == FractionDelimiter.COMMA ? ',' : '.' ); 652 sb.append( fractionDigits.format( fraction ).substring( 2 ) ); 653 654 break; 655 } 656 657 if ( timeZoneFormat == TimeZoneFormat.Z && calendar.getTimeZone().hasSameRules( GMT ) ) 658 { 659 sb.append( 'Z' ); 660 } 661 else 662 { 663 TimeZone timeZone = calendar.getTimeZone(); 664 int rawOffset = timeZone.getRawOffset(); 665 sb.append( rawOffset < 0 ? '-' : '+' ); 666 667 rawOffset = Math.abs( rawOffset ); 668 int hour = rawOffset / ( 60 * 60 * 1000 ); 669 int minute = ( rawOffset - ( hour * 60 * 60 * 1000 ) ) / ( 1000 * 60 ); 670 671 if ( hour < 10 ) 672 { 673 sb.append( '0' ); 674 } 675 sb.append( hour ); 676 677 if ( timeZoneFormat == TimeZoneFormat.DIFF_HOUR_MINUTE || timeZoneFormat == TimeZoneFormat.Z ) 678 { 679 if ( minute < 10 ) 680 { 681 sb.append( '0' ); 682 } 683 sb.append( minute ); 684 } 685 } 686 687 return sb.toString(); 688 } 689 690 691 /** 692 * Gets the calendar. It could be used to manipulate this 693 * {@link GeneralizedTime} settings. 694 * 695 * @return the calendar 696 */ 697 public Calendar getCalendar() 698 { 699 return calendar; 700 } 701 702 703 @Override 704 public String toString() 705 { 706 return toGeneralizedTime(); 707 } 708 709 710 @Override 711 public int hashCode() 712 { 713 final int prime = 31; 714 int result = 1; 715 result = prime * result + calendar.hashCode(); 716 return result; 717 } 718 719 720 @Override 721 public boolean equals( Object obj ) 722 { 723 if ( obj instanceof GeneralizedTime ) 724 { 725 GeneralizedTime other = ( GeneralizedTime ) obj; 726 return calendar.equals( other.calendar ); 727 } 728 else 729 { 730 return false; 731 } 732 } 733 734 735 /** 736 * Compares this GeneralizedTime object with the specified GeneralizedTime object. 737 * 738 * @param other the other GeneralizedTime object 739 * 740 * @return a negative integer, zero, or a positive integer as this object 741 * is less than, equal to, or greater than the specified object. 742 * 743 * @see java.lang.Comparable#compareTo(java.lang.Object) 744 */ 745 public int compareTo( GeneralizedTime other ) 746 { 747 return calendar.compareTo( other.calendar ); 748 } 749 750 }