Update #0 - First Release

This commit is contained in:
LAX1DUDE
2022-12-25 01:12:28 -08:00
commit e7179fad45
2154 changed files with 256324 additions and 0 deletions

View File

@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.time;
import java.util.Calendar;
import java.util.Objects;
/**
* Helps use {@link Calendar}s.
*
* @since 3.10
*/
public class CalendarUtils {
/**
* The singleton instance for {@link Calendar#getInstance()}.
*/
public static final CalendarUtils INSTANCE = new CalendarUtils(Calendar.getInstance());
private final Calendar calendar;
/**
* Creates an instance for the given Calendar.
*
* @param calendar A Calendar.
*/
public CalendarUtils(final Calendar calendar) {
this.calendar = Objects.requireNonNull(calendar, "calendar");
}
/**
* Gets the current day of month.
*
* @return the current day of month.
*/
public int getDayOfMonth() {
return calendar.get(Calendar.DAY_OF_MONTH);
}
/**
* Gets the current month.
*
* @return the current month.
*/
public int getMonth() {
return calendar.get(Calendar.MONTH);
}
/**
* Gets the current year.
*
* @return the current year.
*/
public int getYear() {
return calendar.get(Calendar.YEAR);
}
}

View File

@ -0,0 +1,449 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.time;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* <p>
* Date and time formatting utilities and constants.
* </p>
*
* <p>
* Formatting is performed using the thread-safe
* {@link org.apache.commons.lang3.time.FastDateFormat} class.
* </p>
*
* <p>
* Note that the JDK has a bug wherein calling Calendar.get(int) will override
* any previously called Calendar.clear() calls. See LANG-755.
* </p>
*
* @since 2.0
*/
public class DateFormatUtils {
/**
* The UTC time zone (often referred to as GMT). This is private as it is
* mutable.
*/
private static final TimeZone UTC_TIME_ZONE = FastTimeZone.getGmtTimeZone();
/**
* ISO 8601 formatter for date-time without time zone.
*
* <p>
* The format used is {@code yyyy-MM-dd'T'HH:mm:ss}. This format uses the
* default TimeZone in effect at the time of loading DateFormatUtils class.
* </p>
*
* @since 3.5
*/
public static final FastDateFormat ISO_8601_EXTENDED_DATETIME_FORMAT = FastDateFormat
.getInstance("yyyy-MM-dd'T'HH:mm:ss");
/**
* @deprecated - as of 4.0, ISO_DATETIME_FORMAT will be replaced by
* ISO_8601_EXTENDED_DATETIME_FORMAT.
*/
@Deprecated
public static final FastDateFormat ISO_DATETIME_FORMAT = ISO_8601_EXTENDED_DATETIME_FORMAT;
/**
* ISO 8601 formatter for date-time with time zone.
*
* <p>
* The format used is {@code yyyy-MM-dd'T'HH:mm:ssZZ}. This format uses the
* default TimeZone in effect at the time of loading DateFormatUtils class.
* </p>
*
* @since 3.5
*/
public static final FastDateFormat ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT = FastDateFormat
.getInstance("yyyy-MM-dd'T'HH:mm:ssZZ");
/**
* @deprecated - as of 4.0, ISO_DATETIME_TIME_ZONE_FORMAT will be replaced by
* ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.
*/
@Deprecated
public static final FastDateFormat ISO_DATETIME_TIME_ZONE_FORMAT = ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT;
/**
* ISO 8601 formatter for date without time zone.
*
* <p>
* The format used is {@code yyyy-MM-dd}. This format uses the default TimeZone
* in effect at the time of loading DateFormatUtils class.
* </p>
*
* @since 3.5
*/
public static final FastDateFormat ISO_8601_EXTENDED_DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd");
/**
* @deprecated - as of 4.0, ISO_DATE_FORMAT will be replaced by
* ISO_8601_EXTENDED_DATE_FORMAT.
*/
@Deprecated
public static final FastDateFormat ISO_DATE_FORMAT = ISO_8601_EXTENDED_DATE_FORMAT;
/**
* ISO 8601-like formatter for date with time zone.
*
* <p>
* The format used is {@code yyyy-MM-ddZZ}. This pattern does not comply with
* the formal ISO 8601 specification as the standard does not allow a time zone
* without a time. This format uses the default TimeZone in effect at the time
* of loading DateFormatUtils class.
* </p>
*
* @deprecated - as of 4.0, ISO_DATE_TIME_ZONE_FORMAT will be removed.
*/
@Deprecated
public static final FastDateFormat ISO_DATE_TIME_ZONE_FORMAT = FastDateFormat.getInstance("yyyy-MM-ddZZ");
/**
* Non-compliant formatter for time without time zone (ISO 8601 does not prefix
* 'T' for standalone time value).
*
* <p>
* The format used is {@code 'T'HH:mm:ss}. This format uses the default TimeZone
* in effect at the time of loading DateFormatUtils class.
* </p>
*
* @deprecated - as of 4.0, ISO_TIME_FORMAT will be removed.
*/
@Deprecated
public static final FastDateFormat ISO_TIME_FORMAT = FastDateFormat.getInstance("'T'HH:mm:ss");
/**
* Non-compliant formatter for time with time zone (ISO 8601 does not prefix 'T'
* for standalone time value).
*
* <p>
* The format used is {@code 'T'HH:mm:ssZZ}. This format uses the default
* TimeZone in effect at the time of loading DateFormatUtils class.
* </p>
*
* @deprecated - as of 4.0, ISO_TIME_TIME_ZONE_FORMAT will be removed.
*/
@Deprecated
public static final FastDateFormat ISO_TIME_TIME_ZONE_FORMAT = FastDateFormat.getInstance("'T'HH:mm:ssZZ");
/**
* ISO 8601 formatter for time without time zone.
*
* <p>
* The format used is {@code HH:mm:ss}. This format uses the default TimeZone in
* effect at the time of loading DateFormatUtils class.
* </p>
*
* @since 3.5
*/
public static final FastDateFormat ISO_8601_EXTENDED_TIME_FORMAT = FastDateFormat.getInstance("HH:mm:ss");
/**
* @deprecated - as of 4.0, ISO_TIME_NO_T_FORMAT will be replaced by
* ISO_8601_EXTENDED_TIME_FORMAT.
*/
@Deprecated
public static final FastDateFormat ISO_TIME_NO_T_FORMAT = ISO_8601_EXTENDED_TIME_FORMAT;
/**
* ISO 8601 formatter for time with time zone.
*
* <p>
* The format used is {@code HH:mm:ssZZ}. This format uses the default TimeZone
* in effect at the time of loading DateFormatUtils class.
* </p>
*
* @since 3.5
*/
public static final FastDateFormat ISO_8601_EXTENDED_TIME_TIME_ZONE_FORMAT = FastDateFormat
.getInstance("HH:mm:ssZZ");
/**
* @deprecated - as of 4.0, ISO_TIME_NO_T_TIME_ZONE_FORMAT will be replaced by
* ISO_8601_EXTENDED_TIME_TIME_ZONE_FORMAT.
*/
@Deprecated
public static final FastDateFormat ISO_TIME_NO_T_TIME_ZONE_FORMAT = ISO_8601_EXTENDED_TIME_TIME_ZONE_FORMAT;
/**
* SMTP (and probably other) date headers.
*
* <p>
* The format used is {@code EEE, dd MMM yyyy HH:mm:ss Z} in US locale. This
* format uses the default TimeZone in effect at the time of loading
* DateFormatUtils class.
* </p>
*/
public static final FastDateFormat SMTP_DATETIME_FORMAT = FastDateFormat.getInstance("EEE, dd MMM yyyy HH:mm:ss Z",
Locale.US);
// -----------------------------------------------------------------------
/**
* <p>
* DateFormatUtils instances should NOT be constructed in standard programming.
* </p>
*
* <p>
* This constructor is public to permit tools that require a JavaBean instance
* to operate.
* </p>
*/
public DateFormatUtils() {
}
/**
* <p>
* Formats a date/time into a specific pattern using the UTC time zone.
* </p>
*
* @param millis the date to format expressed in milliseconds
* @param pattern the pattern to use to format the date, not null
* @return the formatted date
*/
public static String formatUTC(final long millis, final String pattern) {
return format(new Date(millis), pattern, UTC_TIME_ZONE, null);
}
/**
* <p>
* Formats a date/time into a specific pattern using the UTC time zone.
* </p>
*
* @param date the date to format, not null
* @param pattern the pattern to use to format the date, not null
* @return the formatted date
*/
public static String formatUTC(final Date date, final String pattern) {
return format(date, pattern, UTC_TIME_ZONE, null);
}
/**
* <p>
* Formats a date/time into a specific pattern using the UTC time zone.
* </p>
*
* @param millis the date to format expressed in milliseconds
* @param pattern the pattern to use to format the date, not null
* @param locale the locale to use, may be {@code null}
* @return the formatted date
*/
public static String formatUTC(final long millis, final String pattern, final Locale locale) {
return format(new Date(millis), pattern, UTC_TIME_ZONE, locale);
}
/**
* <p>
* Formats a date/time into a specific pattern using the UTC time zone.
* </p>
*
* @param date the date to format, not null
* @param pattern the pattern to use to format the date, not null
* @param locale the locale to use, may be {@code null}
* @return the formatted date
*/
public static String formatUTC(final Date date, final String pattern, final Locale locale) {
return format(date, pattern, UTC_TIME_ZONE, locale);
}
/**
* <p>
* Formats a date/time into a specific pattern.
* </p>
*
* @param millis the date to format expressed in milliseconds
* @param pattern the pattern to use to format the date, not null
* @return the formatted date
*/
public static String format(final long millis, final String pattern) {
return format(new Date(millis), pattern, null, null);
}
/**
* <p>
* Formats a date/time into a specific pattern.
* </p>
*
* @param date the date to format, not null
* @param pattern the pattern to use to format the date, not null
* @return the formatted date
*/
public static String format(final Date date, final String pattern) {
return format(date, pattern, null, null);
}
/**
* <p>
* Formats a calendar into a specific pattern.
* </p>
*
* @param calendar the calendar to format, not null
* @param pattern the pattern to use to format the calendar, not null
* @return the formatted calendar
* @see FastDateFormat#format(Calendar)
* @since 2.4
*/
public static String format(final Calendar calendar, final String pattern) {
return format(calendar, pattern, null, null);
}
/**
* <p>
* Formats a date/time into a specific pattern in a time zone.
* </p>
*
* @param millis the time expressed in milliseconds
* @param pattern the pattern to use to format the date, not null
* @param timeZone the time zone to use, may be {@code null}
* @return the formatted date
*/
public static String format(final long millis, final String pattern, final TimeZone timeZone) {
return format(new Date(millis), pattern, timeZone, null);
}
/**
* <p>
* Formats a date/time into a specific pattern in a time zone.
* </p>
*
* @param date the date to format, not null
* @param pattern the pattern to use to format the date, not null
* @param timeZone the time zone to use, may be {@code null}
* @return the formatted date
*/
public static String format(final Date date, final String pattern, final TimeZone timeZone) {
return format(date, pattern, timeZone, null);
}
/**
* <p>
* Formats a calendar into a specific pattern in a time zone.
* </p>
*
* @param calendar the calendar to format, not null
* @param pattern the pattern to use to format the calendar, not null
* @param timeZone the time zone to use, may be {@code null}
* @return the formatted calendar
* @see FastDateFormat#format(Calendar)
* @since 2.4
*/
public static String format(final Calendar calendar, final String pattern, final TimeZone timeZone) {
return format(calendar, pattern, timeZone, null);
}
/**
* <p>
* Formats a date/time into a specific pattern in a locale.
* </p>
*
* @param millis the date to format expressed in milliseconds
* @param pattern the pattern to use to format the date, not null
* @param locale the locale to use, may be {@code null}
* @return the formatted date
*/
public static String format(final long millis, final String pattern, final Locale locale) {
return format(new Date(millis), pattern, null, locale);
}
/**
* <p>
* Formats a date/time into a specific pattern in a locale.
* </p>
*
* @param date the date to format, not null
* @param pattern the pattern to use to format the date, not null
* @param locale the locale to use, may be {@code null}
* @return the formatted date
*/
public static String format(final Date date, final String pattern, final Locale locale) {
return format(date, pattern, null, locale);
}
/**
* <p>
* Formats a calendar into a specific pattern in a locale.
* </p>
*
* @param calendar the calendar to format, not null
* @param pattern the pattern to use to format the calendar, not null
* @param locale the locale to use, may be {@code null}
* @return the formatted calendar
* @see FastDateFormat#format(Calendar)
* @since 2.4
*/
public static String format(final Calendar calendar, final String pattern, final Locale locale) {
return format(calendar, pattern, null, locale);
}
/**
* <p>
* Formats a date/time into a specific pattern in a time zone and locale.
* </p>
*
* @param millis the date to format expressed in milliseconds
* @param pattern the pattern to use to format the date, not null
* @param timeZone the time zone to use, may be {@code null}
* @param locale the locale to use, may be {@code null}
* @return the formatted date
*/
public static String format(final long millis, final String pattern, final TimeZone timeZone, final Locale locale) {
return format(new Date(millis), pattern, timeZone, locale);
}
/**
* <p>
* Formats a date/time into a specific pattern in a time zone and locale.
* </p>
*
* @param date the date to format, not null
* @param pattern the pattern to use to format the date, not null, not null
* @param timeZone the time zone to use, may be {@code null}
* @param locale the locale to use, may be {@code null}
* @return the formatted date
*/
public static String format(final Date date, final String pattern, final TimeZone timeZone, final Locale locale) {
final FastDateFormat df = FastDateFormat.getInstance(pattern, timeZone, locale);
return df.format(date);
}
/**
* <p>
* Formats a calendar into a specific pattern in a time zone and locale.
* </p>
*
* @param calendar the calendar to format, not null
* @param pattern the pattern to use to format the calendar, not null
* @param timeZone the time zone to use, may be {@code null}
* @param locale the locale to use, may be {@code null}
* @return the formatted calendar
* @see FastDateFormat#format(Calendar)
* @since 2.4
*/
public static String format(final Calendar calendar, final String pattern, final TimeZone timeZone,
final Locale locale) {
final FastDateFormat df = FastDateFormat.getInstance(pattern, timeZone, locale);
return df.format(calendar);
}
}

