Format a calendar instance that is parseable from JavaScript, according to ISO-8601.

@param cal the calendar to format to a JSON string
@return a formatted date in the form of a string
540471
public static String toJson(Calendar cal) {
        if (cal == null) {
            return NULL_VALUE;
        }

        CharBuf buffer = CharBuf.create(26);
        writeDate(cal.getTime(), buffer);

        return buffer.toString();
    }
1010579
private static long parseIso8601(String date) {
        SimpleDateFormat formatter = new SimpleDateFormat(
                "yyyyMMdd'T'HHmmss'Z'");
        formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
        logger.debug("8601date {}", date);
        try {
            return formatter.parse(date).getTime() / 1000;
        } catch (ParseException pe) {
            throw new IllegalArgumentException(pe);
        }
    }
1033381
@Override
    public void parse(String text, Calendar cal, ParsePosition parsePos)
    {
        TimeZone backupTZ = null;
        Calendar resultCal = null;
        if (cal != calendar && !cal.getType().equals(calendar.getType())) {
            // Different calendar type
            // We use the time/zone from the input calendar, but
            // do not use the input calendar for field calculation.
            calendar.setTimeInMillis(cal.getTimeInMillis());
            backupTZ = calendar.getTimeZone();
            calendar.setTimeZone(cal.getTimeZone());
            resultCal = cal;
            cal = calendar;
        }

        int pos = parsePos.getIndex();
        if(pos < 0) {
            parsePos.setErrorIndex(0);
            return;
        }
        int start = pos;

        // Hold the day period until everything else is parsed, because we need
        // the hour to interpret time correctly.
        // Using an one-element array for output parameter.
        Output<DayPeriodRules.DayPeriod> dayPeriod = new Output<DayPeriodRules.DayPeriod>(null);

        Output<TimeType> tzTimeType = new Output<TimeType>(TimeType.UNKNOWN);
        boolean[] ambiguousYear = { false };

        // item index for the first numeric field within a contiguous numeric run
        int numericFieldStart = -1;
        // item length for the first numeric field within a contiguous numeric run
        int numericFieldLength = 0;
        // start index of numeric text run in the input text
        int numericStartPos = 0;

        MessageFormat numericLeapMonthFormatter = null;
        if (formatData.leapMonthPatterns != null && formatData.leapMonthPatterns.length >= DateFormatSymbols.DT_MONTH_PATTERN_COUNT) {
            numericLeapMonthFormatter = new MessageFormat(formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_NUMERIC], locale);
        }

        Object[] items = getPatternItems();
        int i = 0;
        while (i < items.length) {
            if (items[i] instanceof PatternItem) {
                // Handle pattern field
                PatternItem field = (PatternItem)items[i];
                if (field.isNumeric) {
                    // Handle fields within a run of abutting numeric fields.  Take
                    // the pattern "HHmmss" as an example. We will try to parse
                    // 2/2/2 characters of the input text, then if that fails,
                    // 1/2/2.  We only adjust the width of the leftmost field; the
                    // others remain fixed.  This allows "123456" => 12:34:56, but
                    // "12345" => 1:23:45.  Likewise, for the pattern "yyyyMMdd" we
                    // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
                    if (numericFieldStart == -1) {
                        // check if this field is followed by abutting another numeric field
                        if ((i + 1) < items.length
                                && (items[i + 1] instanceof PatternItem)
                                && ((PatternItem)items[i + 1]).isNumeric) {
                            // record the first numeric field within a numeric text run
                            numericFieldStart = i;
                            numericFieldLength = field.length;
                            numericStartPos = pos;
                        }
                    }
                }
                if (numericFieldStart != -1) {
                    // Handle a numeric field within abutting numeric fields
                    int len = field.length;
                    if (numericFieldStart == i) {
                        len = numericFieldLength;
                    }

                    // Parse a numeric field
                    pos = subParse(text, pos, field.type, len,
                            true, false, ambiguousYear, cal, numericLeapMonthFormatter, tzTimeType);

                    if (pos < 0) {
                        // If the parse fails anywhere in the numeric run, back up to the
                        // start of the run and use shorter pattern length for the first
                        // numeric field.
                        --numericFieldLength;
                        if (numericFieldLength == 0) {
                            // can not make shorter any more
                            parsePos.setIndex(start);
                            parsePos.setErrorIndex(pos);
                            if (backupTZ != null) {
                                calendar.setTimeZone(backupTZ);
                            }
                            return;
                        }
                        i = numericFieldStart;
                        pos = numericStartPos;
                        continue;
                    }

                } else if (field.type != 'l') { // (SMALL LETTER L) obsolete pattern char just gets ignored
                    // Handle a non-numeric field or a non-abutting numeric field
                    numericFieldStart = -1;

                    int s = pos;
                    pos = subParse(text, pos, field.type, field.length,
                            false, true, ambiguousYear, cal, numericLeapMonthFormatter, tzTimeType, dayPeriod);

                    if (pos < 0) {
                        if (pos == ISOSpecialEra) {
                            // era not present, in special cases allow this to continue
                            pos = s;

                            if (i+1 < items.length) {

                                String patl = null;
                                // if it will cause a class cast exception to String, we can't use it
                                try {
                                    patl = (String)items[i+1];
                                } catch(ClassCastException cce) {
                                    parsePos.setIndex(start);
                                    parsePos.setErrorIndex(s);
                                    if (backupTZ != null) {
                                        calendar.setTimeZone(backupTZ);
                                    }
                                    return;
                                }

                                // get next item in pattern
                                if(patl == null)
                                    patl = (String)items[i+1];
                                int plen = patl.length();
                                int idx=0;

                                // White space characters found in pattern.
                                // Skip contiguous white spaces.
                                while (idx < plen) {

                                    char pch = patl.charAt(idx);
                                    if (PatternProps.isWhiteSpace(pch))
                                        idx++;
                                    else
                                        break;
                                }

                                // if next item in pattern is all whitespace, skip it
                                if (idx == plen) {
                                    i++;
                                }

                            }
                        } else {
                            parsePos.setIndex(start);
                            parsePos.setErrorIndex(s);
                            if (backupTZ != null) {
                                calendar.setTimeZone(backupTZ);
                            }
                            return;
                        }
                    }

                }
            } else {
                // Handle literal pattern text literal
                numericFieldStart = -1;
                boolean[] complete = new boolean[1];
                pos = matchLiteral(text, pos, items, i, complete);
                if (!complete[0]) {
                    // Set the position of mismatch
                    parsePos.setIndex(start);
                    parsePos.setErrorIndex(pos);
                    if (backupTZ != null) {
                        calendar.setTimeZone(backupTZ);
                    }
                    return;
                }
            }
            ++i;
        }

        // Special hack for trailing "." after non-numeric field.
        if (pos < text.length()) {
            char extra = text.charAt(pos);
            if (extra == '.' && getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_WHITESPACE) && items.length != 0) {
                // only do if the last field is not numeric
                Object lastItem = items[items.length - 1];
                if (lastItem instanceof PatternItem && !((PatternItem)lastItem).isNumeric) {
                    pos++; // skip the extra "."
                }
            }
        }

        // If dayPeriod is set, use it in conjunction with hour-of-day to determine am/pm.
        if (dayPeriod.value != null) {
            DayPeriodRules ruleSet = DayPeriodRules.getInstance(getLocale());

            if (!cal.isSet(Calendar.HOUR) && !cal.isSet(Calendar.HOUR_OF_DAY)) {
                // If hour is not set, set time to the midpoint of current day period, overwriting
                // minutes if it's set.
                double midPoint = ruleSet.getMidPointForDayPeriod(dayPeriod.value);

                // Truncate midPoint toward zero to get the hour.
                // Any leftover means it was a half-hour.
                int midPointHour = (int) midPoint;
                int midPointMinute = (midPoint - midPointHour) > 0 ? 30 : 0;

                // No need to set am/pm because hour-of-day is set last therefore takes precedence.
                cal.set(Calendar.HOUR_OF_DAY, midPointHour);
                cal.set(Calendar.MINUTE, midPointMinute);
            } else {
                int hourOfDay;

                if (cal.isSet(Calendar.HOUR_OF_DAY)) {  // Hour is parsed in 24-hour format.
                    hourOfDay = cal.get(Calendar.HOUR_OF_DAY);
                } else {  // Hour is parsed in 12-hour format.
                    hourOfDay = cal.get(Calendar.HOUR);
                    // cal.get() turns 12 to 0 for 12-hour time; change 0 to 12
                    // so 0 unambiguously means a 24-hour time from above.
                    if (hourOfDay == 0) { hourOfDay = 12; }
                }
                assert(0 <= hourOfDay && hourOfDay <= 23);


                // If hour-of-day is 0 or 13 thru 23 then input time in unambiguously in 24-hour format.
                if (hourOfDay == 0 || (13 <= hourOfDay && hourOfDay <= 23)) {
                    // Make hour-of-day take precedence over (hour + am/pm) by setting it again.
                    cal.set(Calendar.HOUR_OF_DAY, hourOfDay);
                } else {
                    // We have a 12-hour time and need to choose between am and pm.
                    // Behave as if dayPeriod spanned 6 hours each way from its center point.
                    // This will parse correctly for consistent time + period (e.g. 10 at night) as
                    // well as provide a reasonable recovery for inconsistent time + period (e.g.
                    // 9 in the afternoon).

                    // Assume current time is in the AM.
                    // - Change 12 back to 0 for easier handling of 12am.
                    // - Append minutes as fractional hours because e.g. 8:15 and 8:45 could be parsed
                    // into different half-days if center of dayPeriod is at 14:30.
                    // - cal.get(MINUTE) will return 0 if MINUTE is unset, which works.
                    if (hourOfDay == 12) { hourOfDay = 0; }
                    double currentHour = hourOfDay + cal.get(Calendar.MINUTE) / 60.0;
                    double midPointHour = ruleSet.getMidPointForDayPeriod(dayPeriod.value);

                    double hoursAheadMidPoint = currentHour - midPointHour;

                    // Assume current time is in the AM.
                    if (-6 <= hoursAheadMidPoint && hoursAheadMidPoint < 6) {
                        // Assumption holds; set time as such.
                        cal.set(Calendar.AM_PM, 0);
                    } else {
                        cal.set(Calendar.AM_PM, 1);
                    }
                }
            }
        }

        // At this point the fields of Calendar have been set.  Calendar
        // will fill in default values for missing fields when the time
        // is computed.

        parsePos.setIndex(pos);

        // This part is a problem:  When we call parsedDate.after, we compute the time.
        // Take the date April 3 2004 at 2:30 am.  When this is first set up, the year
        // will be wrong if we're parsing a 2-digit year pattern.  It will be 1904.
        // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day.  2:30 am
        // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
        // on that day.  It is therefore parsed out to fields as 3:30 am.  Then we
        // add 100 years, and get April 3 2004 at 3:30 am.  Note that April 3 2004 is
        // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
        /*
          Date parsedDate = cal.getTime();
          if( ambiguousYear[0] && !parsedDate.after(getDefaultCenturyStart()) ) {
          cal.add(Calendar.YEAR, 100);
          parsedDate = cal.getTime();
          }
        */
        // Because of the above condition, save off the fields in case we need to readjust.
        // The procedure we use here is not particularly efficient, but there is no other
        // way to do this given the API restrictions present in Calendar.  We minimize
        // inefficiency by only performing this computation when it might apply, that is,
        // when the two-digit year is equal to the start year, and thus might fall at the
        // front or the back of the default century.  This only works because we adjust
        // the year correctly to start with in other cases -- see subParse().
        try {
            TimeType tztype = tzTimeType.value;
            if (ambiguousYear[0] || tztype != TimeType.UNKNOWN) {
                // We need a copy of the fields, and we need to avoid triggering a call to
                // complete(), which will recalculate the fields.  Since we can't access
                // the fields[] array in Calendar, we clone the entire object.  This will
                // stop working if Calendar.clone() is ever rewritten to call complete().
                Calendar copy;
                if (ambiguousYear[0]) { // the two-digit year == the default start year
                    copy = (Calendar)cal.clone();
                    Date parsedDate = copy.getTime();
                    if (parsedDate.before(getDefaultCenturyStart())) {
                        // We can't use add here because that does a complete() first.
                        cal.set(Calendar.YEAR, getDefaultCenturyStartYear() + 100);
                    }
                }
                if (tztype != TimeType.UNKNOWN) {
                    copy = (Calendar)cal.clone();
                    TimeZone tz = copy.getTimeZone();
                    BasicTimeZone btz = null;
                    if (tz instanceof BasicTimeZone) {
                        btz = (BasicTimeZone)tz;
                    }

                    // Get local millis
                    copy.set(Calendar.ZONE_OFFSET, 0);
                    copy.set(Calendar.DST_OFFSET, 0);
                    long localMillis = copy.getTimeInMillis();

                    // Make sure parsed time zone type (Standard or Daylight)
                    // matches the rule used by the parsed time zone.
                    int[] offsets = new int[2];
                    if (btz != null) {
                        if (tztype == TimeType.STANDARD) {
                            btz.getOffsetFromLocal(localMillis,
                                    BasicTimeZone.LOCAL_STD, BasicTimeZone.LOCAL_STD, offsets);
                        } else {
                            btz.getOffsetFromLocal(localMillis,
                                    BasicTimeZone.LOCAL_DST, BasicTimeZone.LOCAL_DST, offsets);
                        }
                    } else {
                        // No good way to resolve ambiguous time at transition,
                        // but following code work in most case.
                        tz.getOffset(localMillis, true, offsets);

                        if (tztype == TimeType.STANDARD && offsets[1] != 0
                            || tztype == TimeType.DAYLIGHT && offsets[1] == 0) {
                            // Roll back one day and try it again.
                            // Note: This code assumes 1. timezone transition only happens
                            // once within 24 hours at max
                            // 2. the difference of local offsets at the transition is
                            // less than 24 hours.
                            tz.getOffset(localMillis - (24*60*60*1000), true, offsets);
                        }
                    }

                    // Now, compare the results with parsed type, either standard or
                    // daylight saving time
                    int resolvedSavings = offsets[1];
                    if (tztype == TimeType.STANDARD) {
                        if (offsets[1] != 0) {
                            // Override DST_OFFSET = 0 in the result calendar
                            resolvedSavings = 0;
                        }
                    } else { // tztype == TZTYPE_DST
                        if (offsets[1] == 0) {
                            if (btz != null) {
                                long time = localMillis + offsets[0];
                                // We use the nearest daylight saving time rule.
                                TimeZoneTransition beforeTrs, afterTrs;
                                long beforeT = time, afterT = time;
                                int beforeSav = 0, afterSav = 0;

                                // Search for DST rule before or on the time
                                while (true) {
                                    beforeTrs = btz.getPreviousTransition(beforeT, true);
                                    if (beforeTrs == null) {
                                        break;
                                    }
                                    beforeT = beforeTrs.getTime() - 1;
                                    beforeSav = beforeTrs.getFrom().getDSTSavings();
                                    if (beforeSav != 0) {
                                        break;
                                    }
                                }

                                // Search for DST rule after the time
                                while (true) {
                                    afterTrs = btz.getNextTransition(afterT, false);
                                    if (afterTrs == null) {
                                        break;
                                    }
                                    afterT = afterTrs.getTime();
                                    afterSav = afterTrs.getTo().getDSTSavings();
                                    if (afterSav != 0) {
                                        break;
                                    }
                                }

                                if (beforeTrs != null && afterTrs != null) {
                                    if (time - beforeT > afterT - time) {
                                        resolvedSavings = afterSav;
                                    } else {
                                        resolvedSavings = beforeSav;
                                    }
                                } else if (beforeTrs != null && beforeSav != 0) {
                                    resolvedSavings = beforeSav;
                                } else if (afterTrs != null && afterSav != 0) {
                                    resolvedSavings = afterSav;
                                } else {
                                    resolvedSavings = btz.getDSTSavings();
                                }
                            } else {
                                resolvedSavings = tz.getDSTSavings();
                            }
                            if (resolvedSavings == 0) {
                                // Final fallback
                                resolvedSavings = millisPerHour;
                            }
                        }
                    }
                    cal.set(Calendar.ZONE_OFFSET, offsets[0]);
                    cal.set(Calendar.DST_OFFSET, resolvedSavings);
                }
            }
        }
        // An IllegalArgumentException will be thrown by Calendar.getTime()
        // if any fields are out of range, e.g., MONTH == 17.
        catch (IllegalArgumentException e) {
            parsePos.setErrorIndex(pos);
            parsePos.setIndex(start);
            if (backupTZ != null) {
                calendar.setTimeZone(backupTZ);
            }
            return;
        }
        // Set the parsed result if local calendar is used
        // instead of the input calendar
        if (resultCal != null) {
            resultCal.setTimeZone(cal.getTimeZone());
            resultCal.setTimeInMillis(cal.getTimeInMillis());
        }
        // Restore the original time zone if required
        if (backupTZ != null) {
            calendar.setTimeZone(backupTZ);
        }
    }
