bb.util
Class DateUtil

java.lang.Object
  extended by bb.util.DateUtil

public final class DateUtil
extends Object

Provides static utility methods relating to Dates.

isExactXXX versus isSameXXX

Several methods in this class are named like isExactXXX (e.g. isExactDay), while other methods in this class are named like isSameXXX (e.g. isSameDayOfYear) These methods have slightly different semantics. The isExactXXX methods are named for events that can only happen at one block of time. For example, given a particular day (i.e. one that has a precise start time), there is no other day out of every possible day that could be the exact same day (since it would have a different start time). In contrast, the isSameXXX methods are named for events that can only happen at multiple blocks of time. For instance, a particular day of the year (e.g. the 3rd day) occurs every single year.

Calendar used

Every method in this class that uses a Calendar instance to do its work ultimately gets that Calendar by a call to Calendar.getInstance.

One consequence is that the JVM's default time zone and locale are used to define all date related defaults (e.g. what day of the week is the start of the week). These defaults can be changed in several ways. One way is programmatically, by calling Locale.setDefault and TimeZone.setDefault. Probably a better approach, altho it seems to be undocumented by Sun, is to specify these defaults via the system properties user.region and user.timezone.

Caching

Because most calls to the Calendar class are very slow, many methods in this class do not always directly use a Calendar instance. Instead, they automatically uses extensive caching to achieve high performance.

The only downside to caching is the memory used, so this class offers functionality to prevent memory exhaustion:

  1. for caching of calendar field codes, which uses instances of the DateUtil.DateInfo inner class, see setDateInfoCacheSizeMax
  2. for caching of String <--> Date mappings, see the dayOfYearCache, timeOfDayStampCache, timeOfDayStampForFileCache, timeStampCache, and timeStampForFileCache fields. Since each is an instance of DateUtil.DateStringCache, it will have these methods available: setSizeMax and clear. To conveniently suppress all these caches, call suppressDateStringCaches.

Date text pattern

Unless noted otherwise, every date's text pattern follows the ISO 8601 specification as closely as possible. This specification was chosen because its top down (most to least significant) information order is the only rational choice, unlike the common (but defective) American and European conventions. It has many advantages. For example, lexicographic order equals chronological order: String representations of Dates formatted by this spec, if sorted using String.compareTo, will be in ascending time order as well. This has all sorts of pleasant side effects, such as time stamps in file names cause the files to be sorted in time order. For more discussions, see link1 and link2.

Time zone changes and leap seconds

This class only makes these simplifying assumptions:

  1. a given day never has both a time zone change as well as a leap second: it can have at most one such event
  2. there is at most 1 leap second that can occcur on a given day. This differs from the ISO C date and time conventions used by Date, which allow up to 2 leap seconds to occur on a given day. A consequence is that the seconds field of a date processed by this class may reach 60 on a positive leap second day, but will never reach 61 as allowed by ISO C.
See getDayLength for more discussion.

Otherwise, this class generally makes no simplifying assumptions about time zone changes and leap seconds (e.g. that a day is always exactly 24 hours long) unless explicitly stated otherwise (e.g. get24HoursLater). Instead, the algorithms typically use a Calendar instance (or the cached result thereof) to perform their work, so they should always operate correctly.

Concerning leap seconds, the javadocs for Date have a brief discussion; below are more references:

Leap second and UT1-UTC information
In order to keep the cumulative difference in UT1-UTC less than 0.9 seconds, a leap second is added to the atomic time to decrease the difference between the two. This leap second can be either positive or negative depending on the Earth's rotation. Since the first leap second in 1972, all leap seconds have been positive and there were 22 leap seconds in the 27 years to January, 1999. This pattern reflects the general slowing trend of the Earth due to tidal braking.
http://tycho.usno.navy.mil/leapsec.html
UTC is kept always within one second of GMT by the insertion of extra seconds as necessary (positive leap seconds). It could happen that seconds would need to be removed (negative leap seconds), however all leap seconds so far have been positive.
http://www.npl.co.uk/time/leap_second.html
Leap seconds add up to roughly an extra day every 115,000 years.
http://mindprod.com/jgloss/leapyear.html
UTC might be redefined without Leap Seconds...
http://www.ucolick.org/~sla/leapsecs/
The Future of Leap Seconds...
http://en.wikipedia.org/wiki/Coordinated_Universal_Time#Future
http://www.ucolick.org/~sla/leapsecs/onlinebib.html
UTC has a complicated history, and leap seconds as presently known in UTC only were introduced into the standard in 1972
http://www.ucolick.org/~sla/leapsecs/timescales.html
(the section on "Coordinated Universal Time -- UTC" is near the end of this page)
Calendar reform:
http://slashdot.org/article.pl?sid=04/12/21/1519235&tid=99
Leap seconds may be eliminated:
http://science.slashdot.org/article.pl?sid=07/11/20/0356214
http://www.timeanddate.com/time/leap-seconds-future.html

Warning: it appears that leap seconds are not currently supported in Calendar on any platform, which if true means that this class too does not support them either. See UnitTest.test_calendarLeapSecondBehavior or this bug report.

Threads

This class is multithread safe. In order order to achieve high performance, the simple approach of using synchronized methods has been avoided and other more complicated techniques are used instead, for instance:

  1. each thread has its own Calendar and various DateFormat instances stored in ThreadLocals
  2. the caching methods do not synchronize unless the relevant cache needs to be modified, and the different caches each use their own synchronization lock

Author:
Brent Boyer

Nested Class Summary
private static class DateUtil.DateInfo
          Stores various calendar related qualities of a Date, such as its era, year, etc.
private static class DateUtil.DateInfoBin
          Stores all the DateUtil.DateInfo instances which have a common day number key (as calculated by getDateInfo).