View File

@ -0,0 +1,139 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.time;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* DateParser is the "missing" interface for the parsing methods of
* {@link java.text.DateFormat}. You can obtain an object implementing this
* interface by using one of the FastDateFormat factory methods.
* <p>
* Warning: Since binary compatible methods may be added to this interface in
* any release, developers are not expected to implement this interface.
*
* @since 3.2
*/
public interface DateParser {
/**
* Equivalent to DateFormat.parse(String).
*
* See {@link java.text.DateFormat#parse(String)} for more information.
*
* @param source A {@code String} whose beginning should be parsed.
* @return A {@code Date} parsed from the string
* @throws ParseException if the beginning of the specified string cannot be
* parsed.
*/
Date parse(String source) throws ParseException;
/**
* Equivalent to DateFormat.parse(String, ParsePosition).
*
* See {@link java.text.DateFormat#parse(String, ParsePosition)} for more
* information.
*
* @param source A {@code String}, part of which should be parsed.
* @param pos A {@code ParsePosition} object with index and error index
* information as described above.
* @return A {@code Date} parsed from the string. In case of error, returns
* null.
* @throws NullPointerException if text or pos is null.
*/
Date parse(String source, ParsePosition pos);
/**
* Parses a formatted date string according to the format. Updates the Calendar
* with parsed fields. Upon success, the ParsePosition index is updated to
* indicate how much of the source text was consumed. Not all source text needs
* to be consumed. Upon parse failure, ParsePosition error index is updated to
* the offset of the source text which does not match the supplied format.
*
* @param source The text to parse.
* @param pos On input, the position in the source to start parsing, on
* output, updated position.
* @param calendar The calendar into which to set parsed fields.
* @return true, if source has been parsed (pos parsePosition is updated);
* otherwise false (and pos errorIndex is updated)
* @throws IllegalArgumentException when Calendar has been set to be not
* lenient, and a parsed field is out of range.
*
* @since 3.5
*/
boolean parse(String source, ParsePosition pos, Calendar calendar);
// Accessors
// -----------------------------------------------------------------------
/**
* <p>
* Gets the pattern used by this parser.
* </p>
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/
String getPattern();
/**
* <p>
* Gets the time zone used by this parser.
* </p>
*
* <p>
* The default {@link TimeZone} used to create a {@link Date} when the
* {@link TimeZone} is not specified by the format pattern.
* </p>
*
* @return the time zone
*/
TimeZone getTimeZone();
/**
* <p>
* Gets the locale used by this parser.
* </p>
*
* @return the locale
*/
Locale getLocale();
/**
* Parses text from a string to produce a Date.
*
* @param source A {@code String} whose beginning should be parsed.
* @return a {@code java.util.Date} object
* @throws ParseException if the beginning of the specified string cannot be
* parsed.
* @see java.text.DateFormat#parseObject(String)
*/
Object parseObject(String source) throws ParseException;
/**
* Parses a date/time string according to the given parse position.
*
* @param source A {@code String} whose beginning should be parsed.
* @param pos the parse position
* @return a {@code java.util.Date} object
* @see java.text.DateFormat#parseObject(String, ParsePosition)
*/
Object parseObject(String source, ParsePosition pos);
}

View File

