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