static class DateUtil.DateStringCache
          Stores Date <--> String mappings (i.e. from the user's perspective, it is a bidirectional map).
private static class DateUtil.IsoDateFormat
          Formats/parses dates that are ISO 8601 compliant.
private static class DateUtil.TimeZoneChange
          Stores information about a time zone change.
static class DateUtil.UnitTest
          See the Overview page of the project's javadocs for a general description of this unit test class.
 
Field Summary
private static ThreadLocal<Calendar> calendarPerThread
           
private static long countDateInfoCacheMisses
          Stores the number of cache misses for the current DateInfo cache.
private static DateUtil.DateInfoBin[] dateInfoBins
          Forms the top level of the DateInfo cache, namely, an array of DateUtil.DateInfoBin instances.
private static int dateInfoCacheSize
          Stores the number of DateInfo instances that are currently cached.
private static int dateInfoCacheSizeMax
          Specifies the maximum number of DateInfo instances that will be cached.
static DateUtil.DateStringCache dayOfYearCache
          The cache used by getDayStamp and parseDayStamp.
private static String dayOfYearPattern
          A time pattern String for the SimpleDateFormat class which exactly specifies a day of the year.
private static Object lock_DateInfo
          Object used for synchronization by the DateInfo code.
private static String prefix_default
          Default value for the prefix param of getCacheIssues(prefix, separator).
private static String separator_default
          Default value for the separator param of getCacheIssues(prefix, separator).
private static String timeOfDayForFilePattern
          Serves the same purpose as timeOfDayPattern except that it should produce legal file names.
private static String timeOfDayPattern
          A time pattern String for the SimpleDateFormat class which exactly specifies the time of the day with as much precision (millseconds) as the JDK allows.
static DateUtil.DateStringCache timeOfDayStampCache
          The cache used by getDayStamp and parseDayStamp.
static DateUtil.DateStringCache timeOfDayStampForFileCache
          The cache used by getDayStamp and parseDayStamp.
static DateUtil.DateStringCache timeStampCache
          The cache used by getTimeStamp and parseTimeStamp.
static DateUtil.DateStringCache timeStampForFileCache
          The cache used by getTimeStampForFile and parseTimeStampForFile.
private static String timeStampForFilePattern
          Serves the same purpose as timeStampPattern except that it should produce legal file names.
private static String timeStampPattern
          A time pattern String for the SimpleDateFormat class which exactly specifies a moment in time with as much precision (millseconds) as the JDK allows.
 
Constructor Summary
private DateUtil()
          This private constructor suppresses the default (public) constructor, ensuring non-instantiability.
 
Method Summary
private static void appendIssues(String label, String issues, StringBuilder sb, String header)
           
static void clearDateStringCaches()
          Calls clear on all the DateUtil.DateStringCache fields of this class: dayOfYearCache, timeOfDayStampCache, timeOfDayStampForFileCache, timeStampCache, timeStampForFileCache.
static Date get24HoursLater(Date date)
          Returns a new Date instance that is exactly 24 hours after date.
static int getAmountTimeZoneChange(Date date)
          Returns the number of hours that the clock was changed by on the day that date lies in due to a time zone change.
static String getCacheIssues()
          Returns getCacheIssues(prefix_default).
static String getCacheIssues(String prefix)
          Returns getCacheIssues(prefix, separator_default).
static String getCacheIssues(String prefix, String separator)
          Returns a String description of any cache issues (e.g. warnings of cache misses, which indicate that caches might need to be increased).
private static Calendar getCalendar()
           
static int getCenturyOfMillenia(Date date)
          Returns the century of the millenia of date.
private static DateUtil.DateInfo getDateInfo(Date date)
          Returns the DateInfo instance in the cache corresponding to the day of date.
private static String getDateInfoCacheIssues()
          Contract: the result always ends with a newline.
static Date getDayEnd(Date date)
          Returns that Date which represents the end of the day that date lies in.
static long getDayLength(Date date)
          Returns the length of date's day in milliseconds.
static int getDayOfMonth(Date date)
          Returns the day of the month of date.
static int getDayOfWeek(Date date)
          Returns the day of the week of date.
static String getDayOfWeekName(int dayOfWeek)
          Returns the day of the week name as a String which corresponds to dayOfWeek.
static int getDayOfYear(Date date)
          Returns the day of the year of date.
static String getDayStamp()
           
static String getDayStamp(Date date)
          ...
static Date getDayStart(Date date)
          Returns that Date which represents the start of the day that date lies in.
static int getDecadeOfCentury(Date date)
          Returns the decade of the century of date.
static int getEra(Date date)
          Returns the era of date.
static int getHourOfDay(Date date)
          Returns the hour of the day (result is inside [0, 23]).
static Date getHourStart(Date date)
          Returns that Date which represents the start of the hour that date lies in.
static int getLeapSecond(Date date)
          Returns the leap second for date's day.
static int getMilliSecondOfSecond(Date date)
          Returns the millisecond of the second.
static int getMinuteOfHour(Date date)
          Returns the minute of the hour.
static int getMonth(Date date)
          Returns the month of the year of date.
static Date getMonthEnd(Date date)
          Returns that Date which represents the end of the month that date lies in.
static Date getMonthStart(Date date)
          Returns that Date which represents the start of the month that date lies in.
static Date getSameTimeNextDay(Date date)
          Returns that Date which represents the same time of the day referred to by date but on the next day.
static Date getSameTimeNextMonth(Date date)
          Returns that Date which represents the same time of the day referred to by date but on the next month of the year.
static Date getSameTimeNextWeek(Date date)
          Returns that Date which represents the same time of the day referred to by date but on the next week of the month.
static Date getSameTimeNextYear(Date date)
          Returns that Date which represents the same time of the day referred to by date but on the next year.
static Date getSameTimeOtherDay(Date date, int dayDifference)
          Returns that Date which represents the same time of the day referred to by date (down to the millisecond), but its day minus date's day equals dayDifference (e.g. +1 returns next day, -1 returns previous day).
static Date getSameTimeOtherMonth(Date date, int monthDifference)
          Returns that Date which represents the same time of the day referred to by date (down to the millisecond), but its month of the year minus date's month of the year equals monthDifference (e.g. +1 returns next month, -1 returns previous month).
static Date getSameTimeOtherWeek(Date date, int weekDifference)
          Returns that Date which represents the same time of the day referred to by date (down to the millisecond), but its week of the month minus date's week of the month equals weekDifference (e.g. +1 returns next week, -1 returns previous week).
static Date getSameTimeOtherYear(Date date, int yearDifference)
          Returns that Date which represents the same time of the day referred to by date (down to the millisecond), but its year minus date's year equals yearDifference (e.g. +1 returns next year, -1 returns previous year).
static Date getSameTimePreviousDay(Date date)
          Returns that Date which represents the same time of the day referred to by date but on the previous day.
static Date getSameTimePreviousMonth(Date date)
          Returns that Date which represents the same time of the day referred to by date but on the previous month of the year.
static Date getSameTimePreviousWeek(Date date)
          Returns that Date which represents the same time of the day referred to by date but on the previous week of the month.
static Date getSameTimePreviousYear(Date date)
          Returns that Date which represents the same time of the day referred to by date but on the previous year.
static int getSecondOfMinute(Date date)
          Returns the second of the minute.
static long getTimeOfDay(Date date)
          Returns the time of day for date.
static String getTimeOfDayStamp()
          Returns getTimeOfDayStamp( new Date() ).
static String getTimeOfDayStamp(Date date)
          Returns the time of day for date.
static String getTimeOfDayStamp(long timeOfDay)
          Is almost the same as getTimeOfDayStamp(Date) except that it takes a long arg.
static String getTimeOfDayStampConcise(long timeOfDay)
          Returns a (possibly) more compact result than getTimeOfDayStamp by dropping the milliseconds and seconds fields if they can be implicitly understood.
static String getTimeOfDayStampForFile()
           
static String getTimeOfDayStampForFile(Date date)
           
static String getTimeStamp()
           
static String getTimeStamp(Date date)
          ...
static String getTimeStampConcise(Date date)
          Returns a more compact result than getTimeStamp by: always dropping the time zone dropping any low significance fields which can be implicitly understood The result, however, always contains at least the year.
static String getTimeStampForFile()
           
static String getTimeStampForFile(Date date)
          ...
static int getWeekOfMonth(Date date)
          Returns the week of month.
static int getWeekOfYear(Date date)
          Returns the week of year.
static Date getWeekStart(Date date)
          Returns that Date which represents the start of the week that date lies in.
static int getYear(Date date)
          Returns the year of date.
static Date getYearEnd(Date date)
          Returns that Date which represents the end of the year that date lies in.
static Date getYearStart(Date date)
          Returns that Date which represents the start of the year that date lies in.
static boolean isDayOfMonthLast(Date date)
          Determines whether date falls on the last day of the month.
static boolean isDayOfYearLast(Date date)
          Determines whether or not date falls on the last day of the year.
static boolean isExactDay(Date date, Date dateReference)
          Determines whether or not date occurs on the exact day as dateReference.
static boolean isExactWeek(Date date, Date dateReference)
          Determines whether or not date occurs in the exact week as dateReference occurs in.
static boolean isLeapDay(Date date)
          Determines whether or not date is a leap day in the Gregorian Calendar (i.e. is February 29th).
static boolean isSameCenturyOfMillenia(Date date, Date dateReference)
          Determines whether or not date occurs in the same century of the millenia as dateReference.
static boolean isSameDayOfMonth(Date date, Date dateReference)
          Determines whether or not date occurs in the same day of month as dateReference.
static boolean isSameDayOfWeek(Date date, Date dateReference)
          Determines whether or not date occurs in the same day of week as dateReference.
static boolean isSameDayOfYear(Date date, Date dateReference)
          Determines whether or not date occurs in the same day of year as dateReference.
static boolean isSameDecadeOfCentury(Date date, Date dateReference)
          Determines whether or not date occurs in the same decade of the century as dateReference.
static boolean isSameHourOfDay(Date date, Date dateReference)
          Determines whether or not date occurs in the same hour of the day as dateReference.
static boolean isSameMinuteOfHour(Date date, Date dateReference)
          Determines whether or not date occurs in the same minute of the hour as dateReference.
static boolean isSameMonth(Date date, Date dateReference)
          Determines whether or not date occurs in the same month of the year as dateReference.
static boolean isSameSecondOfMinute(Date date, Date dateReference)
          Determines whether or not date occurs in the same second of the minute as dateReference.
static boolean isSameWeekOfMonth(Date date, Date dateReference)
          Determines whether or not date occurs in the same week of month as dateReference.
static boolean isSameWeekOfYear(Date date, Date dateReference)
          Determines whether or not date occurs in the same week of year as dateReference.
static boolean isSameYear(Date date, Date dateReference)
          Determines whether or not date occurs in the same year as dateReference.
static boolean isWeekDay(Date date)
          Determines whether or not date's day of week is a "week day" (i.e.
static boolean isWeekEnd(Date date)
          Determines whether or not date's day of week is a "week end" (i.e.
static boolean isWithinDays(Date date, Date dateReference, int limit)
          Determines whether or not date occurs within the the specififed limit of days from dateReference.
static Date parseDayStamp(String string)
           
static Date parseTimeStamp(String string)
           
static Date parseTimeStampForFile(String string)
           
static void setDateInfoCacheSizeMax(int sizeMax)
          Sets the dateInfoCacheSizeMax field to sizeMax.
static void suppressDateStringCaches()
          Calls setSizeMax(0) on all the DateUtil.DateStringCache fields of this class: dayOfYearCache, timeOfDayStampCache, timeOfDayStampForFileCache, timeStampCache, timeStampForFileCache.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

dayOfYearPattern

private static final String dayOfYearPattern
A time pattern String for the SimpleDateFormat class which exactly specifies a day of the year.

For reasons stated in the class javadocs, this format follows the human-readable version of the ISO 8601 specification for day of the year dates (±YYYY-MM-DD). But it must be used with a DateUtil.IsoDateFormat instance to get the era tokens right.

No explicit time zone is given. Other users of this field (e.g. timeStampPattern) explicitly state the time zone elsewhere in their format. If left unspecified, then the local time zone has to be assumed.

This format may be used in a file name; see timeOfDayForFilePattern.

See Also:
Constant Field Values

timeOfDayPattern

private static final String timeOfDayPattern
A time pattern String for the SimpleDateFormat class which exactly specifies the time of the day with as much precision (millseconds) as the JDK allows.

For reasons stated in the class javadocs, this format follows the human-readable version of the ISO 8601 specification for the time of the day (HH:MM:SS.SSS). Here, HH is a 24-hour clock (i.e. takes on the values 00 thru 23, which eliminates the need to specify am/pm) and SSS is the milliseconds.

No explicit time zone is given. Other users of this field (e.g. timeStampPattern) explicitly state the time zone elsewhere in their format. If left unspecified, then the local time zone has to be assumed.

Because this format uses the colon (':') char as a field separator, it is not suitable for use in a file name time stamp; see timeOfDayForFilePattern.

See Also:
Constant Field Values

timeOfDayForFilePattern

private static final String timeOfDayForFilePattern
Serves the same purpose as timeOfDayPattern except that it should produce legal file names.

To achieve this, the ISO 8601 specified colon (':') field separator char is replaced by a dash ('-') char. This means that this format is not strictly ISO 8601 compliant.

Concerning what consitutes a legal file name across many operating systems, note that the ISO 9660 Level 1 format is probably the most universal file name format, but it is hopelessly restrictive. ISO 9660 Level 2 merely adds extra file name chars (30 instead of 8), so it too is insufficient. So, the choice was made to restrict this constant to a format that should work on the major operating systems; see

See Also:
Constant Field Values

timeStampPattern

private static final String timeStampPattern
A time pattern String for the SimpleDateFormat class which exactly specifies a moment in time with as much precision (millseconds) as the JDK allows.

For reasons stated in the class javadocs, this format follows the human-readable version of the ISO 8601 specification for dates and times (±YYYY-MM-DDTHH:MM:SS.SSSZ) In other words, this is a concatenation of @link #dayOfYearPattern}, the letter 'T', timeOfDayPattern, and the explicit RFC 822 time zone used; see further notes in those constants. But it must be used with a DateUtil.IsoDateFormat instance to get the era tokens right.

Because this format uses the colon (':') char as a field separator via timeOfDayPattern, it is not suitable for use in a file name time stamp; see timeStampForFilePattern.

See Also:
Constant Field Values

timeStampForFilePattern

private static final String timeStampForFilePattern
Serves the same purpose as timeStampPattern except that it should produce legal file names.

This is a concatenation of @link #dayOfYearPattern}, the letter 'T', timeOfDayForFilePattern, and the explicit RFC 822 time zone used; see further notes in those constants. This means that this format is not strictly ISO 8601 compliant.

See Also:
Constant Field Values

calendarPerThread

private static final ThreadLocal<Calendar> calendarPerThread

dayOfYearCache

public static final DateUtil.DateStringCache dayOfYearCache
The cache used by getDayStamp and parseDayStamp.


timeOfDayStampCache

public static final DateUtil.DateStringCache timeOfDayStampCache
The cache used by getDayStamp and parseDayStamp.


timeOfDayStampForFileCache

public static final DateUtil.DateStringCache timeOfDayStampForFileCache
The cache used by getDayStamp and parseDayStamp.


timeStampCache

public static final DateUtil.DateStringCache timeStampCache
The cache used by getTimeStamp and parseTimeStamp.


timeStampForFileCache

public static final DateUtil.DateStringCache timeStampForFileCache
The cache used by getTimeStampForFile and parseTimeStampForFile.


prefix_default

private static final String prefix_default
Default value for the prefix param of getCacheIssues(prefix, separator).


separator_default

private static final String separator_default
Default value for the separator param of getCacheIssues(prefix, separator).

See Also:
Constant Field Values

lock_DateInfo

private static final Object lock_DateInfo
Object used for synchronization by the DateInfo code.


dateInfoCacheSize

private static int dateInfoCacheSize
Stores the number of DateInfo instances that are currently cached.

This value gets reset to 0 whenever setDateInfoCacheSizeMax is called.

Contract: this field must only be used inside a synchronized (lock_DateInfo) { ... } block. Because of this, it need not be declared as volatile.


dateInfoCacheSizeMax

private static int dateInfoCacheSizeMax
Specifies the maximum number of DateInfo instances that will be cached.

Its default value is 32 * 1024 = 32,768. This was chosen because it is a multiple of 1024 which is close to 100 years * 365 days/year = 36,500 which is an estimate for how many days might be encountered in a typical application.

Contract: this field must only be used inside a synchronized (lock_DateInfo) { ... } block. Because of this, it need not be declared as volatile.


countDateInfoCacheMisses

private static long countDateInfoCacheMisses
Stores the number of cache misses for the current DateInfo cache. The only cache misses which contribute to this field are those due to the cache size limit being reached; cache misses due to other causes (see DateInfo.onCacheMiss) are ignored.

This value gets reset to 0 whenever setDateInfoCacheSizeMax is called.

Contract: this field must only be used inside a synchronized (lock_DateInfo) { ... } block. Because of this, it need not be declared as volatile.


dateInfoBins

private static volatile DateUtil.DateInfoBin[] dateInfoBins
Forms the top level of the DateInfo cache, namely, an array of DateUtil.DateInfoBin instances. This array stores the DateInfoBins in ascending day order, modulo the array length. Once a given element of this array has been assigned to a DateInfoBin instance, it is never changed.

The currennt implementation initializes this field to a DateInfoBin[] of length dateInfoCacheSizeMax so that hopefully each DateInfo will be stored in exactly 1 DateInfoBin, which leads to optimal searching (just like a hash table has best performance when each element is in one bucket).

Contract: this field is always a non-null reference to a fully valid array (i.e. it never has null elements), but it may be zero-length. It must be declared as volatile because it is designed to be accessed outside of synchronized blocks for high performance.

Constructor Detail

DateUtil

private DateUtil()
This private constructor suppresses the default (public) constructor, ensuring non-instantiability.

Method Detail

getCalendar

private static Calendar getCalendar()

clearDateStringCaches

public static void clearDateStringCaches()
Calls clear on all the DateUtil.DateStringCache fields of this class: dayOfYearCache, timeOfDayStampCache, timeOfDayStampForFileCache, timeStampCache, timeStampForFileCache.


suppressDateStringCaches

public static void suppressDateStringCaches()
Calls setSizeMax(0) on all the DateUtil.DateStringCache fields of this class: dayOfYearCache, timeOfDayStampCache, timeOfDayStampForFileCache, timeStampCache, timeStampForFileCache.


getCacheIssues

public static String getCacheIssues()
Returns getCacheIssues(prefix_default).


getCacheIssues

public static String getCacheIssues(String prefix)
Returns getCacheIssues(prefix, separator_default).


getCacheIssues

public static String getCacheIssues(String prefix,
                                    String separator)
Returns a String description of any cache issues (e.g. warnings of cache misses, which indicate that caches might need to be increased).

Contract: the result is never null, but is zero-length if and only if there are no issues. If there are issues, they are listed one per line. See the prefix and separator param descriptions below for where they appear.

Parameters:
prefix - used in formatting the result for better readability; this value, if non-null and if there are issues, appears as the first piece of text in the result; the value should end in a newline char; common choices would be one or more simple newline chars ('\n') or maybe a text preamble of some sort
separator - used in formatting the result for better readability; this value, if non-null and if there are issues, appears in exactly two places in the result: immediately before the issues as well as immediately after the issues; the value should end in a newline char; a common choice is a line of distinctive chars like asterisks ('*')

appendIssues

private static void appendIssues(String label,
                                 String issues,
                                 StringBuilder sb,
                                 String header)

isSameCenturyOfMillenia

public static boolean isSameCenturyOfMillenia(Date date,
                                              Date dateReference)
                                       throws IllegalArgumentException
Determines whether or not date occurs in the same century of the millenia as dateReference.

Throws:
IllegalArgumentException - if date == null; dateReference == null

isSameDecadeOfCentury

public static boolean isSameDecadeOfCentury(Date date,
                                            Date dateReference)
                                     throws IllegalArgumentException
Determines whether or not date occurs in the same decade of the century as dateReference.

Throws:
IllegalArgumentException - if date == null; dateReference == null

isSameYear

public static boolean isSameYear(Date date,
                                 Date dateReference)
                          throws IllegalArgumentException
Determines whether or not date occurs in the same year as dateReference.

Throws:
IllegalArgumentException - if date == null; dateReference == null

isDayOfYearLast

public static boolean isDayOfYearLast(Date date)
                               throws IllegalArgumentException
Determines whether or not date falls on the last day of the year.

Throws:
IllegalArgumentException - if date == null

isSameDayOfYear

public static boolean isSameDayOfYear(Date date,
                                      Date dateReference)
                               throws IllegalArgumentException
Determines whether or not date occurs in the same day of year as dateReference.

Throws:
IllegalArgumentException - if date == null; dateReference == null

isSameMonth

public static boolean isSameMonth(Date date,
                                  Date dateReference)
                           throws IllegalArgumentException
Determines whether or not date occurs in the same month of the year as dateReference.

Throws:
IllegalArgumentException - if date == null; dateReference == null

isDayOfMonthLast

public static boolean isDayOfMonthLast(Date date)
                                throws IllegalArgumentException
Determines whether date falls on the last day of the month.

Throws:
IllegalArgumentException - if date == null

isSameDayOfMonth

public static boolean isSameDayOfMonth(Date date,
                                       Date dateReference)
                                throws IllegalArgumentException
Determines whether or not date occurs in the same day of month as dateReference.

Throws:
IllegalArgumentException - if date == null; dateReference == null

isExactWeek

public static boolean isExactWeek(Date date,
                                  Date dateReference)
                           throws IllegalArgumentException
Determines whether or not date occurs in the exact week as dateReference occurs in. Here, the exact week is defined as both Dates sharing the same week start. Note that weeks can straddle year boundaries, so this method can return true for days in different years. For example, 1998-01-01 was a Thursday. If Calendar.getFirstDayOfWeek is SUNDAY, then this method returns true when supplied with Dates occuring inside [1997-12-28, 1998-01-03].

Throws:
IllegalArgumentException - if date == null; dateReference == null

isSameWeekOfYear

public static boolean isSameWeekOfYear(Date date,
                                       Date dateReference)
                                throws IllegalArgumentException
Determines whether or not date occurs in the same week of year as dateReference.

Throws:
IllegalArgumentException - if date == null; dateReference == null

isSameWeekOfMonth

public static boolean isSameWeekOfMonth(Date date,
                                        Date dateReference)
                                 throws IllegalArgumentException
Determines whether or not date occurs in the same week of month as dateReference.

Throws:
IllegalArgumentException - if date == null; dateReference == null

isExactDay

public static boolean isExactDay(Date date,
                                 Date dateReference)
                          throws IllegalArgumentException
Determines whether or not date occurs on the exact day as dateReference. Here, the exact day is defined as both Dates sharing a) the same year b) month c) day of month. This criteria is stricter than that of the isSameDayXXX methods.