@ -0,0 +1,207 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.time;
import java.text.FieldPosition;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* DatePrinter is the "missing" interface for the format methods of
* {@link java.text.DateFormat}. You can obtain an object implementing this
* interface by using one of the FastDateFormat factory methods.
* <p>
* Warning: Since binary compatible methods may be added to this interface in
* any release, developers are not expected to implement this interface.
*
* @since 3.2
*/
public interface DatePrinter {
/**
* <p>
* Formats a millisecond {@code long} value.
* </p>
*
* @param millis the millisecond value to format
* @return the formatted string
* @since 2.1
*/
String format(long millis);
/**
* <p>
* Formats a {@code Date} object using a {@code GregorianCalendar}.
* </p>
*
* @param date the date to format
* @return the formatted string
*/
String format(Date date);
/**
* <p>
* Formats a {@code Calendar} object.
* </p>
* The TimeZone set on the Calendar is only used to adjust the time offset. The
* TimeZone specified during the construction of the Parser will determine the
* TimeZone used in the formatted string.
*
* @param calendar the calendar to format.
* @return the formatted string
*/
String format(Calendar calendar);
/**
* <p>
* Formats a millisecond {@code long} value into the supplied
* {@code StringBuffer}.
* </p>
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {{@link #format(long, Appendable)}.
*/
@Deprecated
StringBuffer format(long millis, StringBuffer buf);
/**
* <p>
* Formats a {@code Date} object into the supplied {@code StringBuffer} using a
* {@code GregorianCalendar}.
* </p>
*
* @param date the date to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {{@link #format(Date, Appendable)}.
*/
@Deprecated
StringBuffer format(Date date, StringBuffer buf);
/**
* <p>
* Formats a {@code Calendar} object into the supplied {@code StringBuffer}.
* </p>
* The TimeZone set on the Calendar is only used to adjust the time offset. The
* TimeZone specified during the construction of the Parser will determine the
* TimeZone used in the formatted string.
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {{@link #format(Calendar, Appendable)}.
*/
@Deprecated
StringBuffer format(Calendar calendar, StringBuffer buf);
/**
* <p>
* Formats a millisecond {@code long} value into the supplied
* {@code Appendable}.
* </p>
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @param <B> the Appendable class type, usually StringBuilder or
* StringBuffer.
* @return the specified string buffer
* @since 3.5
*/
<B extends Appendable> B format(long millis, B buf);
/**
* <p>
* Formats a {@code Date} object into the supplied {@code Appendable} using a
* {@code GregorianCalendar}.
* </p>
*
* @param date the date to format
* @param buf the buffer to format into
* @param <B> the Appendable class type, usually StringBuilder or StringBuffer.
* @return the specified string buffer
* @since 3.5
*/
<B extends Appendable> B format(Date date, B buf);
/**
* <p>
* Formats a {@code Calendar} object into the supplied {@code Appendable}.
* </p>
* The TimeZone set on the Calendar is only used to adjust the time offset. The
* TimeZone specified during the construction of the Parser will determine the
* TimeZone used in the formatted string.
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @param <B> the Appendable class type, usually StringBuilder or
* StringBuffer.
* @return the specified string buffer
* @since 3.5
*/
<B extends Appendable> B format(Calendar calendar, B buf);
// Accessors
// -----------------------------------------------------------------------
/**
* <p>
* Gets the pattern used by this printer.
* </p>
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/
String getPattern();
/**
* <p>
* Gets the time zone used by this printer.
* </p>
*
* <p>
* This zone is always used for {@code Date} printing.
* </p>
*
* @return the time zone
*/
TimeZone getTimeZone();
/**
* <p>
* Gets the locale used by this printer.
* </p>
*
* @return the locale
*/
Locale getLocale();
/**
* <p>
* Formats a {@code Date}, {@code Calendar} or {@code Long} (milliseconds)
* object.
* </p>
*
* @param obj the object to format
* @param toAppendTo the buffer to append to
* @param pos the position - ignored
* @return the buffer passed in
* @see java.text.DateFormat#format(Object, StringBuffer, FieldPosition)
*/
StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,765 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.time;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
/**
* <p>
* Duration formatting utilities and constants. The following table describes
* the tokens used in the pattern language for formatting.
* </p>
* <table border="1">
* <caption>Pattern Tokens</caption>
* <tr>
* <th>character</th>
* <th>duration element</th>
* </tr>
* <tr>
* <td>y</td>
* <td>years</td>
* </tr>
* <tr>
* <td>M</td>
* <td>months</td>
* </tr>
* <tr>
* <td>d</td>
* <td>days</td>
* </tr>
* <tr>
* <td>H</td>
* <td>hours</td>
* </tr>
* <tr>
* <td>m</td>
* <td>minutes</td>
* </tr>
* <tr>
* <td>s</td>
* <td>seconds</td>
* </tr>
* <tr>
* <td>S</td>
* <td>milliseconds</td>
* </tr>
* <tr>
* <td>'text'</td>
* <td>arbitrary text content</td>
* </tr>
* </table>
*
* <b>Note: It's not currently possible to include a single-quote in a
* format.</b> <br>
* Token values are printed using decimal digits. A token character can be
* repeated to ensure that the field occupies a certain minimum size. Values
* will be left-padded with 0 unless padding is disabled in the method
* invocation.
*
* @since 2.1
*/
public class DurationFormatUtils {
/**
* <p>
* DurationFormatUtils instances should NOT be constructed in standard
* programming.
* </p>
*
* <p>
* This constructor is public to permit tools that require a JavaBean instance
* to operate.
* </p>
*/
public DurationFormatUtils() {
}
/**
* <p>
* Pattern used with {@code FastDateFormat} and {@code SimpleDateFormat} for the
* ISO 8601 period format used in durations.
* </p>
*
* @see org.apache.commons.lang3.time.FastDateFormat
* @see java.text.SimpleDateFormat
*/
public static final String ISO_EXTENDED_FORMAT_PATTERN = "'P'yyyy'Y'M'M'd'DT'H'H'm'M's.SSS'S'";
// -----------------------------------------------------------------------
/**
* <p>
* Formats the time gap as a string.
* </p>
*
* <p>
* The format used is ISO 8601-like: {@code HH:mm:ss.SSS}.
* </p>
*
* @param durationMillis the duration to format
* @return the formatted duration, not null
* @throws java.lang.IllegalArgumentException if durationMillis is negative
*/
public static String formatDurationHMS(final long durationMillis) {
return formatDuration(durationMillis, "HH:mm:ss.SSS");
}
/**
* <p>
* Formats the time gap as a string.
* </p>
*
* <p>
* The format used is the ISO 8601 period format.
* </p>
*
* <p>
* This method formats durations using the days and lower fields of the ISO
* format pattern, such as P7D6TH5M4.321S.
* </p>
*
* @param durationMillis the duration to format
* @return the formatted duration, not null
* @throws java.lang.IllegalArgumentException if durationMillis is negative
*/
public static String formatDurationISO(final long durationMillis) {
return formatDuration(durationMillis, ISO_EXTENDED_FORMAT_PATTERN, false);
}
/**
* <p>
* Formats the time gap as a string, using the specified format, and padding
* with zeros.
* </p>
*
* <p>
* This method formats durations using the days and lower fields of the format
* pattern. Months and larger are not used.
* </p>
*
* @param durationMillis the duration to format
* @param format the way in which to format the duration, not null
* @return the formatted duration, not null
* @throws java.lang.IllegalArgumentException if durationMillis is negative
*/
public static String formatDuration(final long durationMillis, final String format) {
return formatDuration(durationMillis, format, true);
}
/**
* <p>
* Formats the time gap as a string, using the specified format. Padding the
* left hand side of numbers with zeroes is optional.
* </p>
*
* <p>
* This method formats durations using the days and lower fields of the format
* pattern. Months and larger are not used.
* </p>
*
* @param durationMillis the duration to format
* @param format the way in which to format the duration, not null
* @param padWithZeros whether to pad the left hand side of numbers with 0's
* @return the formatted duration, not null
* @throws java.lang.IllegalArgumentException if durationMillis is negative
*/
public static String formatDuration(final long durationMillis, final String format, final boolean padWithZeros) {
Validate.inclusiveBetween(0, Long.MAX_VALUE, durationMillis, "durationMillis must not be negative");
final Token[] tokens = lexx(format);
long days = 0;
long hours = 0;
long minutes = 0;
long seconds = 0;
long milliseconds = durationMillis;
if (Token.containsTokenWithValue(tokens, d)) {
days = milliseconds / DateUtils.MILLIS_PER_DAY;
milliseconds = milliseconds - (days * DateUtils.MILLIS_PER_DAY);
}
if (Token.containsTokenWithValue(tokens, H)) {
hours = milliseconds / DateUtils.MILLIS_PER_HOUR;
milliseconds = milliseconds - (hours * DateUtils.MILLIS_PER_HOUR);
}
if (Token.containsTokenWithValue(tokens, m)) {
minutes = milliseconds / DateUtils.MILLIS_PER_MINUTE;
milliseconds = milliseconds - (minutes * DateUtils.MILLIS_PER_MINUTE);
}
if (Token.containsTokenWithValue(tokens, s)) {
seconds = milliseconds / DateUtils.MILLIS_PER_SECOND;
milliseconds = milliseconds - (seconds * DateUtils.MILLIS_PER_SECOND);
}
return format(tokens, 0, 0, days, hours, minutes, seconds, milliseconds, padWithZeros);
}
/**
* <p>
* Formats an elapsed time into a pluralization correct string.
* </p>
*
* <p>
* This method formats durations using the days and lower fields of the format
* pattern. Months and larger are not used.
* </p>
*
* @param durationMillis the elapsed time to report in
* milliseconds
* @param suppressLeadingZeroElements suppresses leading 0 elements
* @param suppressTrailingZeroElements suppresses trailing 0 elements
* @return the formatted text in days/hours/minutes/seconds, not null
* @throws java.lang.IllegalArgumentException if durationMillis is negative
*/
public static String formatDurationWords(final long durationMillis, final boolean suppressLeadingZeroElements,
final boolean suppressTrailingZeroElements) {
// This method is generally replaceable by the format method, but
// there are a series of tweaks and special cases that require
// trickery to replicate.
String duration = formatDuration(durationMillis, "d' days 'H' hours 'm' minutes 's' seconds'");
if (suppressLeadingZeroElements) {
// this is a temporary marker on the front. Like ^ in regexp.
duration = " " + duration;
String tmp = StringUtils.replaceOnce(duration, " 0 days", StringUtils.EMPTY);
if (tmp.length() != duration.length()) {
duration = tmp;
tmp = StringUtils.replaceOnce(duration, " 0 hours", StringUtils.EMPTY);
if (tmp.length() != duration.length()) {
duration = tmp;
tmp = StringUtils.replaceOnce(duration, " 0 minutes", StringUtils.EMPTY);
duration = tmp;
if (tmp.length() != duration.length()) {
duration = StringUtils.replaceOnce(tmp, " 0 seconds", StringUtils.EMPTY);
}
}
}
if (!duration.isEmpty()) {
// strip the space off again
duration = duration.substring(1);
}
}
if (suppressTrailingZeroElements) {
String tmp = StringUtils.replaceOnce(duration, " 0 seconds", StringUtils.EMPTY);
if (tmp.length() != duration.length()) {
duration = tmp;
tmp = StringUtils.replaceOnce(duration, " 0 minutes", StringUtils.EMPTY);
if (tmp.length() != duration.length()) {
duration = tmp;
tmp = StringUtils.replaceOnce(duration, " 0 hours", StringUtils.EMPTY);
if (tmp.length() != duration.length()) {
duration = StringUtils.replaceOnce(tmp, " 0 days", StringUtils.EMPTY);
}
}
}
}
// handle plurals
duration = " " + duration;
duration = StringUtils.replaceOnce(duration, " 1 seconds", " 1 second");
duration = StringUtils.replaceOnce(duration, " 1 minutes", " 1 minute");
duration = StringUtils.replaceOnce(duration, " 1 hours", " 1 hour");
duration = StringUtils.replaceOnce(duration, " 1 days", " 1 day");
return duration.trim();
}
// -----------------------------------------------------------------------
/**
* <p>
* Formats the time gap as a string.
* </p>
*
* <p>
* The format used is the ISO 8601 period format.
* </p>
*
* @param startMillis the start of the duration to format
* @param endMillis the end of the duration to format
* @return the formatted duration, not null
* @throws java.lang.IllegalArgumentException if startMillis is greater than
* endMillis
*/
public static String formatPeriodISO(final long startMillis, final long endMillis) {
return formatPeriod(startMillis, endMillis, ISO_EXTENDED_FORMAT_PATTERN, false, TimeZone.getDefault());
}
/**
* <p>
* Formats the time gap as a string, using the specified format. Padding the
* left hand side of numbers with zeroes is optional.
*
* @param startMillis the start of the duration
* @param endMillis the end of the duration
* @param format the way in which to format the duration, not null
* @return the formatted duration, not null
* @throws java.lang.IllegalArgumentException if startMillis is greater than
* endMillis
*/
public static String formatPeriod(final long startMillis, final long endMillis, final String format) {
return formatPeriod(startMillis, endMillis, format, true, TimeZone.getDefault());
}
/**
* <p>
* Formats the time gap as a string, using the specified format. Padding the
* left hand side of numbers with zeroes is optional and the time zone may be
* specified.
* </p>
*
* <p>
* When calculating the difference between months/days, it chooses to calculate
* months first. So when working out the number of months and days between
* January 15th and March 10th, it choose 1 month and 23 days gained by choosing
* January-&gt;February = 1 month and then calculating days forwards, and not
* the 1 month and 26 days gained by choosing March -&gt; February = 1 month and
* then calculating days backwards.
* </p>
*
* <p>
* For more control, the <a href="http://joda-time.sf.net/">Joda-Time</a>
* library is recommended.
* </p>
*
* @param startMillis the start of the duration
* @param endMillis the end of the duration
* @param format the way in which to format the duration, not null
* @param padWithZeros whether to pad the left hand side of numbers with 0's
* @param timezone the millis are defined in
* @return the formatted duration, not null
* @throws java.lang.IllegalArgumentException if startMillis is greater than
* endMillis
*/
public static String formatPeriod(final long startMillis, final long endMillis, final String format,
final boolean padWithZeros, final TimeZone timezone) {
Validate.isTrue(startMillis <= endMillis, "startMillis must not be greater than endMillis");
// Used to optimise for differences under 28 days and
// called formatDuration(millis, format); however this did not work
// over leap years.
// TODO: Compare performance to see if anything was lost by
// losing this optimisation.
final Token[] tokens = lexx(format);
// time zones get funky around 0, so normalizing everything to GMT
// stops the hours being off
final Calendar start = Calendar.getInstance(timezone);
start.setTime(new Date(startMillis));
final Calendar end = Calendar.getInstance(timezone);
end.setTime(new Date(endMillis));
// initial estimates
int milliseconds = end.get(Calendar.MILLISECOND) - start.get(Calendar.MILLISECOND);
int seconds = end.get(Calendar.SECOND) - start.get(Calendar.SECOND);
int minutes = end.get(Calendar.MINUTE) - start.get(Calendar.MINUTE);
int hours = end.get(Calendar.HOUR_OF_DAY) - start.get(Calendar.HOUR_OF_DAY);
int days = end.get(Calendar.DAY_OF_MONTH) - start.get(Calendar.DAY_OF_MONTH);
int months = end.get(Calendar.MONTH) - start.get(Calendar.MONTH);
int years = end.get(Calendar.YEAR) - start.get(Calendar.YEAR);
// each initial estimate is adjusted in case it is under 0
while (milliseconds < 0) {
milliseconds += 1000;
seconds -= 1;
}
while (seconds < 0) {
seconds += 60;
minutes -= 1;
}
while (minutes < 0) {
minutes += 60;
hours -= 1;
}
while (hours < 0) {
hours += 24;
days -= 1;
}
if (Token.containsTokenWithValue(tokens, M)) {
while (days < 0) {
days += start.getActualMaximum(Calendar.DAY_OF_MONTH);
months -= 1;
start.add(Calendar.MONTH, 1);
}
while (months < 0) {
months += 12;
years -= 1;
}
if (!Token.containsTokenWithValue(tokens, y) && years != 0) {
while (years != 0) {
months += 12 * years;
years = 0;
}
}
} else {
// there are no M's in the format string
if (!Token.containsTokenWithValue(tokens, y)) {
int target = end.get(Calendar.YEAR);
if (months < 0) {
// target is end-year -1
target -= 1;
}
while (start.get(Calendar.YEAR) != target) {
days += start.getActualMaximum(Calendar.DAY_OF_YEAR) - start.get(Calendar.DAY_OF_YEAR);
// Not sure I grok why this is needed, but the brutal tests show it is
if (start instanceof GregorianCalendar && start.get(Calendar.MONTH) == Calendar.FEBRUARY
&& start.get(Calendar.DAY_OF_MONTH) == 29) {
days += 1;
}
start.add(Calendar.YEAR, 1);
days += start.get(Calendar.DAY_OF_YEAR);
}
years = 0;
}
while (start.get(Calendar.MONTH) != end.get(Calendar.MONTH)) {
days += start.getActualMaximum(Calendar.DAY_OF_MONTH);
start.add(Calendar.MONTH, 1);
}
months = 0;
while (days < 0) {
days += start.getActualMaximum(Calendar.DAY_OF_MONTH);
months -= 1;
start.add(Calendar.MONTH, 1);
}
}
// The rest of this code adds in values that
// aren't requested. This allows the user to ask for the
// number of months and get the real count and not just 0->11.
if (!Token.containsTokenWithValue(tokens, d)) {
hours += 24 * days;
days = 0;
}
if (!Token.containsTokenWithValue(tokens, H)) {
minutes += 60 * hours;
hours = 0;
}
if (!Token.containsTokenWithValue(tokens, m)) {
seconds += 60 * minutes;
minutes = 0;
}
if (!Token.containsTokenWithValue(tokens, s)) {
milliseconds += 1000 * seconds;
seconds = 0;
}
return format(tokens, years, months, days, hours, minutes, seconds, milliseconds, padWithZeros);
}
// -----------------------------------------------------------------------
/**
* <p>
* The internal method to do the formatting.
* </p>
*
* @param tokens the tokens
* @param years the number of years
* @param months the number of months
* @param days the number of days
* @param hours the number of hours
* @param minutes the number of minutes
* @param seconds the number of seconds
* @param milliseconds the number of millis
* @param padWithZeros whether to pad
* @return the formatted string
*/
static String format(final Token[] tokens, final long years, final long months, final long days, final long hours,
final long minutes, final long seconds, final long milliseconds, final boolean padWithZeros) {
final StringBuilder buffer = new StringBuilder();
boolean lastOutputSeconds = false;
for (final Token token : tokens) {
final Object value = token.getValue();
final int count = token.getCount();
if (value instanceof StringBuilder) {
buffer.append(value.toString());
} else if (value.equals(y)) {
buffer.append(paddedValue(years, padWithZeros, count));
lastOutputSeconds = false;
} else if (value.equals(M)) {
buffer.append(paddedValue(months, padWithZeros, count));
lastOutputSeconds = false;
} else if (value.equals(d)) {
buffer.append(paddedValue(days, padWithZeros, count));
lastOutputSeconds = false;
} else if (value.equals(H)) {
buffer.append(paddedValue(hours, padWithZeros, count));
lastOutputSeconds = false;
} else if (value.equals(m)) {
buffer.append(paddedValue(minutes, padWithZeros, count));
lastOutputSeconds = false;
} else if (value.equals(s)) {
buffer.append(paddedValue(seconds, padWithZeros, count));
lastOutputSeconds = true;
} else if (value.equals(S)) {
if (lastOutputSeconds) {
// ensure at least 3 digits are displayed even if padding is not selected
final int width = padWithZeros ? Math.max(3, count) : 3;
buffer.append(paddedValue(milliseconds, true, width));
} else {
buffer.append(paddedValue(milliseconds, padWithZeros, count));
}
lastOutputSeconds = false;
}
}
return buffer.toString();
}
/**
* <p>
* Converts a {@code long} to a {@code String} with optional zero padding.
* </p>
*
* @param value the value to convert
* @param padWithZeros whether to pad with zeroes
* @param count the size to pad to (ignored if {@code padWithZeros} is
* false)
* @return the string result
*/
private static String paddedValue(final long value, final boolean padWithZeros, final int count) {
final String longString = Long.toString(value);
return padWithZeros ? StringUtils.leftPad(longString, count, '0') : longString;
}
static final String y = "y";
static final String M = "M";
static final String d = "d";
static final String H = "H";
static final String m = "m";
static final String s = "s";
static final String S = "S";
/**
* Parses a classic date format string into Tokens
*
* @param format the format to parse, not null
* @return array of Token[]
*/
static Token[] lexx(final String format) {
final ArrayList<Token> list = new ArrayList<>(format.length());
boolean inLiteral = false;
// Although the buffer is stored in a Token, the Tokens are only
// used internally, so cannot be accessed by other threads
StringBuilder buffer = null;
Token previous = null;
for (int i = 0; i < format.length(); i++) {
final char ch = format.charAt(i);
if (inLiteral && ch != '\'') {
buffer.append(ch); // buffer can't be null if inLiteral is true
continue;
}
String value = null;
switch (ch) {
// TODO: Need to handle escaping of '
case '\'':
if (inLiteral) {
buffer = null;
inLiteral = false;
} else {
buffer = new StringBuilder();
list.add(new Token(buffer));
inLiteral = true;
}
break;
case 'y':
value = y;
break;
case 'M':
value = M;
break;
case 'd':
value = d;
break;
case 'H':
value = H;
break;
case 'm':
value = m;
break;
case 's':
value = s;
break;
case 'S':
value = S;
break;
default:
if (buffer == null) {
buffer = new StringBuilder();
list.add(new Token(buffer));
}
buffer.append(ch);
}
if (value != null) {
if (previous != null && previous.getValue().equals(value)) {
previous.increment();
} else {
final Token token = new Token(value);
list.add(token);
previous = token;
}
buffer = null;
}
}
if (inLiteral) { // i.e. we have not found the end of the literal
throw new IllegalArgumentException("Unmatched quote in format: " + format);
}
return list.toArray(Token.EMPTY_ARRAY);
}
/**
* Element that is parsed from the format pattern.
*/
static class Token {
/** Empty array. */
private static final Token[] EMPTY_ARRAY = new Token[0];
/**
* Helper method to determine if a set of tokens contain a value
*
* @param tokens set to look in
* @param value to look for
* @return boolean {@code true} if contained
*/
static boolean containsTokenWithValue(final Token[] tokens, final Object value) {
for (final Token token : tokens) {
if (token.getValue() == value) {
return true;
}
}
return false;
}
private final Object value;
private int count;
/**
* Wraps a token around a value. A value would be something like a 'Y'.
*
* @param value to wrap
*/
Token(final Object value) {
this.value = value;
this.count = 1;
}
/**
* Wraps a token around a repeated number of a value, for example it would store
* 'yyyy' as a value for y and a count of 4.
*
* @param value to wrap
* @param count to wrap
*/
Token(final Object value, final int count) {
this.value = value;
this.count = count;
}
/**
* Adds another one of the value
*/
void increment() {
count++;
}
/**
* Gets the current number of values represented
*
* @return int number of values represented
*/
int getCount() {
return count;
}
/**
* Gets the particular value this token represents.
*
* @return Object value
*/
Object getValue() {
return value;
}
/**
* Supports equality of this Token to another Token.
*
* @param obj2 Object to consider equality of
* @return boolean {@code true} if equal
*/
@Override
public boolean equals(final Object obj2) {
if (obj2 instanceof Token) {
final Token tok2 = (Token) obj2;
if (this.value.getClass() != tok2.value.getClass()) {
return false;
}
if (this.count != tok2.count) {
return false;
}
if (this.value instanceof StringBuilder) {
return this.value.toString().equals(tok2.value.toString());
} else if (this.value instanceof Number) {
return this.value.equals(tok2.value);
} else {
return this.value == tok2.value;
}
}
return false;
}
/**
* Returns a hash code for the token equal to the hash code for the token's
* value. Thus 'TT' and 'TTTT' will have the same hash code.
*
* @return The hash code for the token
*/
@Override
public int hashCode() {
return this.value.hashCode();
}
/**
* Represents this token as a String.
*
* @return String representation of the token
*/
@Override
public String toString() {
return StringUtils.repeat(this.value.toString(), this.count);
}
}
}

View File

@ -0,0 +1,161 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.time;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.Range;
import org.apache.commons.lang3.function.FailableBiConsumer;
import org.apache.commons.lang3.math.NumberUtils;
/**
* Utilities for {@link Duration}.
*
* @since 3.12.0
*/
public class DurationUtils {
/**
* An Integer Range that accepts Longs.
*/
static final Range<Long> LONG_TO_INT_RANGE = Range.between(NumberUtils.LONG_INT_MIN_VALUE,
NumberUtils.LONG_INT_MAX_VALUE);
/**
* Accepts the function with the duration as a long milliseconds and int
* nanoseconds.
*
* @param <T> The function exception.
* @param consumer Accepting function.
* @param duration The duration to pick apart.
* @throws T See the function signature.
*/
public static <T extends Throwable> void accept(final FailableBiConsumer<Long, Integer, T> consumer,
final Duration duration) throws T {
if (consumer != null && duration != null) {
consumer.accept(duration.toMillis(), getNanosOfMiili(duration));
}
}
/**
* Gets the nanosecond part of a Duration converted to milliseconds.
* <p>
* Handy when calling an API that takes a long of milliseconds and an int of
* nanoseconds. For example, {@link Object#wait(long, int)} and
* {@link Thread#sleep(long, int)}.
* </p>
* <p>
* Note that is this different from {@link Duration#getNano()} because a
* duration are seconds and nanoseconds.
* </p>
*
* @param duration The duration to query.
* @return nanoseconds between 0 and 999,999.
*/
public static int getNanosOfMiili(final Duration duration) {
return duration.getNano() % 1_000_000;
}
/**
* Tests whether the given Duration is positive (&gt;0).
*
* @param duration the value to test
* @return whether the given Duration is positive (&gt;0).
*/
public static boolean isPositive(final Duration duration) {
return !duration.isNegative() && !duration.isZero();
}
/**
* Converts a {@link TimeUnit} to a {@link ChronoUnit}.
*
* @param timeUnit A non-null TimeUnit.
* @return The corresponding ChronoUnit.
*/
static ChronoUnit toChronoUnit(final TimeUnit timeUnit) {
// TODO when using Java >= 9: Use TimeUnit.toChronoUnit().
switch (Objects.requireNonNull(timeUnit)) {
case NANOSECONDS:
return ChronoUnit.NANOS;
case MICROSECONDS:
return ChronoUnit.MICROS;
case MILLISECONDS:
return ChronoUnit.MILLIS;
case SECONDS:
return ChronoUnit.SECONDS;
case MINUTES:
return ChronoUnit.MINUTES;
case HOURS:
return ChronoUnit.HOURS;
case DAYS:
return ChronoUnit.DAYS;
default:
throw new IllegalArgumentException(timeUnit.toString());
}
}
/**
* Converts an amount and TimeUnit into a Duration.
*
* @param amount the amount of the duration, measured in terms of the unit,
* positive or negative
* @param timeUnit the unit that the duration is measured in, must have an exact
* duration, not null
* @return a Duration.
*/
public static Duration toDuration(final long amount, final TimeUnit timeUnit) {
return Duration.of(amount, toChronoUnit(timeUnit));
}
/**
* Converts a Duration to milliseconds bound to an int (instead of a long).
* <p>
* Handy for low-level APIs that take millisecond timeouts in ints rather than
* longs.
* </p>
* <ul>
* <li>If the duration milliseconds are greater than {@link Integer#MAX_VALUE},
* then return {@link Integer#MAX_VALUE}.</li>
* <li>If the duration milliseconds are lesser than {@link Integer#MIN_VALUE},
* then return {@link Integer#MIN_VALUE}.</li>
* </ul>
*
* @param duration The duration to convert, not null.
* @return int milliseconds.
*/
public static int toMillisInt(final Duration duration) {
Objects.requireNonNull(duration, "duration");
// intValue() does not do a narrowing conversion here
return LONG_TO_INT_RANGE.fit(Long.valueOf(duration.toMillis())).intValue();
}
/**
* Returns the given non-null value or {@link Duration#ZERO} if null.
*
* @param duration The duration to test.
* @return The given duration or {@link Duration#ZERO}.
*/
public static Duration zeroIfNull(final Duration duration) {
return ObjectUtils.defaultIfNull(duration, Duration.ZERO);
}
}

View File

@ -0,0 +1,757 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.time;
import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* <p>
* FastDateFormat is a fast and thread-safe version of
* {@link java.text.SimpleDateFormat}.
* </p>
*
* <p>
* To obtain an instance of FastDateFormat, use one of the static factory
* methods: {@link #getInstance(String, TimeZone, Locale)},
* {@link #getDateInstance(int, TimeZone, Locale)},
* {@link #getTimeInstance(int, TimeZone, Locale)}, or
* {@link #getDateTimeInstance(int, int, TimeZone, Locale)}
* </p>
*
* <p>
* Since FastDateFormat is thread safe, you can use a static member instance:
* </p>
* <code>
* private static final FastDateFormat DATE_FORMATTER = FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.SHORT);
* </code>
*
* <p>
* This class can be used as a direct replacement to {@code SimpleDateFormat} in
* most formatting and parsing situations. This class is especially useful in
* multi-threaded server environments. {@code SimpleDateFormat} is not
* thread-safe in any JDK version, nor will it be as Sun have closed the
* bug/RFE.
* </p>
*
* <p>
* All patterns are compatible with SimpleDateFormat (except time zones and some
* year patterns - see below).
* </p>
*
* <p>
* Since 3.2, FastDateFormat supports parsing as well as printing.
* </p>
*
* <p>
* Java 1.4 introduced a new pattern letter, {@code 'Z'}, to represent time
* zones in RFC822 format (eg. {@code +0800} or {@code -1100}). This pattern
* letter can be used here (on all JDK versions).
* </p>
*
* <p>
* In addition, the pattern {@code 'ZZ'} has been made to represent ISO 8601
* extended format time zones (eg. {@code +08:00} or {@code -11:00}). This
* introduces a minor incompatibility with Java 1.4, but at a gain of useful
* functionality.
* </p>
*
* <p>
* Javadoc cites for the year pattern: <i>For formatting, if the number of
* pattern letters is 2, the year is truncated to 2 digits; otherwise it is
* interpreted as a number.</i> Starting with Java 1.7 a pattern of 'Y' or 'YYY'
* will be formatted as '2003', while it was '03' in former Java versions.
* FastDateFormat implements the behavior of Java 7.
* </p>
*
* @since 2.0
*/
public class FastDateFormat extends Format implements DateParser, DatePrinter {
/**
* Required for serialization support.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = 2L;
/**
* FULL locale dependent date or time style.
*/
public static final int FULL = DateFormat.FULL;
/**
* LONG locale dependent date or time style.
*/
public static final int LONG = DateFormat.LONG;
/**
* MEDIUM locale dependent date or time style.
*/
public static final int MEDIUM = DateFormat.MEDIUM;
/**
* SHORT locale dependent date or time style.
*/
public static final int SHORT = DateFormat.SHORT;
private static final FormatCache<FastDateFormat> cache = new FormatCache<FastDateFormat>() {
@Override
protected FastDateFormat createInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
return new FastDateFormat(pattern, timeZone, locale);
}
};
private final FastDatePrinter printer;
private final FastDateParser parser;
// -----------------------------------------------------------------------
/**
* <p>
* Gets a formatter instance using the default pattern in the default locale.
* </p>
*
* @return a date/time formatter
*/
public static FastDateFormat getInstance() {
return cache.getInstance();
}
/**
* <p>
* Gets a formatter instance using the specified pattern in the default locale.
* </p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(final String pattern) {
return cache.getInstance(pattern, null, null);
}
/**
* <p>
* Gets a formatter instance using the specified pattern and time zone.
* </p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern
* @param timeZone optional time zone, overrides time zone of formatted date
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone) {
return cache.getInstance(pattern, timeZone, null);
}
/**
* <p>
* Gets a formatter instance using the specified pattern and locale.
* </p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern
* @param locale optional locale, overrides system locale
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(final String pattern, final Locale locale) {
return cache.getInstance(pattern, null, locale);
}
/**
* <p>
* Gets a formatter instance using the specified pattern, time zone and locale.
* </p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern
* @param timeZone optional time zone, overrides time zone of formatted date
* @param locale optional locale, overrides system locale
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid or {@code null}
*/
public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
return cache.getInstance(pattern, timeZone, locale);
}
// -----------------------------------------------------------------------
/**
* <p>
* Gets a date formatter instance using the specified style in the default time
* zone and locale.
* </p>
*
* @param style date style: FULL, LONG, MEDIUM, or SHORT
* @return a localized standard date formatter
* @throws IllegalArgumentException if the Locale has no date pattern defined
* @since 2.1
*/
public static FastDateFormat getDateInstance(final int style) {
return cache.getDateInstance(style, null, null);
}
/**
* <p>
* Gets a date formatter instance using the specified style and locale in the
* default time zone.
* </p>
*
* @param style date style: FULL, LONG, MEDIUM, or SHORT
* @param locale optional locale, overrides system locale
* @return a localized standard date formatter
* @throws IllegalArgumentException if the Locale has no date pattern defined
* @since 2.1
*/
public static FastDateFormat getDateInstance(final int style, final Locale locale) {
return cache.getDateInstance(style, null, locale);
}
/**
* <p>
* Gets a date formatter instance using the specified style and time zone in the
* default locale.
* </p>
*
* @param style date style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of formatted date
* @return a localized standard date formatter
* @throws IllegalArgumentException if the Locale has no date pattern defined
* @since 2.1
*/
public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone) {
return cache.getDateInstance(style, timeZone, null);
}
/**
* <p>
* Gets a date formatter instance using the specified style, time zone and
* locale.
* </p>
*
* @param style date style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of formatted date
* @param locale optional locale, overrides system locale
* @return a localized standard date formatter
* @throws IllegalArgumentException if the Locale has no date pattern defined
*/
public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone, final Locale locale) {
return cache.getDateInstance(style, timeZone, locale);
}
// -----------------------------------------------------------------------
/**
* <p>
* Gets a time formatter instance using the specified style in the default time
* zone and locale.
* </p>
*
* @param style time style: FULL, LONG, MEDIUM, or SHORT
* @return a localized standard time formatter
* @throws IllegalArgumentException if the Locale has no time pattern defined
* @since 2.1
*/
public static FastDateFormat getTimeInstance(final int style) {
return cache.getTimeInstance(style, null, null);
}
/**
* <p>
* Gets a time formatter instance using the specified style and locale in the
* default time zone.
* </p>
*
* @param style time style: FULL, LONG, MEDIUM, or SHORT
* @param locale optional locale, overrides system locale
* @return a localized standard time formatter
* @throws IllegalArgumentException if the Locale has no time pattern defined
* @since 2.1
*/
public static FastDateFormat getTimeInstance(final int style, final Locale locale) {
return cache.getTimeInstance(style, null, locale);
}
/**
* <p>
* Gets a time formatter instance using the specified style and time zone in the
* default locale.
* </p>
*
* @param style time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of formatted time
* @return a localized standard time formatter
* @throws IllegalArgumentException if the Locale has no time pattern defined
* @since 2.1
*/
public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone) {
return cache.getTimeInstance(style, timeZone, null);
}
/**
* <p>
* Gets a time formatter instance using the specified style, time zone and
* locale.
* </p>
*
* @param style time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of formatted time
* @param locale optional locale, overrides system locale
* @return a localized standard time formatter
* @throws IllegalArgumentException if the Locale has no time pattern defined
*/
public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone, final Locale locale) {
return cache.getTimeInstance(style, timeZone, locale);
}
// -----------------------------------------------------------------------
/**
* <p>
* Gets a date/time formatter instance using the specified style in the default
* time zone and locale.
* </p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time pattern
* defined
* @since 2.1
*/
public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle) {
return cache.getDateTimeInstance(dateStyle, timeStyle, null, null);
}
/**
* <p>
* Gets a date/time formatter instance using the specified style and locale in
* the default time zone.
* </p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time pattern
* defined
* @since 2.1
*/
public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final Locale locale) {
return cache.getDateTimeInstance(dateStyle, timeStyle, null, locale);
}
/**
* <p>
* Gets a date/time formatter instance using the specified style and time zone
* in the default locale.
* </p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of formatted date
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time pattern
* defined
* @since 2.1
*/
public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle,
final TimeZone timeZone) {
return getDateTimeInstance(dateStyle, timeStyle, timeZone, null);
}
/**
* <p>
* Gets a date/time formatter instance using the specified style, time zone and
* locale.
* </p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of formatted date
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time pattern
* defined
*/
public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone,
final Locale locale) {
return cache.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale);
}
// Constructor
// -----------------------------------------------------------------------
/**
* <p>
* Constructs a new FastDateFormat.
* </p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale to use
* @throws NullPointerException if pattern, timeZone, or locale is null.
*/
protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale) {
this(pattern, timeZone, locale, null);
}
// Constructor
// -----------------------------------------------------------------------
/**
* <p>
* Constructs a new FastDateFormat.
* </p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale to use
* @param centuryStart The start of the 100 year period to use as the "default
* century" for 2 digit year parsing. If centuryStart is
* null, defaults to now - 80 years
* @throws NullPointerException if pattern, timeZone, or locale is null.
*/
protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale,
final Date centuryStart) {
printer = new FastDatePrinter(pattern, timeZone, locale);
parser = new FastDateParser(pattern, timeZone, locale, centuryStart);
}
// Format methods
// -----------------------------------------------------------------------
/**
* <p>
* Formats a {@code Date}, {@code Calendar} or {@code Long} (milliseconds)
* object.
* </p>
* This method is an implementation of
* {@link Format#format(Object, StringBuffer, FieldPosition)}
*
* @param obj the object to format
* @param toAppendTo the buffer to append to
* @param pos the position - ignored
* @return the buffer passed in
*/
@Override
public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
return toAppendTo.append(printer.format(obj));
}
/**
* <p>
* Formats a millisecond {@code long} value.
* </p>
*
* @param millis the millisecond value to format
* @return the formatted string
* @since 2.1
*/
@Override
public String format(final long millis) {
return printer.format(millis);
}
/**
* <p>
* Formats a {@code Date} object using a {@code GregorianCalendar}.
* </p>
*
* @param date the date to format
* @return the formatted string
*/
@Override
public String format(final Date date) {
return printer.format(date);
}
/**
* <p>
* Formats a {@code Calendar} object.
* </p>
*
* @param calendar the calendar to format
* @return the formatted string
*/
@Override
public String format(final Calendar calendar) {
return printer.format(calendar);
}
/**
* <p>
* Formats a millisecond {@code long} value into the supplied
* {@code StringBuffer}.
* </p>
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @return the specified string buffer
* @since 2.1
* @deprecated Use {{@link #format(long, Appendable)}.
*/
@Deprecated
@Override
public StringBuffer format(final long millis, final StringBuffer buf) {
return printer.format(millis, buf);
}
/**
* <p>
* Formats a {@code Date} object into the supplied {@code StringBuffer} using a
* {@code GregorianCalendar}.
* </p>
*
* @param date the date to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {{@link #format(Date, Appendable)}.
*/
@Deprecated
@Override
public StringBuffer format(final Date date, final StringBuffer buf) {
return printer.format(date, buf);
}
/**
* <p>
* Formats a {@code Calendar} object into the supplied {@code StringBuffer}.
* </p>
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {{@link #format(Calendar, Appendable)}.
*/
@Deprecated
@Override
public StringBuffer format(final Calendar calendar, final StringBuffer buf) {
return printer.format(calendar, buf);
}
/**
* <p>
* Formats a millisecond {@code long} value into the supplied
* {@code StringBuffer}.
* </p>
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @return the specified string buffer
* @since 3.5
*/
@Override
public <B extends Appendable> B format(final long millis, final B buf) {
return printer.format(millis, buf);
}
/**
* <p>
* Formats a {@code Date} object into the supplied {@code StringBuffer} using a
* {@code GregorianCalendar}.
* </p>
*
* @param date the date to format
* @param buf the buffer to format into
* @return the specified string buffer
* @since 3.5
*/
@Override
public <B extends Appendable> B format(final Date date, final B buf) {
return printer.format(date, buf);
}
/**
* <p>
* Formats a {@code Calendar} object into the supplied {@code StringBuffer}.
* </p>
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @return the specified string buffer
* @since 3.5
*/
@Override
public <B extends Appendable> B format(final Calendar calendar, final B buf) {
return printer.format(calendar, buf);
}
// Parsing
// -----------------------------------------------------------------------
/*
* (non-Javadoc)
*
* @see DateParser#parse(java.lang.String)
*/
@Override
public Date parse(final String source) throws ParseException {
return parser.parse(source);
}
/*
* (non-Javadoc)
*
* @see DateParser#parse(java.lang.String, java.text.ParsePosition)
*/
@Override
public Date parse(final String source, final ParsePosition pos) {
return parser.parse(source, pos);
}
/*
* (non-Javadoc)
*
* @see org.apache.commons.lang3.time.DateParser#parse(java.lang.String,
* java.text.ParsePosition, java.util.Calendar)
*/
@Override
public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) {
return parser.parse(source, pos, calendar);
}
/*
* (non-Javadoc)
*
* @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
*/
@Override
public Object parseObject(final String source, final ParsePosition pos) {
return parser.parseObject(source, pos);
}
// Accessors
// -----------------------------------------------------------------------
/**
* <p>
* Gets the pattern used by this formatter.
* </p>
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/
@Override
public String getPattern() {
return printer.getPattern();
}
/**
* <p>
* Gets the time zone used by this formatter.
* </p>
*
* <p>
* This zone is always used for {@code Date} formatting.
* </p>
*
* @return the time zone
*/
@Override
public TimeZone getTimeZone() {
return printer.getTimeZone();
}
/**
* <p>
* Gets the locale used by this formatter.
* </p>
*
* @return the locale
*/
@Override
public Locale getLocale() {
return printer.getLocale();
}
/**
* <p>
* Gets an estimate for the maximum string length that the formatter will
* produce.
* </p>
*
* <p>
* The actual formatted length will almost always be less than or equal to this
* amount.
* </p>
*
* @return the maximum formatted length
*/
public int getMaxLengthEstimate() {
return printer.getMaxLengthEstimate();
}
// Basics
// -----------------------------------------------------------------------
/**
* <p>
* Compares two objects for equality.
* </p>
*
* @param obj the object to compare to
* @return {@code true} if equal
*/
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof FastDateFormat)) {
return false;
}
final FastDateFormat other = (FastDateFormat) obj;
// no need to check parser, as it has same invariants as printer
return printer.equals(other.printer);
}
/**
* <p>
* Returns a hash code compatible with equals.
* </p>
*
* @return a hash code compatible with equals
*/
@Override
public int hashCode() {
return printer.hashCode();
}
/**
* <p>
* Gets a debugging string version of this formatter.
* </p>
*
* @return a debugging string
*/
@Override
public String toString() {
return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + ","
+ printer.getTimeZone().getID() + "]";
}
/**
* <p>
* Performs the formatting by applying the rules to the specified calendar.
* </p>
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @return the specified string buffer
* @deprecated Use {@link #format(Calendar, Appendable)}
*/
@Deprecated
protected StringBuffer applyRules(final Calendar calendar, final StringBuffer buf) {
return printer.applyRules(calendar, buf);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,97 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.time;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Faster methods to produce custom time zones.
*
* @since 3.7
*/
public class FastTimeZone {
private static final Pattern GMT_PATTERN = Pattern.compile("^(?:(?i)GMT)?([+-])?(\\d\\d?)?(:?(\\d\\d?))?$");
private static final TimeZone GREENWICH = new GmtTimeZone(false, 0, 0);
/**
* Gets the GMT TimeZone.
*
* @return A TimeZone with a raw offset of zero.
*/
public static TimeZone getGmtTimeZone() {
return GREENWICH;
}
/**
* Gets a TimeZone with GMT offsets. A GMT offset must be either 'Z', or 'UTC',
* or match <em>(GMT)? hh?(:?mm?)?</em>, where h and m are digits representing
* hours and minutes.
*
* @param pattern The GMT offset
* @return A TimeZone with offset from GMT or null, if pattern does not match.
*/
public static TimeZone getGmtTimeZone(final String pattern) {
if ("Z".equals(pattern) || "UTC".equals(pattern)) {
return GREENWICH;
}
final Matcher m = GMT_PATTERN.matcher(pattern);
if (m.matches()) {
final int hours = parseInt(m.group(2));
final int minutes = parseInt(m.group(4));
if (hours == 0 && minutes == 0) {
return GREENWICH;
}
return new GmtTimeZone(parseSign(m.group(1)), hours, minutes);
}
return null;
}
/**
* Gets a TimeZone, looking first for GMT custom ids, then falling back to Olson
* ids. A GMT custom id can be 'Z', or 'UTC', or has an optional prefix of GMT,
* followed by sign, hours digit(s), optional colon(':'), and optional minutes
* digits. i.e. <em>[GMT] (+|-) Hours [[:] Minutes]</em>
*
* @param id A GMT custom id (or Olson id
* @return A time zone
*/
public static TimeZone getTimeZone(final String id) {
final TimeZone tz = getGmtTimeZone(id);
if (tz != null) {
return tz;
}
return TimeZone.getTimeZone(id);
}
private static int parseInt(final String group) {
return group != null ? Integer.parseInt(group) : 0;
}
private static boolean parseSign(final String group) {
return group != null && group.charAt(0) == '-';
}
// do not instantiate
private FastTimeZone() {
}
}

View File

@ -0,0 +1,270 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.time;
import java.text.DateFormat;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import org.apache.commons.lang3.LocaleUtils;
import org.apache.commons.lang3.Validate;
/**
* FormatCache is a cache and factory for {@link Format}s.
*
* @since 3.0
*/
// TODO: Before making public move from getDateTimeInstance(Integer, ...) to int; or some other approach.
abstract class FormatCache<F extends Format> {
/**
* No date or no time. Used in same parameters as DateFormat.SHORT or
* DateFormat.LONG
*/
static final int NONE = -1;
private final Map<ArrayKey, F> cInstanceCache = new HashMap<>(7);
private static final Map<ArrayKey, String> cDateTimeInstanceCache = new HashMap<>(7);
/**
* Gets a formatter instance using the default pattern in the default time zone
* and locale.
*
* @return a date/time formatter
*/
public F getInstance() {
return getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, TimeZone.getDefault(), Locale.getDefault());
}
/**
* Gets a formatter instance using the specified pattern, time zone and locale.
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern,
* non-null
* @param timeZone the time zone, null means use the default TimeZone
* @param locale the locale, null means use the default Locale
* @return a pattern based date/time formatter
* @throws NullPointerException if pattern is {@code null}
* @throws IllegalArgumentException if pattern is invalid
*/
public F getInstance(final String pattern, TimeZone timeZone, Locale locale) {
Validate.notNull(pattern, "pattern");
if (timeZone == null) {
timeZone = TimeZone.getDefault();
}
locale = LocaleUtils.toLocale(locale);
final ArrayKey key = new ArrayKey(pattern, timeZone, locale);
F format = cInstanceCache.get(key);
if (format == null) {
format = createInstance(pattern, timeZone, locale);
final F previousValue = cInstanceCache.putIfAbsent(key, format);
if (previousValue != null) {
// another thread snuck in and did the same work
// we should return the instance that is in ConcurrentMap
format = previousValue;
}
}
return format;
}
/**
* Create a format instance using the specified pattern, time zone and locale.
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern, this
* will not be null.
* @param timeZone time zone, this will not be null.
* @param locale locale, this will not be null.
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid or {@code null}
*/
protected abstract F createInstance(String pattern, TimeZone timeZone, Locale locale);
/**
* Gets a date/time formatter instance using the specified style, time zone and
* locale.
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no
* date in format
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no
* time in format
* @param timeZone optional time zone, overrides time zone of formatted date,
* null means use default Locale
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time pattern
* defined
*/
// This must remain private, see LANG-884
private F getDateTimeInstance(final Integer dateStyle, final Integer timeStyle, final TimeZone timeZone,
Locale locale) {
locale = LocaleUtils.toLocale(locale);
final String pattern = getPatternForStyle(dateStyle, timeStyle, locale);
return getInstance(pattern, timeZone, locale);
}
/**
* Gets a date/time formatter instance using the specified style, time zone and
* locale.
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of formatted date,
* null means use default Locale
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time pattern
* defined
*/
// package protected, for access from FastDateFormat; do not make public or
// protected
F getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) {
return getDateTimeInstance(Integer.valueOf(dateStyle), Integer.valueOf(timeStyle), timeZone, locale);
}
/**
* Gets a date formatter instance using the specified style, time zone and
* locale.
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of formatted date,
* null means use default Locale
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time pattern
* defined
*/
// package protected, for access from FastDateFormat; do not make public or
// protected
F getDateInstance(final int dateStyle, final TimeZone timeZone, final Locale locale) {
return getDateTimeInstance(Integer.valueOf(dateStyle), null, timeZone, locale);
}
/**
* Gets a time formatter instance using the specified style, time zone and
* locale.
*
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of formatted date,
* null means use default Locale
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time pattern
* defined
*/
// package protected, for access from FastDateFormat; do not make public or
// protected
F getTimeInstance(final int timeStyle, final TimeZone timeZone, final Locale locale) {
return getDateTimeInstance(null, Integer.valueOf(timeStyle), timeZone, locale);
}
/**
* Gets a date/time format for the specified styles and locale.
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no
* date in format
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no
* time in format
* @param locale The non-null locale of the desired format
* @return a localized standard date/time format
* @throws IllegalArgumentException if the Locale has no date/time pattern
* defined
*/
// package protected, for access from test code; do not make public or protected
static String getPatternForStyle(final Integer dateStyle, final Integer timeStyle, final Locale locale) {
final Locale safeLocale = LocaleUtils.toLocale(locale);
final ArrayKey key = new ArrayKey(dateStyle, timeStyle, safeLocale);
String pattern = cDateTimeInstanceCache.get(key);
if (pattern == null) {
try {
final DateFormat formatter;
if (dateStyle == null) {
formatter = DateFormat.getTimeInstance(timeStyle.intValue(), safeLocale);
} else if (timeStyle == null) {
formatter = DateFormat.getDateInstance(dateStyle.intValue(), safeLocale);
} else {
formatter = DateFormat.getDateTimeInstance(dateStyle.intValue(), timeStyle.intValue(), safeLocale);
}
pattern = ((SimpleDateFormat) formatter).toPattern();
final String previous = cDateTimeInstanceCache.putIfAbsent(key, pattern);
if (previous != null) {
// even though it doesn't matter if another thread put the pattern
// it's still good practice to return the String instance that is
// actually in the ConcurrentMap
pattern = previous;
}
} catch (final ClassCastException ex) {
throw new IllegalArgumentException("No date time pattern for locale: " + safeLocale);
}
}
return pattern;
}
/**
* Helper class to hold multi-part Map keys as arrays.
*/
private static final class ArrayKey {
private static int computeHashCode(final Object[] keys) {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(keys);
return result;
}
private final Object[] keys;
private final int hashCode;
/**
* Constructs an instance of {@code MultipartKey} to hold the specified objects.
*
* @param keys the set of objects that make up the key. Each key may be null.
*/
ArrayKey(final Object... keys) {
this.keys = keys;
this.hashCode = computeHashCode(keys);
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ArrayKey other = (ArrayKey) obj;
return Arrays.deepEquals(keys, other.keys);
}
}
}

