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