Throws:
IllegalArgumentException - if date == null; dateReference == null

isSameDayOfWeek

public static boolean isSameDayOfWeek(Date date,
                                      Date dateReference)
                               throws IllegalArgumentException
Determines whether or not date occurs in the same day of week as dateReference.

Throws:
IllegalArgumentException - if date == null; dateReference == null

isWeekDay

public static boolean isWeekDay(Date date)
                         throws IllegalArgumentException
Determines whether or not date's day of week is a "week day" (i.e. Monday thru Friday).

Throws:
IllegalArgumentException - if date == null

isWeekEnd

public static boolean isWeekEnd(Date date)
                         throws IllegalArgumentException
Determines whether or not date's day of week is a "week end" (i.e. Saturday or Sunday).

Throws:
IllegalArgumentException - if date == null

isWithinDays

public static boolean isWithinDays(Date date,
                                   Date dateReference,
                                   int limit)
                            throws IllegalArgumentException
Determines whether or not date occurs within the the specififed limit of days from dateReference.

This method first first determines the times of the start of day for both date and dateReference. Then it determines the min and max of these two times (call them tMin and tMax). Then it takes tMin and computes the time for a Date which occurs exactly limit days later (call it tLimit). This method returns true if tMax occurs on or before tLimit. Note that this method does not care which of date or dateReference occurs first, nor what time of day each represents.