View File

@ -0,0 +1,105 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.time;
import java.util.Date;
import java.util.TimeZone;
/**
* Custom time zone that contains offset from GMT.
*
* @since 3.7
*/
class GmtTimeZone extends TimeZone {
private static final int MILLISECONDS_PER_MINUTE = 60 * 1000;
private static final int MINUTES_PER_HOUR = 60;
private static final int HOURS_PER_DAY = 24;
// Serializable!
static final long serialVersionUID = 1L;
private final int offset;
private final String zoneId;
GmtTimeZone(final boolean negate, final int hours, final int minutes) {
if (hours >= HOURS_PER_DAY) {
throw new IllegalArgumentException(hours + " hours out of range");
}
if (minutes >= MINUTES_PER_HOUR) {
throw new IllegalArgumentException(minutes + " minutes out of range");
}
final int milliseconds = (minutes + (hours * MINUTES_PER_HOUR)) * MILLISECONDS_PER_MINUTE;
offset = negate ? -milliseconds : milliseconds;
zoneId = twoDigits(twoDigits(new StringBuilder(9).append("GMT").append(negate ? '-' : '+'), hours).append(':'),
minutes).toString();
}
private static StringBuilder twoDigits(final StringBuilder sb, final int n) {
return sb.append((char) ('0' + (n / 10))).append((char) ('0' + (n % 10)));
}
@Override
public int getOffset(final int era, final int year, final int month, final int day, final int dayOfWeek,
final int milliseconds) {
return offset;
}
@Override
public void setRawOffset(final int offsetMillis) {
throw new UnsupportedOperationException();
}
@Override
public int getRawOffset() {
return offset;
}
@Override
public String getID() {
return zoneId;
}
@Override
public boolean useDaylightTime() {
return false;
}
@Override
public boolean inDaylightTime(final Date date) {
return false;
}
@Override
public String toString() {
return "[GmtTimeZone id=\"" + zoneId + "\",offset=" + offset + ']';
}
@Override
public int hashCode() {
return offset;
}
@Override
public boolean equals(final Object other) {
if (!(other instanceof GmtTimeZone)) {
return false;
}
return zoneId == ((GmtTimeZone) other).zoneId;
}
}