1091082
public static String getUtcTimeAsIso8601(Calendar cal) {
		try {
			if (cal == null) {
				return DatatypeFactory.newInstance().newXMLGregorianCalendar(
						new GregorianCalendar(TimeZone.getTimeZone("UTC")))
						.toXMLFormat().replaceAll("\\.[0-9]{3}", "");
			}

			GregorianCalendar suppliedDateCalendar =
					new GregorianCalendar(TimeZone.getTimeZone("UTC"));
			suppliedDateCalendar.setTimeInMillis(cal.getTimeInMillis());

			return DatatypeFactory.newInstance().newXMLGregorianCalendar(
					suppliedDateCalendar).toXMLFormat().replaceAll("\\.[0-9]{3}", "");
		} catch (DatatypeConfigurationException e) {
			SimpleDateFormat xmlDateUtc = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
			xmlDateUtc.setTimeZone(TimeZone.getTimeZone("UTC"));
			return xmlDateUtc.format(Calendar.getInstance());
		}
	}
532605
public static String format( Date date ) {
        return format( DateFormat.DATE_FORMAT_STR_ISO8601, DateFormat.UTC_TIMEZONE, date );
    }
1144485
private static Calendar toCalendar(Date self) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(self);
        return cal;
    }
663343
public static Calendar parseCalendar(final String calendar, final String format, final TimeZone timeZone) {
        if (N.isNullOrEmpty(calendar) || (calendar.length() == 4 && "null".equalsIgnoreCase(calendar))) {
            return null;
        }

        return createCalendar(parse(calendar, format, timeZone));
    }