Throws:
IllegalArgumentException - if date == null; dateReference == null; limit < 0

isLeapDay

public static boolean isLeapDay(Date date)
                         throws IllegalArgumentException,
                                IllegalStateException
Determines whether or not date is a leap day in the Gregorian Calendar (i.e. is February 29th).

Throws:
IllegalArgumentException - if date == null
IllegalStateException - if getCalendar does not return a GregorianCalendar instance

isSameHourOfDay

public static boolean isSameHourOfDay(Date date,
                                      Date dateReference)
                               throws IllegalArgumentException
Determines whether or not date occurs in the same hour of the day as dateReference.

Throws:
IllegalArgumentException - if date == null; dateReference == null

isSameMinuteOfHour

public static boolean isSameMinuteOfHour(Date date,
                                         Date dateReference)
                                  throws IllegalArgumentException
Determines whether or not date occurs in the same minute of the hour as dateReference.

Throws:
IllegalArgumentException - if date == null; dateReference == null

isSameSecondOfMinute

public static boolean isSameSecondOfMinute(Date date,
                                           Date dateReference)
                                    throws IllegalArgumentException
Determines whether or not date occurs in the same second of the minute as dateReference.

Throws:
IllegalArgumentException - if date == null; dateReference == null

getEra

public static int getEra(Date date)
                  throws IllegalArgumentException