View File

@ -0,0 +1,650 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.time;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
/**
* <p>
* {@code StopWatch} provides a convenient API for timings.
* </p>
*
* <p>
* To start the watch, call {@link #start()} or
* {@link StopWatch#createStarted()}. At this point you can:
* </p>
* <ul>
* <li>{@link #split()} the watch to get the time whilst the watch continues in
* the background. {@link #unsplit()} will remove the effect of the split. At
* this point, these three options are available again.</li>
* <li>{@link #suspend()} the watch to pause it. {@link #resume()} allows the
* watch to continue. Any time between the suspend and resume will not be
* counted in the total. At this point, these three options are available
* again.</li>
* <li>{@link #stop()} the watch to complete the timing session.</li>
* </ul>
*
* <p>
* It is intended that the output methods {@link #toString()} and
* {@link #getTime()} should only be called after stop, split or suspend,
* however a suitable result will be returned at other points.
* </p>
*
* <p>
* NOTE: As from v2.1, the methods protect against inappropriate calls. Thus you
* cannot now call stop before start, resume before suspend or unsplit before
* split.
* </p>
*
* <p>
* 1. split(), suspend(), or stop() cannot be invoked twice<br>
* 2. unsplit() may only be called if the watch has been split()<br>
* 3. resume() may only be called if the watch has been suspend()<br>
* 4. start() cannot be called twice without calling reset()
* </p>
*
* <p>
* This class is not thread-safe
* </p>
*
* @since 2.0
*/
public class StopWatch {
/**
* Enumeration type which indicates the split status of stopwatch.
*/
private enum SplitState {
SPLIT, UNSPLIT
}
/**
* Enumeration type which indicates the status of stopwatch.
*/
private enum State {
RUNNING {
@Override
boolean isStarted() {
return true;
}
@Override
boolean isStopped() {
return false;
}
@Override
boolean isSuspended() {
return false;
}
},
STOPPED {
@Override
boolean isStarted() {
return false;
}
@Override
boolean isStopped() {
return true;
}
@Override
boolean isSuspended() {
return false;
}
},
SUSPENDED {
@Override
boolean isStarted() {
return true;
}
@Override
boolean isStopped() {
return false;
}
@Override
boolean isSuspended() {
return true;
}
},
UNSTARTED {
@Override
boolean isStarted() {
return false;
}
@Override
boolean isStopped() {
return true;
}
@Override
boolean isSuspended() {
return false;
}
};
/**
* <p>
* Returns whether the StopWatch is started. A suspended StopWatch is also
* started watch.
* </p>
*
* @return boolean If the StopWatch is started.
*/
abstract boolean isStarted();
/**
* <p>
* Returns whether the StopWatch is stopped. The stopwatch which's not yet
* started and explicitly stopped stopwatch is considered as stopped.
* </p>
*
* @return boolean If the StopWatch is stopped.
*/
abstract boolean isStopped();
/**
* <p>
* Returns whether the StopWatch is suspended.
* </p>
*
* @return boolean If the StopWatch is suspended.
*/
abstract boolean isSuspended();
}
private static final long NANO_2_MILLIS = 1000000L;
/**
* Creates a stopwatch for convenience.
*
* @return StopWatch a stopwatch.
*
* @since 3.10
*/
public static StopWatch create() {
return new StopWatch();
}
/**
* Creates a started stopwatch for convenience.
*
* @return StopWatch a stopwatch that's already been started.
*
* @since 3.5
*/
public static StopWatch createStarted() {
final StopWatch sw = new StopWatch();
sw.start();
return sw;
}
/**
* A message for string presentation.
*
* @since 3.10
*/
private final String message;
/**
* The current running state of the StopWatch.
*/
private State runningState = State.UNSTARTED;
/**
* Whether the stopwatch has a split time recorded.
*/
private SplitState splitState = SplitState.UNSPLIT;
/**
* The start time in nanoseconds.
*/
private long startTimeNanos;
/**
* The start time in milliseconds - nanoTime is only for elapsed time so we need
* to also store the currentTimeMillis to maintain the old getStartTime API.
*/
private long startTimeMillis;
/**
* The end time in milliseconds - nanoTime is only for elapsed time so we need
* to also store the currentTimeMillis to maintain the old getStartTime API.
*/
private long stopTimeMillis;
/**
* The stop time in nanoseconds.
*/
private long stopTimeNanos;
/**
* <p>
* Constructor.
* </p>
*/
public StopWatch() {
this(null);
}
/**
* <p>
* Constructor.
* </p>
*
* @param message A message for string presentation.
* @since 3.10
*/
public StopWatch(final String message) {
this.message = message;
}
/**
* Returns the time formatted by {@link DurationFormatUtils#formatDurationHMS}.
*
* @return the time formatted by {@link DurationFormatUtils#formatDurationHMS}.
* @since 3.10
*/
public String formatSplitTime() {
return DurationFormatUtils.formatDurationHMS(getSplitTime());
}
/**
* Returns the split time formatted by
* {@link DurationFormatUtils#formatDurationHMS}.
*
* @return the split time formatted by
* {@link DurationFormatUtils#formatDurationHMS}.
* @since 3.10
*/
public String formatTime() {
return DurationFormatUtils.formatDurationHMS(getTime());
}
/**
* Gets the message for string presentation.
*
* @return the message for string presentation.
* @since 3.10
*/
public String getMessage() {
return message;
}
/**
* <p>
* Gets the <em>elapsed</em> time in nanoseconds.
* </p>
*
* <p>
* This is either the time between the start and the moment this method is
* called, or the amount of time between start and stop.
* </p>
*
* @return the <em>elapsed</em> time in nanoseconds.
* @see System#nanoTime()
* @since 3.0
*/
public long getNanoTime() {
if (this.runningState == State.STOPPED || this.runningState == State.SUSPENDED) {
return this.stopTimeNanos - this.startTimeNanos;
} else if (this.runningState == State.UNSTARTED) {
return 0;
} else if (this.runningState == State.RUNNING) {
return System.nanoTime() - this.startTimeNanos;
}
throw new IllegalStateException("Illegal running state has occurred.");
}
/**
* <p>
* Gets the split time in nanoseconds.
* </p>
*
* <p>
* This is the time between start and latest split.
* </p>
*
* @return the split time in nanoseconds
*
* @throws IllegalStateException if the StopWatch has not yet been split.
* @since 3.0
*/
public long getSplitNanoTime() {
if (this.splitState != SplitState.SPLIT) {
throw new IllegalStateException("Stopwatch must be split to get the split time.");
}
return this.stopTimeNanos - this.startTimeNanos;
}
/**
* <p>
* Gets the split time on the stopwatch.
* </p>
*
* <p>
* This is the time between start and latest split.
* </p>
*
* @return the split time in milliseconds
*
* @throws IllegalStateException if the StopWatch has not yet been split.
* @since 2.1
*/
public long getSplitTime() {
return getSplitNanoTime() / NANO_2_MILLIS;
}
/**
* Gets the time this stopwatch was started in milliseconds, between the current
* time and midnight, January 1, 1970 UTC.
*
* @return the time this stopwatch was started in milliseconds, between the
* current time and midnight, January 1, 1970 UTC.
* @throws IllegalStateException if this StopWatch has not been started
* @since 2.4
*/
public long getStartTime() {
if (this.runningState == State.UNSTARTED) {
throw new IllegalStateException("Stopwatch has not been started");
}
// System.nanoTime is for elapsed time
return this.startTimeMillis;
}
/**
* Gets the time this stopwatch was stopped in milliseconds, between the current
* time and midnight, January 1, 1970 UTC.
*
* @return the time this stopwatch was started in milliseconds, between the
* current time and midnight, January 1, 1970 UTC.
* @throws IllegalStateException if this StopWatch has not been started
* @since 3.12.0
*/
public long getStopTime() {
if (this.runningState == State.UNSTARTED) {
throw new IllegalStateException("Stopwatch has not been started");
}
// System.nanoTime is for elapsed time
return this.stopTimeMillis;
}
/**
* <p>
* Gets the time on the stopwatch.
* </p>
*
* <p>
* This is either the time between the start and the moment this method is
* called, or the amount of time between start and stop.
* </p>
*
* @return the time in milliseconds
*/
public long getTime() {
return getNanoTime() / NANO_2_MILLIS;
}
/**
* <p>
* Gets the time in the specified TimeUnit.
* </p>
*
* <p>
* This is either the time between the start and the moment this method is
* called, or the amount of time between start and stop. The resulting time will
* be expressed in the desired TimeUnit with any remainder rounded down. For
* example, if the specified unit is {@code TimeUnit.HOURS} and the stopwatch
* time is 59 minutes, then the result returned will be {@code 0}.
* </p>
*
* @param timeUnit the unit of time, not null
* @return the time in the specified TimeUnit, rounded down
* @since 3.5
*/
public long getTime(final TimeUnit timeUnit) {
return timeUnit.convert(getNanoTime(), TimeUnit.NANOSECONDS);
}
/**
* <p>
* Returns whether the StopWatch is started. A suspended StopWatch is also
* started watch.
* </p>
*
* @return boolean If the StopWatch is started.
* @since 3.2
*/
public boolean isStarted() {
return runningState.isStarted();
}
/**
* <p>
* Returns whether StopWatch is stopped. The stopwatch which's not yet started
* and explicitly stopped stopwatch is considered as stopped.
* </p>
*
* @return boolean If the StopWatch is stopped.
* @since 3.2
*/
public boolean isStopped() {
return runningState.isStopped();
}
/**
* <p>
* Returns whether the StopWatch is suspended.
* </p>
*
* @return boolean If the StopWatch is suspended.
* @since 3.2
*/
public boolean isSuspended() {
return runningState.isSuspended();
}
/**
* <p>
* Resets the stopwatch. Stops it if need be.
* </p>
*
* <p>
* This method clears the internal values to allow the object to be reused.
* </p>
*/
public void reset() {
this.runningState = State.UNSTARTED;
this.splitState = SplitState.UNSPLIT;
}
/**
* <p>
* Resumes the stopwatch after a suspend.
* </p>
*
* <p>
* This method resumes the watch after it was suspended. The watch will not
* include time between the suspend and resume calls in the total time.
* </p>
*
* @throws IllegalStateException if the StopWatch has not been suspended.
*/
public void resume() {
if (this.runningState != State.SUSPENDED) {
throw new IllegalStateException("Stopwatch must be suspended to resume. ");
}
this.startTimeNanos += System.nanoTime() - this.stopTimeNanos;
this.runningState = State.RUNNING;
}
/**
* <p>
* Splits the time.
* </p>
*
* <p>
* This method sets the stop time of the watch to allow a time to be extracted.
* The start time is unaffected, enabling {@link #unsplit()} to continue the
* timing from the original start point.
* </p>
*
* @throws IllegalStateException if the StopWatch is not running.
*/
public void split() {
if (this.runningState != State.RUNNING) {
throw new IllegalStateException("Stopwatch is not running. ");
}
this.stopTimeNanos = System.nanoTime();
this.splitState = SplitState.SPLIT;
}
/**
* <p>
* Starts the stopwatch.
* </p>
*
* <p>
* This method starts a new timing session, clearing any previous values.
* </p>
*
* @throws IllegalStateException if the StopWatch is already running.
*/
public void start() {
if (this.runningState == State.STOPPED) {
throw new IllegalStateException("Stopwatch must be reset before being restarted. ");
}
if (this.runningState != State.UNSTARTED) {
throw new IllegalStateException("Stopwatch already started. ");
}
this.startTimeNanos = System.nanoTime();
this.startTimeMillis = System.currentTimeMillis();
this.runningState = State.RUNNING;
}
/**
* <p>
* Stops the stopwatch.
* </p>
*
* <p>
* This method ends a new timing session, allowing the time to be retrieved.
* </p>
*
* @throws IllegalStateException if the StopWatch is not running.
*/
public void stop() {
if (this.runningState != State.RUNNING && this.runningState != State.SUSPENDED) {
throw new IllegalStateException("Stopwatch is not running. ");
}
if (this.runningState == State.RUNNING) {
this.stopTimeNanos = System.nanoTime();
this.stopTimeMillis = System.currentTimeMillis();
}
this.runningState = State.STOPPED;
}
/**
* <p>
* Suspends the stopwatch for later resumption.
* </p>
*
* <p>
* This method suspends the watch until it is resumed. The watch will not
* include time between the suspend and resume calls in the total time.
* </p>
*
* @throws IllegalStateException if the StopWatch is not currently running.
*/
public void suspend() {
if (this.runningState != State.RUNNING) {
throw new IllegalStateException("Stopwatch must be running to suspend. ");
}
this.stopTimeNanos = System.nanoTime();
this.stopTimeMillis = System.currentTimeMillis();
this.runningState = State.SUSPENDED;
}
/**
* <p>
* Gets a summary of the split time that the stopwatch recorded as a string.
* </p>
*
* <p>
* The format used is ISO 8601-like, [<i>message</i>
* ]<i>hours</i>:<i>minutes</i>:<i>seconds</i>.<i>milliseconds</i>.
* </p>
*
* @return the split time as a String
* @since 2.1
* @since 3.10 Returns the prefix {@code "message "} if the message is set.
*/
public String toSplitString() {
final String msgStr = Objects.toString(message, StringUtils.EMPTY);
final String formattedTime = formatSplitTime();
return msgStr.isEmpty() ? formattedTime : msgStr + StringUtils.SPACE + formattedTime;
}
/**
* <p>
* Gets a summary of the time that the stopwatch recorded as a string.
* </p>
*
* <p>
* The format used is ISO 8601-like, [<i>message</i>
* ]<i>hours</i>:<i>minutes</i>:<i>seconds</i>.<i>milliseconds</i>.
* </p>
*
* @return the time as a String
* @since 3.10 Returns the prefix {@code "message "} if the message is set.
*/
@Override
public String toString() {
final String msgStr = Objects.toString(message, StringUtils.EMPTY);
final String formattedTime = formatTime();
return msgStr.isEmpty() ? formattedTime : msgStr + StringUtils.SPACE + formattedTime;
}
/**
* <p>
* Removes a split.
* </p>
*
* <p>
* This method clears the stop time. The start time is unaffected, enabling
* timing from the original start point to continue.
* </p>
*
* @throws IllegalStateException if the StopWatch has not been split.
*/
public void unsplit() {
if (this.splitState != SplitState.SPLIT) {
throw new IllegalStateException("Stopwatch has not been split. ");
}
this.splitState = SplitState.UNSPLIT;
}
}

View File

@ -0,0 +1,36 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.time;
/**
* Helps to deal with {@link java.util.TimeZone}s.
*
* @since 3.7
*/
public class TimeZones {
// do not instantiate
private TimeZones() {
}
/**
* A public version of {@link java.util.TimeZone}'s package private
* {@code GMT_ID} field.
*/
public static final String GMT_ID = "GMT";
}

View File

@ -0,0 +1,40 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <p>
* Provides classes and methods to work with dates and durations. These classes
* are immutable (and therefore thread-safe) apart from
* {@link org.apache.commons.lang3.time.StopWatch}.
* </p>
*
* <p>
* The time package contains some basic utilities for manipulating time (a
* delorean, police box and grandfather clock?). These include a
* {@link org.apache.commons.lang3.time.StopWatch} for simple performance
* measurements and an optimised
* {@link org.apache.commons.lang3.time.FastDateFormat} class.
* </p>
*
* <p>
* New in Lang 2.1 is the
* {@link org.apache.commons.lang3.time.DurationFormatUtils} class, which
* provides various methods for formatting durations.
* </p>
*
* @since 2.0
*/
package org.apache.commons.lang3.time;