290384
public static String calendarToString(GregorianCalendar cal) {
        String s = String.format("%1$tFT%1$tT.%1$tL%1$tz", cal);
        /* s is almost right, but is missing the colon in the offset */
        assert Pattern.matches(".*[-+][0-9]{4}$", s);
        int n = s.length();
        return s.substring(0, n-2) + ":" + s.substring(n-2);
    }
1134946
public JsonElement serialize(Calendar src, Type typeOfSrc,
			JsonSerializationContext context) {
		int year = src.get(Calendar.YEAR);
		String month = this.formatter.format(Double.valueOf(src
				.get(Calendar.MONTH) + 1));
		String day = this.formatter.format(Double.valueOf(src
				.get(Calendar.DAY_OF_MONTH)));
		String hour = this.formatter.format(Double.valueOf(src
				.get(Calendar.HOUR_OF_DAY)));
		String minute = this.formatter.format(Double.valueOf(src
				.get(Calendar.MINUTE)));
		String formattedDate = year + "-" + month + "-" + day + "T" + hour
				+ ":" + minute;

		return context.serialize(formattedDate);
	}
804800
@Override
    public StringBuffer format(Calendar cal, StringBuffer toAppendTo,
            FieldPosition fieldPosition) {

        String relativeDayString = null;
        DisplayContext capitalizationContext = getContext(DisplayContext.Type.CAPITALIZATION);

        if (fDateStyle != DateFormat.NONE) {
            // calculate the difference, in days, between 'cal' and now.
            int dayDiff = dayDifference(cal);

            // look up string
            relativeDayString = getStringForDay(dayDiff);
        }

        if (fDateTimeFormat != null) {
            if (relativeDayString != null && fDatePattern != null &&
                    (fTimePattern == null || fCombinedFormat == null || combinedFormatHasDateAtStart) ) {
                // capitalize relativeDayString according to context for relative, set formatter no context
                if ( relativeDayString.length() > 0 && UCharacter.isLowerCase(relativeDayString.codePointAt(0)) &&
                     (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
                        (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationOfRelativeUnitsForListOrMenu) ||
                        (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_STANDALONE && capitalizationOfRelativeUnitsForStandAlone) )) {
                    if (capitalizationBrkIter == null) {
                        // should only happen when deserializing, etc.
                        capitalizationBrkIter = BreakIterator.getSentenceInstance(fLocale);
                    }
                    relativeDayString = UCharacter.toTitleCase(fLocale, relativeDayString, capitalizationBrkIter,
                                    UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
                }
                fDateTimeFormat.setContext(DisplayContext.CAPITALIZATION_NONE);
            } else {
                // set our context for the formatter
                fDateTimeFormat.setContext(capitalizationContext);
            }
        }

        if (fDateTimeFormat != null && (fDatePattern != null || fTimePattern != null)) {
            // The new way
            if (fDatePattern == null) {
                // must have fTimePattern
                fDateTimeFormat.applyPattern(fTimePattern);
                fDateTimeFormat.format(cal, toAppendTo, fieldPosition);
            } else if (fTimePattern == null) {
                // must have fDatePattern
                if (relativeDayString != null) {
                    toAppendTo.append(relativeDayString);
                } else {
                    fDateTimeFormat.applyPattern(fDatePattern);
                    fDateTimeFormat.format(cal, toAppendTo, fieldPosition);
                }
            } else {
                String datePattern = fDatePattern; // default;
                if (relativeDayString != null) {
                    // Need to quote the relativeDayString to make it a legal date pattern
                    datePattern = "'" + relativeDayString.replace("'", "''") + "'";
                }
                StringBuffer combinedPattern = new StringBuffer("");
                fCombinedFormat.format(new Object[] {fTimePattern, datePattern}, combinedPattern, new FieldPosition(0));
                fDateTimeFormat.applyPattern(combinedPattern.toString());
                fDateTimeFormat.format(cal, toAppendTo, fieldPosition);
            }
        } else if (fDateFormat != null) {
            // A subset of the old way, for serialization compatibility
            // (just do the date part)
            if (relativeDayString != null) {
                toAppendTo.append(relativeDayString);
            } else {
                fDateFormat.format(cal, toAppendTo, fieldPosition);
            }
        }

        return toAppendTo;
    }
1263459
public ICalendar getCalendar (final String calName) throws SchedulerException
  {
    validateState ();

    return m_aResources.getJobStore ().retrieveCalendar (calName);
  }
449389
String format(final Object obj) {
		if (obj instanceof Date) {
			return format((Date) obj);
		} else if (obj instanceof Calendar) {
			return format((Calendar) obj);
		} else if (obj instanceof Long) {
			return format(((Long) obj).longValue());
		} else {
			throw new IllegalArgumentException("Unknown class: " + (obj == null ? "<null>" : obj.getClass().getName()));
		}
	}
571559
private Calendar cleanHistCalendar(Calendar cal) {
        cal.set(Calendar.MILLISECOND, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.HOUR, 0);
        return cal;
    }
192817
public void setDateFormatCalendar(Calendar calendar) {
        dateFormatter.setCalendar(calendar != null ? calendar : Calendar.getInstance());
    }
123976
public Date asDate(String format) throws ParsableException {
        SimpleDateFormat dateformat = new SimpleDateFormat(format);
        return asDate(dateformat);
    }
1321212
public String format(Calendar calendar, Calendar smscCalendar) {
    if (calendar == null || smscCalendar == null) {
      return null;
    }

    long diffTimeInMillis = calendar.getTimeInMillis() - smscCalendar.getTimeInMillis();
    if (diffTimeInMillis < 0) {
      throw new IllegalArgumentException("The requested relative time has already past.");
    }

    // calculate period from epoch, this is not as accurate as Joda-Time Period class or Java 8 Period
    Calendar offsetEpoch = Calendar.getInstance(utcTimeZone);
    offsetEpoch.setTimeInMillis(diffTimeInMillis);
    int years = offsetEpoch.get(Calendar.YEAR) - 1970;
    int months = offsetEpoch.get(Calendar.MONTH);
    int days = offsetEpoch.get(Calendar.DAY_OF_MONTH) - 1;
    int hours = offsetEpoch.get(Calendar.HOUR_OF_DAY);
    int minutes = offsetEpoch.get(Calendar.MINUTE);
    int seconds = offsetEpoch.get(Calendar.SECOND);

    if (years >= 100) {
      throw new IllegalArgumentException("The requested relative time is more then a century (" + years + " years).");
    }

    return format(years, months, days, hours, minutes, seconds);
  }
469515
public static Date toDate(XMLGregorianCalendar cal) {
        if (cal == null)
            return null;

        return cal.toGregorianCalendar().getTime();
    }