Returns the era of date.

The value that is returned is defined by the concrete subclass of Calendar that is being used for the current Locale used by the JVM. In most parts of the world, by default, this will be an instance of GregorianCalendar, so the result will be either GregorianCalendar.AD or GregorianCalendar.BC.

Throws:
IllegalArgumentException - if date == null

getCenturyOfMillenia

public static int getCenturyOfMillenia(Date date)
                                throws IllegalArgumentException
Returns the century of the millenia of date.

The value that is returned is in the range [0, 9] (e.g. 2004 is the 0th century of the millenia, 2104 is the 1st century, etc).

Throws:
IllegalArgumentException - if date == null

getDecadeOfCentury

public static int getDecadeOfCentury(Date date)
                              throws IllegalArgumentException
Returns the decade of the century of date.

The value that is returned is in the range [0, 9] (e.g. 2004 is the 0th decade of the century, 2014 is the 1st decade, etc).

Throws:
IllegalArgumentException - if date == null

getYear

public static int getYear(Date date)
                   throws IllegalArgumentException
Returns the year of date.

Throws:
IllegalArgumentException - if date == null

getYearStart

public static Date getYearStart(Date date)
                         throws IllegalArgumentException
Returns that Date which represents the start of the year that date lies in. Because Calendar defines midnight to be the first time of day, the result will be midnight of the year's first day.

Throws:
IllegalArgumentException - if date == null

getYearEnd

public static Date getYearEnd(Date date)
                       throws IllegalArgumentException
Returns that Date which represents the end of the year that date lies in. This will be 1 millisecond before midnight of the next year's first day.

Throws:
IllegalArgumentException - if date == null

getSameTimeNextYear

public static Date getSameTimeNextYear(Date date)
                                throws IllegalArgumentException
Returns that Date which represents the same time of the day referred to by date but on the next year. The implementation here simply returns getSameTimeOtherYear(date, 1).

Throws:
IllegalArgumentException - if date == null

getSameTimePreviousYear

public static Date getSameTimePreviousYear(Date date)
                                    throws IllegalArgumentException
Returns that Date which represents the same time of the day referred to by date but on the previous year. The implementation here simply returns getSameTimeOtherYear(date, -1).

Throws:
IllegalArgumentException - if date == null

getSameTimeOtherYear

public static Date getSameTimeOtherYear(Date date,
                                        int yearDifference)
                                 throws IllegalArgumentException
Returns that Date which represents the same time of the day referred to by date (down to the millisecond), but its year minus date's year equals yearDifference (e.g. +1 returns next year, -1 returns previous year).

Note: leap second effects should be properly handled, so the difference between the result and date will not necessarily be a multiple of 24 hours.

Throws:
IllegalArgumentException - if date == null

getMonth

public static int getMonth(Date date)
                    throws IllegalArgumentException
Returns the month of the year of date.

Warning: for compatibility with Calendar, the result uses the same 0 offset month basis. For Locales which use a Gregorian Calendar, the result is in the range [0, 11].

Throws:
IllegalArgumentException - if date == null

getMonthStart

public static Date getMonthStart(Date date)
                          throws IllegalArgumentException
Returns that Date which represents the start of the month that date lies in. Because Calendar defines midnight to be the first time of day, the result will be midnight of the month's first day.

Throws:
IllegalArgumentException - if date == null

getMonthEnd

public static Date getMonthEnd(Date date)
                        throws IllegalArgumentException
Returns that Date which represents the end of the month that date lies in. Because Calendar defines midnight to be the first time of day, the result will be 1 millisecond before midnight of the next month's first day.

Throws:
IllegalArgumentException - if date == null

getSameTimeNextMonth

public static Date getSameTimeNextMonth(Date date)
                                 throws IllegalArgumentException
Returns that Date which represents the same time of the day referred to by date but on the next month of the year. The implementation here simply returns getSameTimeOtherMonth(date, 1).

Throws:
IllegalArgumentException - if date == null

getSameTimePreviousMonth

public static Date getSameTimePreviousMonth(Date date)
                                     throws IllegalArgumentException
Returns that Date which represents the same time of the day referred to by date but on the previous month of the year. The implementation here simply returns getSameTimeOtherMonth(date, -1).

Throws:
IllegalArgumentException - if date == null

getSameTimeOtherMonth

public static Date getSameTimeOtherMonth(Date date,
                                         int monthDifference)
                                  throws IllegalArgumentException
Returns that Date which represents the same time of the day referred to by date (down to the millisecond), but its month of the year minus date's month of the year equals monthDifference (e.g. +1 returns next month, -1 returns previous month).

Note: leap seconds are properly accounted for, so the difference between the result and date will not necessarily be a multiple of 24 hours.

Throws:
IllegalArgumentException - if date == null

getWeekOfYear

public static int getWeekOfYear(Date date)
                         throws IllegalArgumentException
Returns the week of year.

Throws:
IllegalArgumentException - if date == null

getWeekOfMonth

public static int getWeekOfMonth(Date date)
                          throws IllegalArgumentException
Returns the week of month.

Throws:
IllegalArgumentException - if date == null

getWeekStart

public static Date getWeekStart(Date date)
                         throws IllegalArgumentException
Returns that Date which represents the start of the week that date lies in. Because Calendar defines midnight to be the first time of day, the result will be midnight of the week's first day.

Throws:
IllegalArgumentException - if date == null

getSameTimeNextWeek

public static Date getSameTimeNextWeek(Date date)
                                throws IllegalArgumentException
Returns that Date which represents the same time of the day referred to by date but on the next week of the month. The implementation here simply returns getSameTimeOtherWeek(date, 1).

Throws:
IllegalArgumentException - if date == null

getSameTimePreviousWeek

public static Date getSameTimePreviousWeek(Date date)
                                    throws IllegalArgumentException
Returns that Date which represents the same time of the day referred to by date but on the previous week of the month. The implementation here simply returns getSameTimeOtherWeek(date, -1).

Throws:
IllegalArgumentException - if date == null

getSameTimeOtherWeek

public static Date getSameTimeOtherWeek(Date date,
                                        int weekDifference)
                                 throws IllegalArgumentException
Returns that Date which represents the same time of the day referred to by date (down to the millisecond), but its week of the month minus date's week of the month equals weekDifference (e.g. +1 returns next week, -1 returns previous week).

Note: leap seconds are properly accounted for, so the difference between the result and date will not necessarily be a multiple of 7 * 24 hours.

Throws:
IllegalArgumentException - if date == null

getDayOfYear

public static int getDayOfYear(Date date)
                        throws IllegalArgumentException
Returns the day of the year of date.

Throws:
IllegalArgumentException - if date == null

getDayOfMonth

public static int getDayOfMonth(Date date)
                         throws IllegalArgumentException
Returns the day of the month of date.

Throws:
IllegalArgumentException - if date == null

getDayOfWeek

public static int getDayOfWeek(Date date)
                        throws IllegalArgumentException
Returns the day of the week of date.

Throws:
IllegalArgumentException - if date == null

getDayStart

public static Date getDayStart(Date date)
                        throws IllegalArgumentException
Returns that Date which represents the start of the day that date lies in. Because Calendar defines midnight to be the first time of day, the result will be midnight of date's day.

Throws:
IllegalArgumentException - if date == null

getDayEnd

public static Date getDayEnd(Date date)
                      throws IllegalArgumentException
Returns that Date which represents the end of the day that date lies in. Because Calendar defines midnight to be the first time of day, the result will be 1 millisecond before midnight of the day after date.

Throws:
IllegalArgumentException - if date == null

getTimeOfDay

public static long getTimeOfDay(Date date)
                         throws IllegalArgumentException
Returns the time of day for date. In particular, the result is the "wall clock" reading for date expressed as a single long value.

Here, "wall clock" means the time of day fields (i.e. hours, minutes, seconds, and milliseconds) as numbers. Label these fields as HH, mm, ss, and SSS respectively. One way to convert these fields into a single long is to calculate (HH * TimeLength.hour) + (mm * TimeLength.minute) + (ss * TimeLength.second) + SSS. The result of this method is always equal to this calculation (altho this method is not necessarily implemented that way).

Note that the "wall clock" used is adjusted for any time zone change that happened on date for the JVM's default Locale and TimeZone. The "wall clock" (unlike normal physical clocks) is also assumed to be able to represent positive leap seconds (i.e. the second field can be 60).

Warning: a consequence of adjusting for time zone changes is that it is problematic to compute absolute time differences using the results of this method. For instance, even if date1 and date2 are known to fall on the same day, the calculation long timeDiff = getTimeOfDay(date1) - getTimeOfDay(date2) is wrong if a time zone change occurs between date1 and date2.

This method's long result is equivalent to the String returned by getTimeOfDayStamp in that either form can be perfectly converted into the other.

Throws:
IllegalArgumentException - if date == null

getDayLength

public static long getDayLength(Date date)
                         throws IllegalArgumentException,
                                IllegalStateException
Returns the length of date's day in milliseconds.

Contract: the result is always one of these values: TimeLength.day, TimeLength.dayTzChPos, TimeLength.dayTzChNeg, TimeLength.dayLeapPos, TimeLength.dayLeapNeg. Note: this restriction means that this method assumes that at most one special event (time zone change day or leap second) can occur on a given day, and that there is at most 1 leap second that can occcur on a given day (not 2, as allowed by ISO C).

Throws:
IllegalArgumentException - if date == null
IllegalStateException - if an unexpected day length is encountered

getSameTimeNextDay

public static Date getSameTimeNextDay(Date date)
                               throws IllegalArgumentException
Returns that Date which represents the same time of the day referred to by date but on the next day. The implementation here simply returns getSameTimeOtherDay(date, 1).

Throws:
IllegalArgumentException - if date == null

getSameTimePreviousDay

public static Date getSameTimePreviousDay(Date date)
                                   throws IllegalArgumentException
Returns that Date which represents the same time of the day referred to by date but on the previous day. The implementation here simply returns getSameTimeOtherDay(date, -1).

Throws:
IllegalArgumentException - if date == null

getSameTimeOtherDay

public static Date getSameTimeOtherDay(Date date,
                                       int dayDifference)
                                throws IllegalArgumentException
Returns that Date which represents the same time of the day referred to by date (down to the millisecond), but its day minus date's day equals dayDifference (e.g. +1 returns next day, -1 returns previous day).

Note: leap seconds are properly accounted for, so the difference between the result and date will not necessarily be a multiple of 24 hours. See get24HoursLater for a method to advance the time strictly by 24 hours.

Throws:
IllegalArgumentException - if date == null

get24HoursLater

public static Date get24HoursLater(Date date)
                            throws IllegalArgumentException
Returns a new Date instance that is exactly 24 hours after date.

Warning: leap second effects are not accounted for. See getSameTimeNextDay for a method which handles leap seconds.

Throws:
IllegalArgumentException - if date == null

getAmountTimeZoneChange

public static int getAmountTimeZoneChange(Date date)
                                   throws IllegalArgumentException
Returns the number of hours that the clock was changed by on the day that date lies in due to a time zone change. The result can be +1 (a spring forward day), 0 (a normal non-time zone change day), or -1 (a fall back day).

Throws:
IllegalArgumentException - if date == null

getLeapSecond

public static int getLeapSecond(Date date)
                         throws IllegalArgumentException,
                                IllegalStateException
Returns the leap second for date's day. The result can be +1 (a positive leap second day), 0 (a normal non-leap second day), or -1 (a negative leap second day).

Throws:
IllegalArgumentException - if date == null
IllegalStateException - if an unexpected state is encountered

getHourOfDay

public static int getHourOfDay(Date date)
                        throws IllegalArgumentException
Returns the hour of the day (result is inside [0, 23]).

Throws:
IllegalArgumentException - if date == null

getHourStart

public static Date getHourStart(Date date)
                         throws IllegalArgumentException
Returns that Date which represents the start of the hour that date lies in.

Throws:
IllegalArgumentException - if date == null

getMinuteOfHour

public static int getMinuteOfHour(Date date)
                           throws IllegalArgumentException
Returns the minute of the hour.

Throws:
IllegalArgumentException - if date == null

getSecondOfMinute

public static int getSecondOfMinute(Date date)
                             throws IllegalArgumentException
Returns the second of the minute.

Throws:
IllegalArgumentException - if date == null

getMilliSecondOfSecond

public static int getMilliSecondOfSecond(Date date)
                                  throws IllegalArgumentException
Returns the millisecond of the second.

Throws:
IllegalArgumentException - if date == null

getDayStamp

public static String getDayStamp()

getDayStamp

public static String getDayStamp(Date date)
                          throws IllegalArgumentException
...

Throws:
IllegalArgumentException - if date == null

getTimeOfDayStamp

public static String getTimeOfDayStamp()
Returns getTimeOfDayStamp( new Date() ).


getTimeOfDayStamp

public static String getTimeOfDayStamp(Date date)
                                throws IllegalArgumentException
Returns the time of day for date. In particular, the result is the "wall clock" reading for date expressed as a String.

Here, "wall clock" means the time of day fields (i.e. hours, minutes, seconds, and milliseconds) concatenated into a single String as per timeOfDayPattern.

This method's String result is equivalent to the long returned by getTimeOfDay in that either form can be perfectly converted into the other.

Throws:
IllegalArgumentException - if date == null

getTimeOfDayStamp

public static String getTimeOfDayStamp(long timeOfDay)
                                throws IllegalArgumentException
Is almost the same as getTimeOfDayStamp(Date) except that it takes a long arg. Because its arg is not an absolute moment in time, it is impossible for this method to account for time zone changes, therefore, it assumes that no time zone change occurred. This method does, however, handle leap seconds correctly.

Parameters:
timeOfDay - a long value that represents the time of day exactly like the result of getTimeOfDay
Throws:
IllegalArgumentException - if timeOfDay < 0; timeOfDay > TimeLength.dayMax

getTimeOfDayStampConcise

public static String getTimeOfDayStampConcise(long timeOfDay)
                                       throws IllegalArgumentException
Returns a (possibly) more compact result than getTimeOfDayStamp by dropping the milliseconds and seconds fields if they can be implicitly understood. The result, however, always contains at least the hour and minutes.

This method first calls getTimeOfDayStamp and uses that String for all subsequent processing. If the millisecond field of the result is all zeroes (".000")), it is removed. If and only if milliseconds were dropped, then checks the second portion, removing it too if all zeroes (":00").

Here are examples of what this method returns, in the form of long --> String:

This method is typically used to format times of day for human viewing, where convenience and brevity matter.

Parameters:
timeOfDay - a long (not Date) value that represents the time of day as milliseconds since midnight
Throws:
IllegalArgumentException - if timeOfDay < 0; timeOfDay > TimeLength.dayMax

getTimeOfDayStampForFile

public static String getTimeOfDayStampForFile()

getTimeOfDayStampForFile

public static String getTimeOfDayStampForFile(Date date)
                                       throws IllegalArgumentException
Throws:
IllegalArgumentException

getTimeStamp

public static String getTimeStamp()

getTimeStamp

public static String getTimeStamp(Date date)
                           throws IllegalArgumentException
...

Throws:
IllegalArgumentException - if date == null

getTimeStampConcise

public static String getTimeStampConcise(Date date)
                                  throws IllegalArgumentException
Returns a more compact result than getTimeStamp by:
  1. always dropping the time zone
  2. dropping any low significance fields which can be implicitly understood
The result, however, always contains at least the year.

This method first calls getTimeStamp and uses that String for all subsequent processing. The time zone is immediately dropped. Next, if the millisecond field of the result is all zeroes (".000")), it is removed. If and only if milliseconds were dropped, then checks the second portion, removing it too if all zeroes (":00"). Similarly, minutes and hours are examined and removed if zeroes ("00" for seconds and "T00" for hours), and day of the month and month of the year are examined and removed if a one ("-01").

Here are examples of what this method returns, in the form of fullDate --> conciseDate, where fullDate is what getTimeStamp returns and conciseDate what this method returns:

This method is typically used to format dates for human viewing, where convenience and brevity matter. It is obviously unsuitable, however, for providing precise and unambiguous date representations (the lack of a time zone alone forbids that), which require getTimeStamp/getTimeStampForFile instead.

Throws:
IllegalArgumentException

getTimeStampForFile

public static String getTimeStampForFile()

getTimeStampForFile

public static String getTimeStampForFile(Date date)
                                  throws IllegalArgumentException
...

Throws:
IllegalArgumentException - if date == null

parseDayStamp

public static Date parseDayStamp(String string)
                          throws IllegalArgumentException,
                                 ParseException
Throws:
IllegalArgumentException
ParseException

parseTimeStamp

public static Date parseTimeStamp(String string)
                           throws IllegalArgumentException,
                                  ParseException
Throws:
IllegalArgumentException
ParseException

parseTimeStampForFile

public static Date parseTimeStampForFile(String string)
                                  throws IllegalArgumentException,
                                         ParseException
Throws:
IllegalArgumentException
ParseException

getDayOfWeekName

public static String getDayOfWeekName(int dayOfWeek)
                               throws IllegalArgumentException
Returns the day of the week name as a String which corresponds to dayOfWeek.

Parameters:
dayOfWeek - an int that equals one of Calendar's day of the week constants (i.e. Calendar.SUNDAY thru Calendar.SATURDAY)
Throws:
IllegalArgumentException - if dayOfWeek is an invalid value

getDateInfo

private static DateUtil.DateInfo getDateInfo(Date date)
                                      throws IllegalArgumentException
Returns the DateInfo instance in the cache corresponding to the day of date. Result may be null.

Throws:
IllegalArgumentException - if date == null

setDateInfoCacheSizeMax

public static void setDateInfoCacheSizeMax(int sizeMax)
                                    throws IllegalArgumentException
Sets the dateInfoCacheSizeMax field to sizeMax.

The choice for sizeMax is a tradeoff between memory and performance. A smaller value obviously saves memory; in particular, may supply sizeMax = 0 to suppress the DateInfo cache entirely. On the other hand, a larger value causes better performance, both because it allows more days to be cached as well as because it usually results in fewer DateInfo instances per DateInfoBin, which thus reduces the length of the linear search that must be performed by DateInfoBin.findDateInfo.

Because there should ideally be no more then one DateInfo instance per DateInfoBin, a side effect of this method is that it resets dateInfoBins to a new array of length sizeMax, with every element being a newly created DateUtil.DateInfoBin instance. The dateInfoCacheSize and countDateInfoCacheMisses fields are also reset to 0. So, calling this method completely clears the DateInfo cache and resets its state.

Throws:
IllegalArgumentException - if sizeMax < 0

getDateInfoCacheIssues

private static String getDateInfoCacheIssues()
Contract: the result always ends with a newline.