Chapters
Prior to java8, java.util.Date and java.sql.Date are widely used classes for handling date and time. In java 8 and above, classes in java.time are recommended to be used for handling date&time.
java.util.Date class is not recommended anymore due to lots of issues like implementing coordinated universal time(UTC). Although the Date class is intended to reflect coordinated universal time (UTC), it may not do so exactly, depending on the host environment of the Java Virtual Machine.
Nearly all modern operating systems assume that 1 day = 24 × 60 × 60 = 86400 seconds in all cases. In UTC, however, about once every year or two there is an extra second, called a "leap second". Prior to java 8, Joda-Time was used as an alternative for handling date & time.
java.util.Date is a mutable class that represents a specific instant in time, with millisecond precision since the 1st of January 1970 00:00:00 GMT(the epoch time). Since java 8, several constructors and methods of this class have been deprecated. However, there are few constructors and methods left that we can use.
To instantiate a java.util.Date instance, we can use two constructors:
Instance of this class must be "normalized" or in other words the hours, minutes, seconds, and milliseconds must be set to zero.
Since java 8, several constructors and methods of this class have been deprecated. However, there are few constructors and methods left that we can use. Take a look at this example.
For formatting date values in Date objects, use
To support SQL data types related to time like SQL TIME and SQL TIMESTAMP, use Time and Timestamp classes. Time is a thin wrapper around the java.util.Date class that allows the JDBC API to identify this as an SQL TIME value. Timestamp is a thin wrapper around java.util.Date that allows the JDBC API to identify this as an SQL TIMESTAMP value.
In the description above, we can say that
Calendar class is an abstract class that provides methods for converting between a specific instant in time and a set of calendar fields such as YEAR, MONTH, DAY_OF_MONTH, HOUR, and so on, and for manipulating the calendar fields, such as getting the date of the next week.
An instant in time can be represented by a millisecond value that is an offset from the Epoch, January 1, 1970 00:00:00.00 GMT (Gregorian). The class also provides additional fields and methods for implementing a concrete calendar system. Those fields and methods are defined as protected.
Since java 8, most setter and getter methods of
To instantiate an object related to Calendar object, we use the
This example demonstrates Calendar class.
To create a Calendar with consistent locale and timezone, use
To get system's default locale, use
For basic instantiation, call
To convert
To diplay Calendar field numerical value, we use
If a Calendar field contains a numerical value, use
Don't be confused with numerical value and numerical identifier. Numerical value is a representation of something whereas numerical identifier is an identifier to identify a Calendar field. For example, the numerical value of
To clarify further, take a look at this example.
Assume that these codes are part of the codebase in the example above. The first
If you wanna change the first day of a week, use
Take note that changing the first day of a week may affect the week count of Calendar class.
Calendar class has lots of fields that we can use to obtain information regarding date&time. This example demonstrates obtaining information from some Calendar fields.
In the example above, day 23 of March is in week 4 of March 2022.
Take note that
If we count days from the first day of next month(jan 1) up to the day before the first day of week, those days are the minimal days. So, jan 1 is thursday, jan 2 is friday, jan 3 is saturday and jan 4 is sunday. Those days sum up to 4 which is the value that we assign to
In
In
In ISO 8601 standard, the default first day of week is monday and the default minimal days in first week is 4. To see the effect of minimal days in other week-related fields, take a look at this example.
We can set a value to a particular Calendar field by using the first one. If we want to set year, month and day by invoking
Example: Consider a GregorianCalendar originally set to August 31, 1999. Calling set(Calendar.MONTH, Calendar.SEPTEMBER) sets the date to September 31, 1999. This is a temporary internal representation that resolves to October 1, 1999 if getTime()is then called.
However, a call to set(Calendar.DAY_OF_MONTH, 30) before the call to getTime() sets the date to September 30, 1999, since no recomputation occurs after set() itself. To check if a Calendar field has been set or will be changed, call isSet method.
If you wanna set time, use these overloaded forms of
Just remember this general time diagram to easily determine which field is smaller and larger.
In the diagram above,
In the second
A more detailed explanation about
Subclasses should, if possible, override this with a more efficient implementation. For
This method sets the date of this Calendar with the given date specifiers - week year, week of year, and day of week. Unlike the set method, all of the calendar fields and time values are calculated upon return. This example demonstrates this method.
Calendar has two modes for interpreting the calendar fields, lenient and non-lenient. When a Calendar is in lenient mode, it accepts a wider range of calendar field values than it produces. When a Calendar recomputes calendar field values for return by
When a Calendar is in non-lenient mode, it throws an exception if there is any inconsistency in its calendar fields. For example, a GregorianCalendar always produces
This example demonstrates lenient and non-lenient modes.
Calendar class has methods that can be used to make a comparison between dates. Take a look at this example.
If you wanna reset a single Calendar field, you can use
GregorianCalendar is a concrete subclass of
That is, dates are computed by extrapolating the current rules indefinitely far backward and forward in time. As a result,
GregorianCalendar
may be used for all years to generate meaningful and consistent results.
However, dates obtained using GregorianCalendar are historically accurate only from March 1, 4 AD onward, when modern Julian calendar rules were adopted. Before this date, leap year rules were applied irregularly, and before 45 BC the Julian calendar did not even exist.
Unlike Calendar class, this class has constructors that can set date and time individually. Take a look at this example.
These maximum and minimum related methods may be affected by the current values of the
You might ask what's the difference between Julian and Gregorian Calendar. The only difference between the Gregorian and the Julian calendar is the leap year rule. The Julian calendar specifies leap years every four years, whereas the Gregorian calendar specifies leap years every four years but omits century years which are not divisible by 400.
For example, In Gregorian Calendar, year 1600 and 2000 are leap years because they're divisible by 400. Year 1700, 1800 and 1900 are not leap years because they're not divisible by 400.
TimeZone is an abstract class that represents a time zone offset, and also figures out daylight savings. A time zone is an area that observes a uniform standard time for legal, commercial and social purposes.
To obtain the time zone where java platform is running, use the
This method returns a historically correct offset value if an underlying TimeZone implementation subclass supports historical Daylight Saving Time schedule and GMT offset changes. Take note that UTC is effectively a successor to Greenwich Mean Time.
This method returns the offset in milliseconds which can be converted to hour which one of the numbers that we see in this UTC/GMT time zone format:
UTC+-Hours:Minutes or GMT+-Hours:Minutes
Examples:
If you're using Calendar class, a time offset from UTC is stored in
SimpleTimeZone is a concrete subclass of TimeZone that represents a time zone for use with a Gregorian calendar. The class holds an offset from GMT, called raw offset, and start and end rules for a daylight saving time schedule.
Since it only holds single values for each, it cannot handle historical changes in the offset from GMT and the daylight saving schedule, except that the
This example demonstrates SimpleTimeZone and how to schedule daylight saving time.
Month, day and day-of-week have different combinations that have different effects in the start-rule and end-rule of daylight saving time. You can read the combinations in the documentation.
In the example above, this combination in the start-rule:
means first Sunday in April. Therefore, the daylight saving time of
Next, let's talk about time mode.
For example, In the example above, the local time is UTC+01:00(1*60*60*1000 or 3600000 millis or one hour offset). Then, the start-rule of SimpleTimeZone instance in the example above is set to first sunday of April 2021. starting time is 3600000 millis or 1:00am in UTC time(24-hour format).
The returned value of
First off, Start time is 1:00am(3600000 millis) in UTC time. Then, we convert that start time to our local time which is UTC+1(UTC+01:00). Thus, the starting time in UTC+1 is 2:00am. Then, we move the time forward by an hour because we assign 1 hour or 3600000 millis to
Now, for the end-rule, end time is 1:00am(3600000 millis) in UTC time. Then, we convert that end time to our local time which is UTC+1(UTC+01:00). Thus, the end time in UTC+1 is 2:00am. Then, we move the time backward by an hour because we assign 1 hour or 3600000 millis to
If we change the
If we change the
That's why ending time is 01:00am UTC+1 DST, convert ending time back to standard UTC+1 which 00:00am UTC+1, move time backward by an hour and DST ends at 23:00pm.
Now, to test if our DST schedule is working, take a look at this example.
Don't forget to set your program's default timezone to the timezone we created. Use
If you want to use
Aside from the SimpleTimeZone constructor that I demonstrated in the example above, SimpleTimeZone has three more constructors. These constructors:
Uses
This constructor:
Doesn't have daylight time schedule. However, we can use
Month, day and day-of-week combinations that I explained above is applicable here. Take note that these methods(and their overloaded forms) use
Next, let's discuss the methods that I used in the example above.
If you want to get offset from UTC with DST adjustment, use
The classes defined in
This API is new and improved alternative to old date-and-time-related classes like
In this topic, I'm gonna be discussing classes that are commonly used and follow ISO-8601 calendar system such as
Classes like Month, DayOfWeek, Year, YearMonth and MonthDay are also classes that follow ISO-8601 although they store single-value or partial date/time information compared to the classes mentioned above.
All the instances of the classes mentioned above are called temporal objects because those classes implement Temporal or its superinterface which is the TemporalAccessor. Also, the classes mentioned above are immutable and thread-safe.
If you want to extend this API like adding different computations of date and time, you should read the classes and documentation in java.time.temporal package.
To quantify date, use Period class. To quantify time, use Duration class.
We can access and obtain date and time from these classes: Instant, LocalDate, LocalTime, OffsetTime, LocalDateTime, OffsetDateTime and ZonedDateTime.
To format the values of these date-related classes, use DateTimeFormatter. I already discuss this class in this blogpost.
To get the time in our system clock, we invoke
Where possible, applications should use
Many applications can be written only using LocalDate, LocalTime and Instant, with the time-zone added at the user interface (UI) layer. For example, you get your system's date/time using
Then, call
The offset-based date-time types OffsetTime and OffsetDateTime, are intended primarily for use with network protocols and database access. For example, most databases cannot automatically store a time-zone like 'Europe/Paris', but they can store an offset like '+02:00'.
Next, let's discuss how to get specific part of a date like month, day, year etc. This example demonstrates getter methods that get specific part of a date.
In the example above,
TemporalField is a field of date-time, such as month-of-year or minute-of-hour. ChronoField is an enum that contains a standard set of fields that are compatible with
There are different ways of setting date and time. However, one of the common ways of setting date and time is by using
If we want to parse a text to a temporal object, we use
This example demonstrates
To subtract or add date or time to a temporal implementations highligted in this topic, we use methods with minus or plus prefix. This example demonstrates some of those methods.
This example demonstrates
TemporalAmount is a framework-level interface defining an amount of time, such as "6 hours", "8 days" or "2 years and 3 months". This interface has two concrete implementations: Duration and Period.
Period is just like
Duration is just like
To modify date and time in temporal objects, we use
Take note that
There are three methods with 'with' prefix that I wanna discuss.
Overlap happens during transition from daylight saving time to standard time. Gap happens during transition from standard time to daylight saving time. This documentation has a definition about gaps and overlaps.
Next, let's discuss
This example demonstrates
TemporalField is a field of date-time, such as month-of-year or minute-of-hour. ChronoField is an enum that contains a standard set of fields that are compatible with
Use
a temporal object can be queried to extract information from it. To do that, we use the
Chronology is an interface that defines a calendar system, used to organize and identify dates. Java supports different types of calendars and those calendars implement this interface. Most of the time, the default calendar system used by java platforms is the ISO calendar system. IsoChronology defines the rules of the ISO calendar system.
Duration and Period implement TemporalAmount that is used as a parameter of methods such as
Notice that the format
- Overview
- java.util.Date and java.sql.Date
- Calendar Class
- Displaying Date&Time Field Names/Values
- Date&Time Fields
- add(), set() and roll() Methods
- getActualMinimum() and getActualMaximum() Methods
- setWeekDate() Method
- Lenient and Non-Lenient Modes
- Comparing Dates
- GregorianCalendar Class
- TimeZone Class
- java.time Package
Overview
Prior to java8, java.util.Date and java.sql.Date are widely used classes for handling date and time. In java 8 and above, classes in java.time are recommended to be used for handling date&time.
java.util.Date class is not recommended anymore due to lots of issues like implementing coordinated universal time(UTC). Although the Date class is intended to reflect coordinated universal time (UTC), it may not do so exactly, depending on the host environment of the Java Virtual Machine.
Nearly all modern operating systems assume that 1 day = 24 × 60 × 60 = 86400 seconds in all cases. In UTC, however, about once every year or two there is an extra second, called a "leap second". Prior to java 8, Joda-Time was used as an alternative for handling date & time.
java.util.Date and java.sql.Date
java.util.Date is a mutable class that represents a specific instant in time, with millisecond precision since the 1st of January 1970 00:00:00 GMT(the epoch time). Since java 8, several constructors and methods of this class have been deprecated. However, there are few constructors and methods left that we can use.
To instantiate a java.util.Date instance, we can use two constructors:
Date()
and Date(long date)
. This example demonstrates these two constructors.
import java.util.Date; public class SampleClass{ public static void main(String[] args){ Date currentDate = new Date(); Date specificDate = new Date(1332416372111L); Date epoch = new Date(0); System.out.println(currentDate); System.out.println(specificDate); System.out.println(epoch); } } Result(may vary) Mon Mar 21 04:37:24 CST 2022 Thu Mar 22 19:39:32 CST 2012 Thu Jan 01 00:00:00 CST 1970
Date()
constructor creates a Date instance with the current date&time of our machine. Date(long date)
creates a Date instance with date&time extracted from the given timestamp. To compare to Date objects, we can use equals
method. We can also check if a Date object is before or after another Date object by invoking before
and after
methods. Take a look at this example.
import java.util.Date; public class SampleClass{ public static void main(String[] args){ Date currentDate = new Date(); Date specificDate = new Date(1332416372111L); Date specificDate2 = new Date(1332416372111L); System.out.println(currentDate.after(specificDate)); System.out.println(specificDate.before(currentDate)); System.out.println(specificDate.equals(specificDate2)); } } Result true true trueFor formatting date values in Date objects, use
DateFormat
and SimpleDateFormat
classes. I explained them in this article. Here's an example of formatting date using SimpleDateFormat
.
import java.text.SimpleDateFormat; import java.util.Date; public class SampleClass{ public static void main(String[] args){ String pattern = "MM/dd/yyyy hh:mm:ss a"; Date date = new Date(1332416372111L); SimpleDateFormat sdf = new SimpleDateFormat(pattern); String result = sdf.format(date); System.out.println("Unformatted"); System.out.println(date); System.out.println("Formatted"); System.out.println(result); } } Result Unformatted Thu Mar 22 19:39:32 CST 2012 Formatted 03/22/2012 07:39:32 PMjava.sql.Date is a thin wrapper around a millisecond value that allows JDBC to identify the instance of this class as an SQL DATE value. A milliseconds value represents the number of milliseconds that have passed since January 1, 1970 00:00:00.00 GMT.
Instance of this class must be "normalized" or in other words the hours, minutes, seconds, and milliseconds must be set to zero.
java.sql.Date
is a subclass of java.util.Date
.
Since java 8, several constructors and methods of this class have been deprecated. However, there are few constructors and methods left that we can use. Take a look at this example.
import java.sql.Date; public class SampleClass{ public static void main(String[] args){ Date date1 = new Date(0); Date date2 = new Date(0); System.out.println(date1); System.out.println(date1.after(date2)); System.out.println(date2.before(date1)); System.out.println(date1.equals(date2)); } } Result 1970-01-01 false false true
Date(long date)
Constructs a Date object using the given milliseconds time value or timestamp. before
method returns true if the Date object that invokes the method precedes the given Date object. Otherwise, returns false. after
method is the opposite of before
method. equals
returns true if both Date objects are equal. Otherwise, returns false.
For formatting date values in Date objects, use
DateFormat
and SimpleDateFormat
classes. I explained them in this article.
To support SQL data types related to time like SQL TIME and SQL TIMESTAMP, use Time and Timestamp classes. Time is a thin wrapper around the java.util.Date class that allows the JDBC API to identify this as an SQL TIME value. Timestamp is a thin wrapper around java.util.Date that allows the JDBC API to identify this as an SQL TIMESTAMP value.
In the description above, we can say that
java.util.Date
should be used for handling date&time in our program whereas java.sql.Date
should be used as a wrapper for date values when dealing with databases.
Calendar Class
Calendar class is an abstract class that provides methods for converting between a specific instant in time and a set of calendar fields such as YEAR, MONTH, DAY_OF_MONTH, HOUR, and so on, and for manipulating the calendar fields, such as getting the date of the next week.
An instant in time can be represented by a millisecond value that is an offset from the Epoch, January 1, 1970 00:00:00.00 GMT (Gregorian). The class also provides additional fields and methods for implementing a concrete calendar system. Those fields and methods are defined as protected.
Since java 8, most setter and getter methods of
java.util.Date
and java.sql.Date
are deprecated. However, java introduces Calendar class. This class has getter and setter methods that interact with date attributes like days, months, time, etc.
To instantiate an object related to Calendar object, we use the
getInstance
method. This method is a static method has for overloaded form.getInstance()
getInstance(Locale aLocale)
getInstance(TimeZone zone)
getInstance(TimeZone zone, Locale aLocale)
This example demonstrates Calendar class.
import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; public class SampleClass{ public static void main(String[] args){ Calendar calendar = Calendar.getInstance(); System.out.println("System's Default Locale"); System.out.println(Locale.getDefault()); System.out.println("System's Default TimeZone"); System.out.println(TimeZone.getDefault().getID()); System.out.println(); System.out.println("Available Calendar Types"); for(String s : calendar.getAvailableCalendarTypes()) System.out.println(s); System.out.println(); System.out.println("Calendar Type: " + calendar.getCalendarType()); System.out.println("Calendar object instance" + " of GregorianCalendar?"); System.out.println(calendar instanceof GregorianCalendar); System.out.println(); Date date1 = calendar.getTime(); System.out.println("getTime: " + date1); long timestamp = calendar.getTimeInMillis(); Date date2 = new Date(timestamp); System.out.println("getTimeInMillis: " + date2); System.out.println(); } } Result(may vary) System's Default Locale en_US System's Default TimeZone Asia/Taipei Available Calendar Types gregory buddhist japanese Calendar Type: gregory Calendar Object instance of GregorialCalendar? true getTime: Wed Mar 23 17:23:51 CST 2022 getTimeInMillis: Wed Mar 23 17:23:51 CST 2022First off, I invoke
getInstance()
method. This method returns a Calendar object with default time zone and locale. In my system, the default locale "en-US" which is synonymous with Locale.US
. My system's default timezone is "Asia/Taipei". Take note that getInstance()
is inconsistent because every system may have different default locale, timezone and calendar type.
To create a Calendar with consistent locale and timezone, use
getInstance(TimeZone zone, Locale aLocale)
method and set the locale and timezone that you like. If you just want a consistent locale, use getInstance(Locale aLocale)
method. If you just want a consistent timezone, use getInstance(TimeZone zone)
.
To get system's default locale, use
Locale.getDefault()
method. To get system's default timezone, use TimeZone.getDefault()
method. To get the String representation of a timezone in TimeZone object, use getID()
method.
getAvailableCalendarTypes
method returns a Set<String>
object that contains calendar type names. In the example above, there are three available calendar types: gregory, buddhist and japanese. The default calendar type is "gregory" which stands for "Gregorian". To use other calendar types, instantiate a Calendar object using Calendar.Builder.
For basic instantiation, call
Calendar.Builder.setCalendarType(String type)
method and then build the Calendar by calling build
method. For example, Calendar c = Calendar.Builder.setCalendarType("japanese").build()
getCalendarType
returns the type of a Calendar object. In the example above, the calendar type is "gregory". Notice that the Calendar object in the example is an instance of GregorianCalendar
. GregorianCalendar
is a concrete implementation of Calendar provides the standard calendar system used by most of the world, which is the Gregorian Calendar. We will discuss that class later.
getTime
method returns a java.util.Date
object where the date&time of the Calendar object is wrapped into. getTimeInMillis
method returns the Calendar object's date&time in milliseconds. We can use this methods to convert Calendar object's date&time to java.util.Date
or java.sql.Date
.
To convert
java.util.Date
or java.sql.Date
date&time to Calendar
, use setTime(java.util.Date date)
. To convert a timestamp(millis) to Calendar
, use setTimeInMillis
method.
Displaying Date&Time Field Names/Values
To diplay Calendar field numerical value, we use
get
method. To display Calendar field name, use getDisplayName
method. To display all fields of a Calendar field, use getDisplayNames
method. Take note that not all Calendar fields have names and fields. This example demonstrates methods that are mentioned above.
import java.util.Calendar; import java.util.Locale; import java.util.Map; public class SampleClass{ public static void main(String[] args){ Calendar calendar = Calendar.getInstance(); System.out.println("Days"); for(Map.Entry entry : calendar.getDisplayNames( Calendar.DAY_OF_WEEK, Calendar.LONG_FORMAT, Locale.US).entrySet()) System.out.println(entry.getKey() + " | " + entry.getValue()); System.out.println(); System.out.println("First Day of Week: " + calendar.getFirstDayOfWeek()); System.out.println("Day of Week(Plain): " + calendar.get(Calendar.DAY_OF_WEEK)); System.out.println("Day of Week(Short Format, US): " + calendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT_FORMAT, Locale.US)); System.out.println("Day of Week(Short Format, France): " + calendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT_FORMAT, Locale.FRANCE)); System.out.println("Day of Week(Long Format, US): " + calendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG_FORMAT, Locale.US)); } } Result(may vary) Days Monday | 2 Thursday | 5 Friday | 6 Sunday | 1 Wednesday | 4 Tuesday | 3 Saturday | 7 First Day of Week: 1 Day of Week(Plain): 4 Day of Week(Short Format, US): Wed Day of Week(Short Format, France): mer. Day of Week(Long Format, US): Wednesday
getDisplayNames
returns a Map object that contains fields of a field. In the example above, Calendar.DAY_OF_WEEK
has 7 fields that represent days. Field with a numerical value of 1 is the first day of a week. First day of a week is locale-dependent. For example, in France, first day of a week is CALENDAR.MONDAY
. In US, the first day of a week is CALENDAR.SUNDAY
getDisplayNames(int field, int style, Locale locale)
has three parameters. field
is a Calendar field with fields. Take note that most Calendar fields have a numerical(int) identifiers that are compatible with particular methods. For example, the numerical identifier of Calendar.DAY_OF_WEEK
is compatible with getDisplayNames
method. It's also compatible with getDisplayName
method.
CALENDAR.SUNDAY
is not compatible with getDisplayNames
because it doesn't have its own fields. It's not also compatible with getDisplayName
because it doesn't have display name. Its display name is in Calendar.DAY_OF_WEEK
. getDisplayNames
and getDisplayName
return null or an exception if the numerical identifier of a field is not compatible with them.
style
is the format of the extracted field data. There are different types of field formatting that we can use. You can refer to Calendar.ALL_STYLES field to see all available styles. locale
is the specific locale of you choosing.
getDisplayName
is similar to getDisplayNames
and has the same parameters. Although, getDisplayName
extracts display names of a field. For example, getDisplayName
extracts the display name of the current day of a week from Calendar.DAY_OF_WEEK
.
If a Calendar field contains a numerical value, use
get
method. In the example above, get
method extracts the numerical identifier of Calendar.WEDNESDAY
which is 4. This number is the current numerical value of Calendar.DAY_OF_WEEK
.
Don't be confused with numerical value and numerical identifier. Numerical value is a representation of something whereas numerical identifier is an identifier to identify a Calendar field. For example, the numerical value of
Calendar.DAY_OF_MONTH
represents current day of our Calendar object.
To clarify further, take a look at this example.
System.out.println("identifier: "+Calendar.DAY_OF_WEEK); //7
System.out.println("value: "+Calendar.get(Calendar.DAY_OF_WEEK)); //4
Assume that these codes are part of the codebase in the example above. The first
println
returns 7. This number is the numerical identifier. The second println
returns 4. This number is the numerical value.
getFirstDayOfWeek
returns the numerical identifier of the first day of a week. In the example above, this method returns 4 which is the numerical identifier of Calendar.WEDNESDAY
.
If you wanna change the first day of a week, use
setFirstDayOfWeek(int value)
method. The value
parameter should be one of the numerical identifier of fields of Calendar.DAY_OF_WEEK
. For example:setFirstDayOfWeek(Calendar.MONDAY)
Take note that changing the first day of a week may affect the week count of Calendar class.
Date&Time Fields
Calendar class has lots of fields that we can use to obtain information regarding date&time. This example demonstrates obtaining information from some Calendar fields.
import java.util.Calendar; import java.util.Locale; public class SampleClass{ public static void main(String[] args){ Calendar calendar = Calendar.getInstance(); System.out.println("Month: " + calendar.getDisplayName(Calendar.MONTH, Calendar.LONG_FORMAT, Locale.US)); System.out.println("Day: " + calendar.get(Calendar.DAY_OF_MONTH)); System.out.println("Year: " + calendar.get(Calendar.YEAR)); System.out.println("Hour(24-hour format): " + calendar.get(Calendar.HOUR_OF_DAY)); System.out.println("Hour(12-hour format): " + calendar.get(Calendar.HOUR)); System.out.println("Minutes: " + calendar.get(Calendar.MINUTE)); System.out.println("Seconds: " + calendar.get(Calendar.SECOND)); System.out.println("AM_PM: " + calendar.getDisplayName(Calendar.AM_PM, Calendar.SHORT_FORMAT, Locale.US)); System.out.println(); //If isWeekDateSupported method returns false, //your Calendar object doesn't support //week dates and week-related fields may not //work as intended System.out.println("Week Date Supported? " + calendar.isWeekDateSupported()); System.out.println("Week Year: " + calendar.getWeekYear()); System.out.println("Minimal Days in First Week: " + calendar.getMinimalDaysInFirstWeek()); System.out.println("Weeks in Week Year: " + calendar.getWeeksInWeekYear()); System.out.println("Week of Month: " + calendar.get(Calendar.WEEK_OF_MONTH)); System.out.println("Week of Year: " + calendar.get(Calendar.WEEK_OF_YEAR)); } } Result(may vary) Month: March Day: 23 Year: 2022 Hour(24-hour format): 17 Hour(12-hour format): 5 Minutes: 34 Seconds: 31 AM_PM: PM Week Date Supported? true Week Year: 2022 Minimal Days in First Week: 1 Weeks in Week Year: 53 Week of Month: 4 Week of Year: 13In the specified date&time in a Calendar object,
Calendar.MONTH
holds month. Calendar.DAY_OF_MONTH
holds day in the month Calendar.YEAR
holds year. Calendar.HOUR_OF_DAY
holds hours in 24-hour format. Calendar.HOUR
holds hours in 12-hour format. Calendar.MINUTE
holds minutes. Calendar.SECOND
holds seconds. Calendar.AM_PM
holds AM or PM value.
Calendar.WEEK_OF_YEAR
holds the week number in a year where Calendar.DAY_OF_MONTH
belongs. In the example above, day 23 of March is in week 13 of year 2022. Calendar.WEEK_OF_MONTH
holds the week number in a month where Calendar.DAY_OF_MONTH
belongs.
In the example above, day 23 of March is in week 4 of March 2022.
getWeeksInWeekYear
returns the total number of weeks in a year. In the example above, year 2022 has a total of 53 weeks.
Take note that
WEEK_OF_YEAR
, DAY_OF_MONTH
, WEEK_OF_MONTH
and the return value of getWeeksInWeekYear
are affected by minimal days in first week. Minimal days are minimum days required for a first week to be part of next month. Take a look at this example.
import java.util.GregorianCalendar; import java.util.Calendar; import java.util.Locale; public class SampleClass{ public static void main(String[] args){ GregorianCalendar calendar1 = new GregorianCalendar(1998, Calendar.JANUARY, 1); calendar1.setFirstDayOfWeek(Calendar.MONDAY); calendar1.setMinimalDaysInFirstWeek(4); System.out.println("Output #1"); System.out.println("Week Year: " + calendar1.getWeekYear()); System.out.println(); calendar1.setFirstDayOfWeek(Calendar.SUNDAY); System.out.println("Output #2"); System.out.println("Week Year: " + calendar1.getWeekYear()); System.out.println(); calendar1.setFirstDayOfWeek(Calendar.TUESDAY); System.out.println("Output #3"); System.out.println("Week Year: " + calendar1.getWeekYear()); System.out.println(); } } Result Output #1 Week Year: 1998 Output #2 Week Year: 1997 Output #3 Week Year: 1998First off,
getWeekYear
returns the year where the week belongs. In the example above, minimal days in first week is set to 4 and first day of week is set to Calendar.MONDAY
. getWeekYear
returns 1998 because the week where jan 1 belongs is part of 1998. This week starts from first day of week which is monday. Monday in this week is dec 29.
If we count days from the first day of next month(jan 1) up to the day before the first day of week, those days are the minimal days. So, jan 1 is thursday, jan 2 is friday, jan 3 is saturday and jan 4 is sunday. Those days sum up to 4 which is the value that we assign to
setMinimalDaysInFirstWeek
. Thus, we can say that the week starting from monday, dec 29, 1997 to sunday, jan 4, 1998 is part of 1998.
In
Output #2
, the returned week year is 1997 because I changed the first day of week to sunday. If we count days from the first day of next month(jan 1) up to the day before the first day of week, the total minimal days is less than 4. Thus, we can say that the week starting from sunday, dec 28, 1997 to saturday, jan 3, 1998 is part of 1997.
In
Output #3
, the returned week year is 1998 because I changed the first day of week to tuesday. If we count days from the first day of next month(jan 1) up to the day before the first day of week, the total minimal days is greater than 4. Thus, we can say that the week starting from tuesday, dec 30, 1997 to monday, jan 5, 1998 is part of 1998.
In ISO 8601 standard, the default first day of week is monday and the default minimal days in first week is 4. To see the effect of minimal days in other week-related fields, take a look at this example.
import java.util.GregorianCalendar; import java.util.Calendar; import java.util.Locale; public class SampleClass{ public static void main(String[] args){ GregorianCalendar calendar1 = new GregorianCalendar(1998, Calendar.JANUARY, 1); calendar1.setFirstDayOfWeek(Calendar.MONDAY); calendar1.setMinimalDaysInFirstWeek(4); System.out.println("Output #1"); System.out.println("Week Year: " + calendar1.getWeekYear()); System.out.println("Week of Year: " + calendar1.get(Calendar.WEEK_OF_YEAR)); System.out.println("Week of Month: " + calendar1.get(Calendar.WEEK_OF_MONTH)); System.out.println(); calendar1.setFirstDayOfWeek(Calendar.SUNDAY); System.out.println("Output #2"); System.out.println("Week Year: " + calendar1.getWeekYear()); System.out.println("Week of Year: " + calendar1.get(Calendar.WEEK_OF_YEAR)); System.out.println("Week of Month: " + calendar1.get(Calendar.WEEK_OF_MONTH)); System.out.println(); } } Result Output #1 Week Year: 1998 Week of Year: 1 Week of Month: 1 Output #2 Week Year: 1997 Week of Year: 53 Week of Month: 0In the result above,
Output #1
indicates that the week is part of 1998. Output #2
indicates that the week is part of 1997. If you're wondering why week of month in Output #2
is 0, a zero week of month means that the week where the specified day belongs is part of previous month. Thus, we can say that the week where jan 1, 1998 belongs is part of december 1997.
add(), set() and roll() Methods
add
, set
and roll
methods are essentials method in Calendar class. Let's start with set
method. set
method has multiple forms and I'm gonna demonstrate two of them:set(int field, int value)
set(int year, int month, int date)
We can set a value to a particular Calendar field by using the first one. If we want to set year, month and day by invoking
set
method once, use the second form. This example demonstrates set
method.
import java.util.Calendar; public class SampleClass{ public static void main(String[] args){ Calendar calendar = Calendar.getInstance(); System.out.println(calendar.getTime()); calendar.set(Calendar.YEAR, 1994); System.out.println(calendar.getTime()); calendar.set(2012, Calendar.SEPTEMBER, 1); System.out.println(calendar.getTime()); } } Result(may vary) Sat Mar 26 20:22:24 CST 2022 Sat Mar 26 20:22:24 CST 1994 Sat Sep 01 20:22:24 CST 2012Take note that calling
set
method alone doesn't recompute time value in milliseconds until the next call to get
, getTime
, getTimeInMillis
, add
, or roll
method is made.
Example: Consider a GregorianCalendar originally set to August 31, 1999. Calling set(Calendar.MONTH, Calendar.SEPTEMBER) sets the date to September 31, 1999. This is a temporary internal representation that resolves to October 1, 1999 if getTime()is then called.
However, a call to set(Calendar.DAY_OF_MONTH, 30) before the call to getTime() sets the date to September 30, 1999, since no recomputation occurs after set() itself. To check if a Calendar field has been set or will be changed, call isSet method.
If you wanna set time, use these overloaded forms of
set
method.set(int year, int month, int date, int hourOfDay, int minute)
set(int year, int month, int date, int hourOfDay, int minute, int second)
add(f, delta)
adds delta
to field f
. Unlike set method, this method recomputes and adjusts date&time values if it's invoked. This example demonstrates add method.
import java.util.Calendar; public class SampleClass{ public static void main(String[] args){ Calendar calendar = Calendar.getInstance(); calendar.set(2010, Calendar.JANUARY, 31); System.out.println(calendar.getTime()); calendar.add(Calendar.MONTH, 13); System.out.println(calendar.getTime()); } } Result(may vary) Mon Jan 31 08:59:15 CST 2010 Wed Feb 28 08:59:15 CST 2011In the example above, the original date is Jan 13, 2010 then, I added additional 13 months to
Calendar.MONTH
field. The date was changed to Feb and as you can see, the day was adjusted from 31 to 28. This adjustment was necessary because maximum day(no leap year) of February in Gregorian Calendar is day 28. Also, the year was changed to 2011.
roll
method is just like add
except for the adjustments. Unlike add
, roll
method doesn't adjust larger field. Larger field represents a larger unit of time than the given field. Smaller field represents a smaller unit of time than the given field. This example demonstrates roll method.
import java.util.Calendar; public class SampleClass{ public static void main(String[] args){ Calendar calendar = Calendar.getInstance(); calendar.set(2010, Calendar.JANUARY, 31); System.out.println(calendar.getTime()); calendar.roll(Calendar.MONTH, 15); System.out.println(calendar.getTime()); } } Result(may vary) Sun Jan 31 09:26:08 CST 2010 Fri Apr 30 09:26:08 CST 2010In the example above, the original date is Jan 31, 2010. We can see from the result that the day is adjusted to 30 and the day of week is changed to Fri. However, the year stays the same. Year doesn't change because
Calendar.YEAR
field is larger than Calendar.MONTH
. Calendar.DAY_OF_MONTH
field is smaller than Calendar.MONTH
.
Just remember this general time diagram to easily determine which field is smaller and larger.
MILLISECOND < SECOND < MINUTE < HOUR < DAY_OF_MONTH < MONTH < YEAR
In the diagram above,
MILLISECOND
is the smallest unit of time and YEAR
is the largest unit of time. Take note that week-related fields like DAY_OF_WEEK
can be a given value for set
, add
and roll
. Take a look at this example.
import java.util.Calendar; public class SampleClass{ public static void main(String[] args){ Calendar calendar = Calendar.getInstance(); calendar.setFirstDayOfWeek(Calendar.SUNDAY); calendar.set(2010, Calendar.DECEMBER, 31); System.out.println(calendar.getTime()); calendar.roll(Calendar.DAY_OF_WEEK, 1); System.out.println(calendar.getTime()); calendar.set(2010, Calendar.DECEMBER, 31); calendar.setFirstDayOfWeek(Calendar.SUNDAY); calendar.roll(Calendar.DAY_OF_WEEK, 2); System.out.println(calendar.getTime()); } } Result(may vary) Fri Dec 31 10:18:35 CST 2010 Sat Jan 01 10:18:35 CST 2011 Sun Dec 26 10:18:35 CST 2010In the third
println
, the date rolled back to Dec 26. This happened because when roll
is used with DAY_OF_WEEK
, changes revolve within the boundary of week. In the example above, first day of week starts at sunday which is Dec 26. Dec 26, 2010(Sunday) to Jan 01, 2011(Saturday) is the week where the given date(Dec 31, 2010) belongs.
In the second
println
, the date didn't roll back because the new date is in the boundary of the week. In the third println
, the date rolled back to Dec 26 because the new date(Jan 02, 2011) would have been out of the boundary of the week.
A more detailed explanation about
set
, add
and roll
methods can be found in the documentation.
getActualMinimum() getActualMaximum() Methods
getActualMinimum
and getActualMaximum
methods return the minimum and maximum value that the specified calendar field could have, given the time value of this Calendar. The default implementation of this method uses an iterative algorithm to determine the actual minimum value for the calendar field.
Subclasses should, if possible, override this with a more efficient implementation. For
getActualMinimum
, getMinimum
is simply returned in many cases. This example demonstrates these two methods.
import java.util.Calendar; public class SampleClass{ public static void main(String[] args){ Calendar calendar = Calendar.getInstance(); calendar.set(2022, Calendar.FEBRUARY, 1); System.out.println("Max Day of Month: " + calendar.getActualMaximum(Calendar.DAY_OF_MONTH)); calendar.set(2012, Calendar.FEBRUARY, 1); System.out.println("Max Day of Month: " + calendar.getActualMaximum(Calendar.DAY_OF_MONTH)); System.out.println("Min Week of Month: " + calendar.getActualMinimum(Calendar.WEEK_OF_MONTH)); } } Result(may vary) Max Day of Month: 28 Max Day of Month: 29 Min Week of Month: 0
setWeekDate() Method
This method sets the date of this Calendar with the given date specifiers - week year, week of year, and day of week. Unlike the set method, all of the calendar fields and time values are calculated upon return. This example demonstrates this method.
import java.util.Calendar; public class SampleClass{ public static void main(String[] args){ Calendar calendar = Calendar.getInstance(); System.out.println(calendar.getTime()); if(calendar.isWeekDateSupported()){ calendar.setWeekDate(2022, 40, Calendar.FRIDAY); System.out.println(calendar.getTime()); } else System.out.println("Week Dates not Supported!"); } } Result(may vary) Mon Mar 28 20:20:59 CST 2022 Fri Sep 30 20:20:59 CST 2022If your Calendar object doesn't support week dates,
setWeekDate
may not work properly or may throw UnsupportedOperationException.
Lenient and Non-Lenient Modes
Calendar has two modes for interpreting the calendar fields, lenient and non-lenient. When a Calendar is in lenient mode, it accepts a wider range of calendar field values than it produces. When a Calendar recomputes calendar field values for return by
get()
, all of the calendar fields are normalized. For example, a lenient GregorianCalendar
interprets MONTH == JANUARY
, DAY_OF_MONTH == 32
as February 1
.
When a Calendar is in non-lenient mode, it throws an exception if there is any inconsistency in its calendar fields. For example, a GregorianCalendar always produces
DAY_OF_MONTH
values between 1 and the length of the month. For example, A non-lenient GregorianCalendar
throws an exception upon calculating its time or calendar field values if any out-of-range field value has been set.
This example demonstrates lenient and non-lenient modes.
import java.util.Calendar; import java.util.GregorianCalendar; public class SampleClass{ public static void main(String[] args){ GregorianCalendar calendar = new GregorianCalendar(2010, Calendar.JANUARY, 1); System.out.println("Lenient? " + calendar.isLenient()); calendar.set(Calendar.DAY_OF_MONTH, 32); System.out.println(calendar.getTime()); calendar = new GregorianCalendar( 2010, Calendar.JANUARY, 1); calendar.setLenient(false); calendar.set(Calendar.DAY_OF_MONTH, 32); System.out.println(calendar.getTime()); } } Result Lenient? true Mon Feb 01 00:00:00 CST 2010 ...IllegalArgumentException...
isLenient
methods returns true if a Calendar object is lenient. Otherwise, returns false. setLenient
sets the Calendar object to lenient or non-lenient mode.
Comparing Dates
Calendar class has methods that can be used to make a comparison between dates. Take a look at this example.
import java.util.Calendar; public class SampleClass{ public static void main(String[] args){ Calendar calendar1 = Calendar.getInstance(); calendar1.clear(); calendar1.set(1994, Calendar.SEPTEMBER, 1); Calendar calendar2 = Calendar.getInstance(); calendar2.clear(); calendar2.set(1994, Calendar.SEPTEMBER, 1); System.out.println(calendar1.after(calendar2)); System.out.println(calendar2.before(calendar1)); System.out.println(); calendar1.setLenient(false); System.out.println(calendar1.compareTo(calendar2)); System.out.println(calendar1.equals(calendar2)); } } Result false false 0 false
clear
method resets date and time of this calendar(calendar1
) to Calendar-specific defaults. For example, the default date and time is January 1, 1970 00:00:00
(also called Unix time or Epoch time). This method also sets isSet
boolean flag to false for all the calendar fields, and the date and time calculations will treat the fields as if they had never been set.
If you wanna reset a single Calendar field, you can use
clear(int field)
method. This method set isSet
boolean flag to false for the specific Calendar field. This method may reset the Calendar field to its Calendar-specific default value.
after
method returns true if the date and time of Calendar object that calls the method is after the date and time of the given Calendar object. Otherwise, returns false. before
method is the opposite of after
method.
compareTo
method returns 0 if the millisecond representation of both dates are equals. Otherwise, returns a non-zero integer. compareTo
returns a negative integer if the millisecond(offset from epoch) of the given date is greater than the millisecond(offset from epoch) of the other date. Returns a positive integer if the millisecond of the given date is less than the millisecond of the other date.
equals
method compares millisecond(offset from epoch) and some parameters of two Calendar objects. The Calendar parameters are the values represented by the isLenient
, getFirstDayOfWeek
, getMinimalDaysInFirstWeek
and getTimeZone
methods. If there is any difference in those parameters between the two Calendars, this method returns false.
GregorianCalendar Class
GregorianCalendar is a concrete subclass of
Calendar
and provides the standard calendar system used by most of the world. This class implements proleptic Gregorian and Julian calendars.
That is, dates are computed by extrapolating the current rules indefinitely far backward and forward in time. As a result,
GregorianCalendar
may be used for all years to generate meaningful and consistent results.
However, dates obtained using GregorianCalendar are historically accurate only from March 1, 4 AD onward, when modern Julian calendar rules were adopted. Before this date, leap year rules were applied irregularly, and before 45 BC the Julian calendar did not even exist.
Unlike Calendar class, this class has constructors that can set date and time individually. Take a look at this example.
import java.util.Calendar; import java.util.GregorianCalendar; public class SampleClass{ public static void main(String[] args){ GregorianCalendar gc = new GregorianCalendar(2012, Calendar.FEBRUARY, 1); System.out.println("Actual Max: "+ gc.getActualMaximum(Calendar.DAY_OF_MONTH)); System.out.println("Max: "+ gc.getMaximum(Calendar.DAY_OF_MONTH)); System.out.println("Least Max: "+ gc.getLeastMaximum(Calendar.DAY_OF_MONTH)); System.out.println(); System.out.println("Actual Min: "+ gc.getActualMinimum(Calendar.DAY_OF_MONTH)); System.out.println("Min: "+ gc.getMinimum(Calendar.DAY_OF_MONTH)); System.out.println("Greatest Min: "+ gc.getGreatestMinimum(Calendar.DAY_OF_MONTH)); System.out.println(); System.out.println("Is 2012 a leap year? " + gc.isLeapYear(2012)); System.out.println("Cutover Date: " + gc.getGregorianChange()); } } Result(may vary) Actual Max: 29 Max: 31 Least Max: 28 Actual Min: 1 Min: 1 Greatest Min: 1 Is 2012 a leap year? true Cutover Date: Fri Oct 15 00:00:00 CST 1582
GregorianCalendar(int year, int month, int dayOfMonth)
constructs a GregorianCalendar with the given date set in the default time zone with the default locale. If you want to instantiate a GregorianCalendar with time set individally, use these two constructors.GregorianCalendar(int year, int month, int dayOfMonth, int hourOfDay, int minute)
GregorianCalendar(int year, int month, int dayOfMonth, int hourOfDay, int minute, int second)
getActualMaximum
returns the maximum value of a field in a specific date of the Calendar instance. If a year is leap year, the max day of month value that getActualMaximum
can get in February is 29. In the example above, 2012 is a leap year. Thus, getActualMaximum
returns 29.
getMaximum
returns the maximum value of a field based on the maximum value that get
method can get in any possible time value. The maximum day of month in Gregorian Calendar is 31. Thus, the maximum value that get
method can get is 31.
getLeastMaximum
returns the smallest max value that getActualMaximum
can get in any possible time value. If a year is not leap year, the max day of month value that getActualMaximum
can get in February is 28.
getActualMinimum
returns the minimum value of a field in a specific date of the Calendar instance. getMinimum
returns the minimum value of a field based on the minimum value that get
method can get in any possible time value. getGreatestMinimum
returns the highest min value that getActualMinimum
can get in any possible time value.
These maximum and minimum related methods may be affected by the current values of the
getFirstDayOfWeek
, getMinimalDaysInFirstWeek
, getGregorianChange
and getTimeZone
methods.
isLeapYear
method returns true if the given year is a leap year. Otherwise, returns false.
getGregorianChange
gets the Gregorian Calendar change date which is also called as the cutover date. This is the point when the switch from Julian dates to Gregorian dates occurred. Default is October 15, 1582 (Gregorian). Previous to this, dates will be in the Julian calendar. You can set your own cutover date by calling setGregorianChange(Date date)
method.
You might ask what's the difference between Julian and Gregorian Calendar. The only difference between the Gregorian and the Julian calendar is the leap year rule. The Julian calendar specifies leap years every four years, whereas the Gregorian calendar specifies leap years every four years but omits century years which are not divisible by 400.
For example, In Gregorian Calendar, year 1600 and 2000 are leap years because they're divisible by 400. Year 1700, 1800 and 1900 are not leap years because they're not divisible by 400.
TimeZone Class
TimeZone is an abstract class that represents a time zone offset, and also figures out daylight savings. A time zone is an area that observes a uniform standard time for legal, commercial and social purposes.
To obtain the time zone where java platform is running, use the
getDefault
static method. If we want to obtain a time zone via ID name, use getTimeZone(String ID)
static method. If you're using Calendar class, you can call getTimeZone()
method. This example demonstrates TimeZone class and some of its methods.
import java.util.TimeZone; import java.util.Calendar; public class SampleClass{ public static void main(String[] args){ //get system's default time zone TimeZone tz = TimeZone.getDefault(); System.out.println("My Default TimeZone ID"); System.out.println(tz.getID()); System.out.println("Display Name(Long Format)"); System.out.println( tz.getDisplayName(false, TimeZone.LONG)); System.out.println("Daylight Saving Time?"); System.out.println(tz.observesDaylightTime()); System.out.println("Offset from UTC+-00:00"); long millis = Calendar.getInstance().getTimeInMillis(); System.out.println("Millis: "+ tz.getOffset(millis)); double hour = tz.getOffset(millis) / 1000 / 60 / 60; System.out.println("Hour: " + hour); System.out.println(); } } Result(may vary) My Default TimeZone ID Asia/Taipei Display Name(Long Format) Taipe Standard Time Daylight Saving Time? false Offset UTC+-00:00 Millis: 28800000 Hour: 8.0
getID
method returns the ID name of a time zone. We can use ID names in getTimeZone(String ID)
. getDisplayName(boolean daylight, int style)
returns the display name of a time zone. daylight
is a boolean type that determines that if true, the returned display name is daylight savings even the specified time zone doesn't follow daylight saving time. If false, standard display name is returned.
display
is the format of the display name. We can use TimeZone.SHORT or TimeZone.LONG for formatting time zones. getDisplayName
has multiple forms. Look at them in the documentation.
observesDaylightTime
method returns true if the specified time zone follows daylight saving time. Otherwise, returns false.
getOffset
returns the offset of this time zone from UTC(UTC+-00:00) at the specified date. If Daylight Saving Time is in effect at the specified date, the offset value is adjusted with the amount of daylight saving.
This method returns a historically correct offset value if an underlying TimeZone implementation subclass supports historical Daylight Saving Time schedule and GMT offset changes. Take note that UTC is effectively a successor to Greenwich Mean Time.
This method returns the offset in milliseconds which can be converted to hour which one of the numbers that we see in this UTC/GMT time zone format:
UTC+-Hours:Minutes or GMT+-Hours:Minutes
Examples:
UTC+08:00
, GMT-10:00
If you're using Calendar class, a time offset from UTC is stored in
Calendar.ZONE_OFFSET
and Calendar.DST_OFFSET
. Calendar.ZONE_OFFSET
holds the raw offset. This offset is a time offset without adjustments due to Daylight Saving Time. Calendar.DST_OFFSET
is a time offset with adjustments due to Daylight Saving Time.
SimpleTimeZone Class
SimpleTimeZone is a concrete subclass of TimeZone that represents a time zone for use with a Gregorian calendar. The class holds an offset from GMT, called raw offset, and start and end rules for a daylight saving time schedule.
Since it only holds single values for each, it cannot handle historical changes in the offset from GMT and the daylight saving schedule, except that the
setStartYear
method can specify the year when the daylight saving time schedule starts in effect.
This example demonstrates SimpleTimeZone and how to schedule daylight saving time.
import java.util.SimpleTimeZone; import java.util.Calendar; import java.util.GregorianCalendar; public class SampleClass{ public static void main(String[] args){ SimpleTimeZone stz = new SimpleTimeZone(1*60*60*1000, "Europe/Paris", Calendar.APRIL, 1, -Calendar.SUNDAY, 3600000, SimpleTimeZone.UTC_TIME, Calendar.OCTOBER, -1, Calendar.SUNDAY, 3600000, SimpleTimeZone.UTC_TIME, 3600000); stz.setStartYear(2021); System.out.println("Is SimpleTimeZone instance DST? " + stz.observesDaylightTime()); System.out.println("Daylight Savings: " + stz.getDSTSavings()); System.out.println("Raw Offset: " + stz.getRawOffset()); GregorianCalendar gc = new GregorianCalendar( 2021, Calendar.APRIL, 4, 3, 0); gc.setTimeZone(stz); System.out.println("Is April 4, 2021 03:00 in DST? "+ stz.inDaylightTime(gc.getTime())); System.out.println(); System.out.println("Get time zone of gc"); System.out.println("Time zone ID: "+ gc.getTimeZone().getID()); } } Result Is SimpleTimeZone instance DST? true Daylight Savings: 3600000 Raw Offset: 3600000 Is April 4, 2021 03:00 in DST? true Get time zone of gc Time zone ID: Europe/ParisFirst off, let's examine this SimpleTimeZone constructor:
SimpleTimeZone(int rawOffset, String ID, int startMonth, int startDay, int startDayOfWeek, int startTime, int startTimeMode, int endMonth, int endDay, int endDayOfWeek, int endTime, int endTimeMode, int dstSavings)
rawOffset
is the offset from UTC/GMT time. ID
is the ID name that we want to assign to our time zone.
startMonth
, startDay
, startDayOfWeek
, startTime
and startTimeMode
are parameters for the start-rule of our scheduled daylight saving time.
endMonth
, endDay
, endDayOfWeek
, endTime
and endTimeMode
are parameters for the end-rule of our scheduled daylight saving time.
startTime
is the time where our scheduled daylight saving time starts. endTime
is the time where our scheduled daylight saving time ends.
dstSavings
is the saved daylight time per day in daylight saving time.
Month, day and day-of-week have different combinations that have different effects in the start-rule and end-rule of daylight saving time. You can read the combinations in the documentation.
In the example above, this combination in the start-rule:
Calendar.APRIL, 1, -Calendar.SUNDAY
means first Sunday in April. Therefore, the daylight saving time of
stz
starts on the first Sunday in April. Next, this combination in the end-rule:Calendar.OCTOBER, -1, Calendar.SUNDAY
means last Sunday in October. Therefore, the daylight saving time of stz
ends on the last Sunday in October.
Next, let's talk about time mode.
startTimeMode
is the time mode for the start-rule. endTimeMode
is the time mode for the end-rule. There are three time modes: WALL_TIME
, STANDARD_TIME
and UTC_TIME
. startTimeMode
and endTimeMode
are applied to the startTime
and endTime
, respectively.
WALL_TIME
represents local time with daylight time applied to the end-rule.STANDARD_TIME
represents local time without daylight time applied to start-rule and end-rule.UTC_TIME
represents Universal Time(UTC+-00:00) and doesn't apply daylight time to start-rule and end-rule.
For example, In the example above, the local time is UTC+01:00(1*60*60*1000 or 3600000 millis or one hour offset). Then, the start-rule of SimpleTimeZone instance in the example above is set to first sunday of April 2021. starting time is 3600000 millis or 1:00am in UTC time(24-hour format).
The returned value of
inDaylightTime
method is true. This means that April 4, 2021 03:00 is in the daylight saving time of stz
. Make sure that gc
Calendar time zone is stz
. Otherwise, you may get a different result. Use setTimeZone
method to set time zone to a Calendar instance, GregorianCalendar in this case.
setStartYear
sets the daylight saving time starting year. inDaylightTime
returns true if the given date and time is in the daylight saving time(DST) of the SimpleTimeZone that invokes the method. Otherwise, returns false. Now, let's discuss why April 4, 2021 03:00 is in the daylight saving time of stz
.
First off, Start time is 1:00am(3600000 millis) in UTC time. Then, we convert that start time to our local time which is UTC+1(UTC+01:00). Thus, the starting time in UTC+1 is 2:00am. Then, we move the time forward by an hour because we assign 1 hour or 3600000 millis to
dstSavings
parameter. Now, DST starts at sunday, April 4, 2021 03:00am in UTC+1 DST.
Now, for the end-rule, end time is 1:00am(3600000 millis) in UTC time. Then, we convert that end time to our local time which is UTC+1(UTC+01:00). Thus, the end time in UTC+1 is 2:00am. Then, we move the time backward by an hour because we assign 1 hour or 3600000 millis to
dstSavings
parameter. Now, DST ends at sunday, October 31, 2021 01:00am in UTC+1 time.
If we change the
endTimeMode
to STANDARD_TIME
, DST ends at sunday, October 31, 2021 00:00am in UTC+1 time. The result is different because we use a different type of time mode. STANDARD_TIME
uses the given local time which is UTC+1 in this case. That's why ending time is 01:00am UTC+1 standard time, move backward by an hour and DST ends at 00:00am.
If we change the
endTimeMode
to WALL_TIME
, DST ends at saturday, October 30, 2021 23:00pm in UTC+1 time. The result is different because we use a different type of time mode. WALL_TIME
when applied to end-rule, uses local time with daylight time adjustment.
That's why ending time is 01:00am UTC+1 DST, convert ending time back to standard UTC+1 which 00:00am UTC+1, move time backward by an hour and DST ends at 23:00pm.
Now, to test if our DST schedule is working, take a look at this example.
import java.util.SimpleTimeZone; import java.util.TimeZone; import java.util.Calendar; import java.util.GregorianCalendar; public class SampleClass{ public static void main(String[] args){ SimpleTimeZone stz = new SimpleTimeZone(1*60*60*1000, "Europe/Paris", Calendar.APRIL, 1, -Calendar.SUNDAY, 3600000, SimpleTimeZone.UTC_TIME, Calendar.OCTOBER, -1, Calendar.SUNDAY, 3600000, SimpleTimeZone.UTC_TIME, 3600000); stz.setStartYear(2021); GregorianCalendar gc = new GregorianCalendar( 2021, Calendar.APRIL, 4, 1, 50); gc.setTimeZone(stz); TimeZone.setDefault(stz); System.out.println(gc.getTime()); gc.add(Calendar.MINUTE, 30); System.out.println(gc.getTime()); } } Result Sun Apr 4 01:50:00 CET 2021 Sun Apr 4 03:20:00 CEST 2021In the example above, we see that once the time reached 2:00am, an hour was added to the time and the timezone changed from CET(Central European Time) to CEST(Central European Summer Time).
Don't forget to set your program's default timezone to the timezone we created. Use
TimeZone.setDefault
method to set our program's default timezone. This ensures that other date-time related classes like java.util.Date
follow the timezone we created.
If you want to use
SimpleTimeZone
rules in ZonedDateTime
in java.time
package, we can convert SimpleTimeZone
to ZoneId
by calling toZoneId()
method.
Aside from the SimpleTimeZone constructor that I demonstrated in the example above, SimpleTimeZone has three more constructors. These constructors:
SimpleTimeZone(int rawOffset, String ID, int startMonth, int startDay, int startDayOfWeek, int startTime, int endMonth, int endDay, int endDayOfWeek, int endTime)
SimpleTimeZone(int rawOffset, String ID, int startMonth, int startDay, int startDayOfWeek, int startTime, int endMonth, int endDay, int endDayOfWeek, int endTime, int dstSavings)
Uses
WALL_TIME
as default time mode for startTime
and endTime
.
This constructor:
SimpleTimeZone(int rawOffset, String ID)
Doesn't have daylight time schedule. However, we can use
setStartRule
and setEndRule
methods to set a start-rule and end-rule if we want to assign new DST schedule to SimpleTimeZone instance.
Month, day and day-of-week combinations that I explained above is applicable here. Take note that these methods(and their overloaded forms) use
WALL_TIME
as default time mode.
Next, let's discuss the methods that I used in the example above.
observesDaylightTime
returns true if a TimeZone instance observes DST. Otherwise, returns false. getRawOffset
returns the offset of a time zone from UTC. This method doesn't include the effect of DST.
If you want to get offset from UTC with DST adjustment, use
getOffset
method. To set raw offset, use setRawOffset
method. More information can be found in the documentation.
java.time Package
The classes defined in
java.time
package represent the principle date-time concepts, including instants, durations, dates, times, time-zones and periods. They are based on the ISO calendar system, which is the de facto world calendar following the proleptic Gregorian rules. All the classes are immutable and thread-safe.
This API is new and improved alternative to old date-and-time-related classes like
java.util.Date
. Since java 8, this API is recommended to be used for handling date and time.
In this topic, I'm gonna be discussing classes that are commonly used and follow ISO-8601 calendar system such as
LocalDate
, LocalTime
, LocalDateTime
and ZonedDateTime
. If you want to use a calendar other than ISO calendar system, you should read the classes and documentation in java.time.chrono package.
Classes like Month, DayOfWeek, Year, YearMonth and MonthDay are also classes that follow ISO-8601 although they store single-value or partial date/time information compared to the classes mentioned above.
All the instances of the classes mentioned above are called temporal objects because those classes implement Temporal or its superinterface which is the TemporalAccessor. Also, the classes mentioned above are immutable and thread-safe.
If you want to extend this API like adding different computations of date and time, you should read the classes and documentation in java.time.temporal package.
To quantify date, use Period class. To quantify time, use Duration class.
Instant, LocalDate, LocalTime, LocalDateTime, OffsetTime, OffsetDateTime and ZonedDateTime Classes
We can access and obtain date and time from these classes: Instant, LocalDate, LocalTime, OffsetTime, LocalDateTime, OffsetDateTime and ZonedDateTime.
To format the values of these date-related classes, use DateTimeFormatter. I already discuss this class in this blogpost.
Getting Date/Time Information
To get the time in our system clock, we invoke
now
method. Take a look at this example.
import java.time.*; public class SampleClass{ public static void main(String[] args){ Instant instant = Instant.now(); System.out.println("Instant: " + instant); LocalDate locDate = LocalDate.now(); System.out.println("Local Date: " + locDate); LocalTime locTime = LocalTime.now(); System.out.println("Local Time: " + locTime); OffsetTime offsetTime = OffsetTime.now(); System.out.println("Offset Time: " + offsetTime); LocalDateTime ldt = LocalDateTime.now(); System.out.println("Local Date Time: " + ldt); OffsetDateTime odt = OffsetDateTime.now(); System.out.println("Offset Date Time: " + odt); ZonedDateTime zdt = ZonedDateTime.now(); System.out.println("Zoned Date Time: " + zdt); } } Result(may vary) Instant: 2022-04-04T22:14:03.767119900Z Local Date: 2022-04-05 Local Time: 06:41:03.792119900 Offset Time: 06:41:03.792119900+08:00 Local Date Time: 2022-04-05T06:41:03.792119900 Offset Date Time: 2022-04-05T06:41:03.792119900+08:00 Zone Date Time: 2022-04-05T06:41:03.792119900+08:00[Asia/Taipei]
Instant
stores date and time(UTC+0). "T"
stands for time and it separates date and time. "Z"
stands for "Zulu" time which is another term for UTC time. LocalDate
stores date without time. LocalTime
stores time without date. OffsetTime
stores time with offset from UTC.
LocalDateTime
stores date and time. Time is adjusted based on the offset where the time is extracted. OffsetDateTime
stores date and time with the offset from UTC. ZonedDateTime
stores date and time with offset from UTC and ID of a time zone. Also, ZonedDateTime
observes daylight saving time based on the rules of its ZoneRules that resides in its ZoneId.
Where possible, applications should use
LocalDate
, LocalTime
and LocalDateTime
to better model the domain. For example, a birthday should be stored in a code LocalDate
. Bear in mind that any use of a time-zone, such as 'Europe/Paris', adds considerable complexity to a calculation.
Many applications can be written only using LocalDate, LocalTime and Instant, with the time-zone added at the user interface (UI) layer. For example, you get your system's date/time using
ZonedDateTime
. Then, display your system's time zone ID by calling getZone()
which returns a ZoneId instance. If you wanna get offset only, call getOffset()
which returns a ZoneOffset instance.
Then, call
toLocalDate()
that returns a LocalDate
instance with the date of our ZonedDateTime
instance. With this, we can compare dates easily without including time.
The offset-based date-time types OffsetTime and OffsetDateTime, are intended primarily for use with network protocols and database access. For example, most databases cannot automatically store a time-zone like 'Europe/Paris', but they can store an offset like '+02:00'.
Next, let's discuss how to get specific part of a date like month, day, year etc. This example demonstrates getter methods that get specific part of a date.
import java.time.*; import java.time.temporal.ChronoField; public class SampleClass{ public static void main(String[] args){ ZonedDateTime zdt = ZonedDateTime.now(); LocalDate ld = zdt.toLocalDate(); //These getter methods can be found in //LocalDate, LocalDateTime, OffsetDateTime //and ZonedDateTime System.out.println("Day of Month: " + ld.getDayOfMonth()); System.out.println("Day of Week: " + ld.getDayOfWeek()); System.out.println("Day of Year: " + ld.getDayOfYear()); System.out.println("Month: " + ld.getMonth() + " | " + ld.getMonthValue()); System.out.println("Year: " + ld.getYear()); System.out.println(); //These getter methods can be found in //LocalTime, LocalDateTime, OffsetTime, //OffsetDateTime and ZonedDateTime LocalTime lt = zdt.toLocalTime(); System.out.println("Hour: " + lt.getHour()); System.out.println("Minutes: " + lt.getMinute()); System.out.println("Seconds: " + lt.getSecond()); System.out.println("Nano-of-Second: " + lt.getNano()); System.out.println(); //This getter method can be found in //OffsetTime, OffsetDateTime and //ZonedDateTime System.out.println("Offset: " + zdt.getOffset()); //This getter method can be found in //ZonedDateTime System.out.println("Zone ID: " + zdt.getZone()); System.out.println(); //These getter methods can be found in //LocalDate System.out.println("Length of Month: "+ ld.lengthOfMonth()); System.out.println("Length of Year: "+ ld.lengthOfYear()); System.out.println("Is leap year? "+ ld.isLeapYear()); System.out.println("Era: "+ ld.getEra()); System.out.println(); //These getter methods can be found in //LocalTime System.out.println("Nanoseconds of Day: " + lt.toNanoOfDay()); System.out.println("Seconds of Day: " + lt.toSecondOfDay()); System.out.println(); //get(TemporalField field) method //can be found in every temporal //implementations highlighted in this topic if(zdt.isSupported(ChronoField.AMPM_OF_DAY)) System.out.println("AM/PM of day: " + zdt.get(ChronoField.AMPM_OF_DAY)); } } Result Day of Month: 7 Day of Week: THURSDAY Day of Year: 97 Month: APRIL | 4 Year: 2022 Hour: 10 Minutes: 42 Seconds: 31 Nano-of-Second: 432663400 Offset: +08:00 Zone ID: Asia/Taipei Length of Month: 30 Length of Year: 365 Is leap year? false Era: CE Nanoseconds of Day: 38551432663400 Seconds of Day: 38551 AM/PM of Day: 0
getDayOfMonth()
returns the day of the month, getDayOfWeek()
returns a DayOfWeek enum that stores the day of the week and getDayOfYear()
returns the day of the year.
getMonth()
returns a Month enum that denotes the month. getMonthValue()
returns the numerical representation of the month. In ISO calendar system, A value of 1 represents January. A value of 12 represents December. In the example above, the value 4 represents April. getYear()
returns the year.
getHour()
returns hour, getMinute()
returns minutes and getSecond()
returns seconds. getNano()
returns nano-of-second or a fraction of a second in nanoseconds. getOffset()
returns a ZoneOffset instance that stores the offset from UTC. getZone()
returns a ZoneID instance that stores the ID of the time zone.
lengthOfMonth()
returns the total number of days in a month. In the example above, the total number of days in March 2022 is 30. lengthOfYear()
returns the total number of days in a year. In the example above, the total number of days in 2022 is 365. isLeapYear()
returns true is a year is a leap year. Otherwise, returns false. getEra()
returns an IsoEra instance which holds the current era of the specified date.
toNanoOfDay()
returns the elapsed time in a day in nanoseconds. toSecondOfDay()
returns the elapsed time in a day in seconds. get(TemporalField field)
gets the value of the specified field from this date as an int.
In the example above,
ChronoField.AMPM_OF_DAY
holds a value that determines if this date and time is in AM or PM. If the field returns 0, date and time is AM. if 1, date and time is PM. This method will throw an exception if the given field is not supported by this instance or the value of the given field exceeds the max range of int. If the latter happened, use getLong(TemporalField field)
.
TemporalField is a field of date-time, such as month-of-year or minute-of-hour. ChronoField is an enum that contains a standard set of fields that are compatible with
TemporalField
. Other fields that are TemporalField
type can be found in IsoFields, WeekFields and JulianFields.
isSupported(TemporalField field)
returns true if the given field is supported by the class. In the example above, LocalDate
supports ChronoField.AMPM_OF_DAY
field that's why isSupported
returns true.
Setting Date/Time Information
There are different ways of setting date and time. However, one of the common ways of setting date and time is by using
of
method. Take a look at this example.
import java.time.*; public class SampleClass{ public static void main(String[] args){ //of(int year, int month, int dayOfMonth) LocalDate ld = LocalDate.of(2020, 5, 30); System.out.println("LocalDate: " + ld); //of(int hour, int minute) LocalTime lt = LocalTime.of(10, 45); System.out.println("LocalTime: " + lt); //of(LocalTime time, ZoneOffset offset) OffsetTime ot = OffsetTime.of(LocalTime.of(20, 30), ZoneOffset.of("+10:00")); System.out.println("OffsetTime: " + ot); //of(LocalDate date, LocalTime time) LocalDateTime ldt = LocalDateTime.of(LocalDate.of(2015, 1, 15), LocalTime.of(10, 10)); System.out.println("LocalDateTime: " + ldt); //of(LocalDateTime dateTime, ZoneOffset offset) OffsetDateTime odt = OffsetDateTime.of(ldt, ZoneOffset.of("+08:00")); System.out.println("OffsetDateTime: " + odt); //of(LocalDateTime localDateTime, ZoneId zone) ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.of("Asia/Taipei")); System.out.println("ZonedDateTime: " + zdt); } } Result LocalDate: 2020-05-30 LocalTime: 10:45 OffsetTime: 20:30+10:00 LocalDateTime: 2015-01-15T10:00+08:00 ZonedDateTime: 2015-01-15T10:00+08:00[Asia/Taipei]Take note that
of
method has overloaded forms and each overloaded form may be unique to each temporal implementation.
Parsing Date/Time
If we want to parse a text to a temporal object, we use
parse
method. This method can be seen from every temporal implementations highlighted in this topic. This method has two forms:parse(CharSequence text)
parse(CharSequence text, DateTimeFormatter formatter)
This example demonstrates
parse(CharSequence text)
import java.time.*; public class SampleClass{ public static void main(String[] args){ LocalDate ld = LocalDate.parse("2010-10-20"); System.out.println("LocalDate: " + ld); ZonedDateTime zdt = ZonedDateTime.parse( "2007-12-03T10:15:30+01:00[Europe/Paris]"); System.out.println("ZonedDateTime: " + zdt); } } Result LocalDate: 2010-10-20 ZonedDateTime: 2007-12-03T10:15:30+01:00[Europe/Paris]Take note that an ISO format must be followed when the first overloaded form of
parse
is used. For example, parse(CharSequence text) in LocalDate
follows DateTimeFormatter.ISO_LOCAL_DATE. in ZonedDateTime
, first overloaded form of parse follows DateTimeFormatter.ISO_ZONED_DATE_TIME. More information about formatting can be found in the DateTimeFormatter.
Adding/Subtracting Date/Time
To subtract or add date or time to a temporal implementations highligted in this topic, we use methods with minus or plus prefix. This example demonstrates some of those methods.
import java.time.*; public class SampleClass{ public static void main(String[] args){ LocalDate ld = LocalDate.of(2020, 5, 20); //These methods can be found in LocalDate, //LocalDateTime, OffsetDateTime and //ZonedDateTime ld = ld.plusDays(1); ld = ld.plusMonths(1); ld = ld.plusYears(1); System.out.println("Date(plus): " + ld); ld = ld.minusDays(1); ld = ld.minusMonths(1); ld = ld.minusYears(1); System.out.println("Date(minus): " + ld); LocalTime lt = LocalTime.of(10, 45, 15); //These methods can be found in LocalTime, //LocalDateTime, OffsetTime, OffsetDateTime //and ZonedDateTime lt = lt.plusHours(1); lt = lt.plusMinutes(5); lt = lt.plusSeconds(15); System.out.println("Time(plus): " + lt); lt = lt.minusHours(1); lt = lt.minusMinutes(5); lt = lt.minusSeconds(15); System.out.println("Time(minus): " + lt); } } Result Date(plus): 2021-06-21 Date(minus): 2020-05-20 Time(plus): 11:50:30 Time(minus): 10:45:15The methods demonstrated above are easy to understand. Now, let's discuss
plus
and minus
methods. These methods can found in every date-time-related classes highlighted in this topic and both of them have two overloaded forms:plus(long amountToAdd, TemporalUnit unit)
plus(TemporalAmount amountToAdd)
minus(long amountToSubtract, TemporalUnit unit)
minus(TemporalAmount amountToSubtract)
This example demonstrates
plus
and minus
methods.
import java.time.*; import java.time.temporal.ChronoUnit; public class SampleClass{ public static void main(String[] args){ LocalDate ld = LocalDate.of(2020, 5, 20); //plus(long amountToAdd, TemporalUnit unit) if(ld.isSupported(ChronoUnit.DAYS)) ld = ld.plus(5, ChronoUnit.DAYS); else System.out.println("DAYS not supported!"); //plus(TemporalAmount amountToAdd) ld = ld.plus(Period.ofYears(1)); System.out.println("LocalDate: " + ld); LocalTime lt = LocalTime.of(16, 30); //minus(long amountToSubtract, TemporalUnit unit) if(lt.isSupported(ChronoUnit.MINUTES)) lt = lt.minus(15, ChronoUnit.MINUTES); //minus(TemporalAmount amountToSubtract) lt = lt.minus(Duration.ofHours(1)); System.out.println("LocalTime: " + lt); } } Result LocalDate: 2021-05-25 LocalTime: 15:15TemporalUnit A unit of date-time, such as Days or Hours. ChronoUnit is an enum that contains a standard set of fields that are compatible with
TemporalUnit
. Other fields that are TemporalUnit
type can be found in IsoFields.
isSupported(TemporalUnit unit)
returns true if the given field is supported by the class. In the example above, LocalDate
supports ChronoUnit.DAYS
field that's why isSupported
returns true.
TemporalAmount is a framework-level interface defining an amount of time, such as "6 hours", "8 days" or "2 years and 3 months". This interface has two concrete implementations: Duration and Period.
Period is just like
LocalDate
. However, Period
follows a different format and can be used to store single-value date information like months, years, days, etc.
Duration is just like
LocalTime
. However, Duration
follows a different format and can be used to store single-value time information like hours, minutes, seconds, etc.
Modifying Date/Time
To modify date and time in temporal objects, we use
with
or methods with 'with'
prefix. Take note that the methods throw an exception if the given value is not in the range of the specified field. This example demonstrates some of those methods.
import java.time.*; public class SampleClass{ public static void main(String[] args){ LocalDate ld = LocalDate.of(2015, 6, 15); //These methods can be found in LocalDate, //LocalDateTime, OffsetDateTime and //ZonedDateTime ld = ld.withDayOfMonth(5); ld = ld.withMonth(4); ld = ld.withYear(2020); System.out.println("LocalDate: " + ld); LocalTime lt = LocalTime.of(10, 15, 10); //These methods can be found in LocalTime, //OffsetTime, OffsetDateTime and //ZonedDateTime lt = lt.withHour(2); lt = lt.withMinute(5); lt = lt.withSecond(15); System.out.println("LocalTime: " + lt); System.out.println(); OffsetTime ot = OffsetTime.of(lt, ZoneOffset.of("+02:00")); System.out.println("Original: " + ot); //These methods can be found in OffsetTime, //OffsetDateTime ot = ot.withOffsetSameInstant( ZoneOffset.of("+03:00")); System.out.println("withOffsetSameInstant: " + ot); ot = ot.withOffsetSameLocal( ZoneOffset.of("+04:00")); System.out.println("withOffsetSameLocal: " + ot); System.out.println(); ZonedDateTime zdt = ZonedDateTime.of( ld, lt, ZoneId.of("Europe/Paris")); System.out.println("Original: " + zdt); //These methods can be found in //ZonedDateTime zdt = zdt.withZoneSameInstant(ZoneId.of("Asia/Taipei")); System.out.println("withZoneSameInstant: " + zdt); zdt = zdt.withZoneSameLocal(ZoneId.of("Europe/Paris")); System.out.println("withZoneSameLocal: " + zdt); } } Result LocalDate: 2020-04-05 LocalTime: 02:05:15 Original: 02:05:15+02:00 withZoneSameInstant 03:03:15+03:00 03:03:15+04:00 Original: 2020-04-05T02:05:15+02:00[Europe/Paris] withZoneSameInstant 2020-04-05T02:08:15+08:00[Asia/Taipei] withZoneSameLocal 2020-04-05T02:08:15+02:00[Europe/Paris]
withDayOfMonth
, withMonth
and withYear
create new instances with the given day of month, month and year. withHour
, withMinute
and withSecond
create new instances with the given hour, minutes and seconds.
withOffsetSameInstant
creates a new instance with the given offset. Time will adjust based on the previous and new offset. withOffsetSameLocal
creates a new instance with the given offset. The time won't be adjusted.
withZoneSameInstant
creates a new instance with the given ZoneId
and its offset. Time will adjust based on the rules and offset of the previous ZoneId to rules and offset of the new ZoneId. withZoneSameLocal
creates a new instance with the given ZoneId
and its offset from UTC. Time won't be adjusted.
Take note that
ZoneId
has rules that are defined by ZoneRules. ZonedDateTime
follows the rules of ZoneId
. "Europe/Paris" ZoneId has rules that follow daylight saving time. Thus, any ZonedDateTime
that uses "Europe/Paris" Zone ID observes daylight saving time.
There are three methods with 'with' prefix that I wanna discuss.
withEarlierOffsetAtOverlap
, withLaterOffsetAtOverlap
and withFixedOffsetZone
can be found in ZonedDateTime
. Take a look at this example.
import java.time.*; public class SampleClass{ public static void main(String[] args){ ZonedDateTime zdt = ZonedDateTime.of( LocalDateTime.of(2022, 10, 30, 2, 0), ZoneId.of("Europe/Paris")); ZonedDateTime fixedOffsetZone = zdt.withFixedOffsetZone(); fixedOffsetZone = fixedOffsetZone.plusHours(1); System.out.println("Fixed: " + fixedOffsetZone); System.out.println(); System.out.println("Original: " + zdt); zdt = zdt.plusHours(1); System.out.println("Plus 1hr: " + zdt); System.out.println(); zdt = zdt.withEarlierOffsetAtOverlap(); System.out.println("Early Overlap: " + zdt); zdt = zdt.withLaterOffsetAtOverlap(); System.out.println("Later Overlap: " + zdt); } } Result Fixed: 2022-10-30T03:00+02:00 Original: 2022-10-30T02:00+02:00[Europe/Paris] Plus 1hr: 2022-10-30T02:00+01:00[Europe/Paris] Early Overlap: 2022-10-30T02:00+02:00[Europe/Paris] Later Overlap: 2022-10-30T02:00+01:00[Europe/Paris]In the example above, we can see that the time in
fixedOffsetZone
variable didn't adjust because there's no ZoneId
is present in the instance. Thus, no daylight rules to follow. withFixedOffsetZone
discards the ZoneId
of a ZonedDateTime
instance. Making it effectively equivalent to OffsetDateTime
.
withLaterOffsetAtOverlap
returns a ZonedDateTime
with the later valid offset of the ZonedDateTime
that invoked this method. withEarlierOffsetAtOverlap
returns a ZonedDateTime
with the earlier valid offset of the ZonedDateTime
that invoked this method.
Overlap happens during transition from daylight saving time to standard time. Gap happens during transition from standard time to daylight saving time. This documentation has a definition about gaps and overlaps.
Next, let's discuss
with
method. This method can be found in every temporal implementations highlighted in this topic and this method has two forms:with(TemporalAdjuster adjuster)
with(TemporalField field, long newValue)
This example demonstrates
with
method and its overloaded forms.
import java.time.*; import java.time.temporal.TemporalAdjusters; import java.time.temporal.ChronoField; public class SampleClass{ public static void main(String[] args){ LocalDate ld = LocalDate.of(2020, 5, 20); //with(TemporalField field, long newValue) if(ld.isSupported(ChronoField.YEAR)) ld.with(ChronoField.YEAR, 2025); //with(TemporalAdjuster adjuster) ld = ld.with(temporal -> temporal.plus(Period.ofMonths(2))); ld = ld.with(TemporalAdjusters.lastDayOfMonth()); System.out.println("LocalDate: " + ld); } } Result LocalDate: 2020-07-31TemporalAdjuster is a functional interface used for adjusting a temporal object. You can create your own lambda functions that return Temporal or use the pre-defined functions from TemporalAdjusters.
TemporalField is a field of date-time, such as month-of-year or minute-of-hour. ChronoField is an enum that contains a standard set of fields that are compatible with
TemporalField
. Other fields that are TemporalField
type can be found in IsoFields, WeekFields and JulianFields.
Comparing Date/Time
Use
equals
method to compare date and time of two temporal objects with the same type. This method can be found in every temporal implementations highlighted in this topic. Take a look at this example.
import java.time.*; public class SampleClass{ public static void main(String[] args){ LocalDate ld1 = LocalDate.of(2020, 5, 20); LocalDate ld2 = LocalDate.of(2020, 5, 20); LocalDate ld3 = LocalDate.of(2015, 5, 20); System.out.println("ld1 is equal to ld2? " + ld1.equals(ld2)); System.out.println("ld1 is equal to ld3? " + ld1.equals(ld3)); } } Result ld1 is equal to ld2? true ld1 is equal to ld3? falsetake note that
equals
method works differently in every temporal objects. For example, in LocalDate
, only date is being compared. In LocalTime
, only time is compared. In LocalDateTime
, date and time are used for comparing two instances. In ZonedDateTime
, date, time and ZoneId
are used for comparing two instances.
Querying Date/Time
a temporal object can be queried to extract information from it. To do that, we use the
query
method which can be found in every temporal implementations highlighted in this topic. Take a look at this example.
import java.time.*; import java.time.temporal.TemporalQueries; import java.time.chrono.Chronology; import java.time.chrono.IsoChronology; public class SampleClass{ public static void main(String[] args){ ZonedDateTime zdt = ZonedDateTime.of( LocalDate.of(10, 5, 20), LocalTime.of(10, 15), ZoneId.of("Europe/Paris")); Chronology ch = zdt.query(TemporalQueries.chronology()); System.out.println("Chronology: " + ch); boolean isChrono = ch instanceof IsoChronology; System.out.println("is IsoChronology? " + isChrono); //Quarter Year: standard calendar divides //months in a year in a quarter: Q1, Q2, Q3, Q4 //January, February, and March (Q1) April, //May, and June (Q2) //July, August, and September (Q3) //October, November and December (Q4) int quarterYear = zdt.query(temporal -> { LocalDate ld = LocalDate.from(temporal); return (ld.getMonthValue() / 3) + 1; }); System.out.println("Quarter Year: " + quarterYear); } } Result Chronology: ISO is IsoChronology? true Quarter Year: 2TemporalQuery is a functional interface used for querying a temporal object. We can create our own lambda functions for
TemporalQuery
. Additionally, we can use pre-defined functions in TemporalQueries.
Chronology is an interface that defines a calendar system, used to organize and identify dates. Java supports different types of calendars and those calendars implement this interface. Most of the time, the default calendar system used by java platforms is the ISO calendar system. IsoChronology defines the rules of the ISO calendar system.
LocalDate.from
extracts date-related information from the given temporal object and returns LocalDate
instance with the extracted information.
Duration and Period Classes
Duration and Period implement TemporalAmount that is used as a parameter of methods such as
plus
, minus
and with
.
Duration
class models a quantity or amount of time in terms of seconds and nanoseconds. Period
class models a quantity or amount of time in terms of years, months and days. This example demonstrates some methods of Duration
and Period
.
import java.time.*; public class SampleClass{ public static void main(String[] args){ Duration hours = Duration.parse("P2DT10H10M5S"); Duration minutes = Duration.ofMinutes(30); Duration seconds = Duration.ofSeconds(15); hours = hours.plusMinutes(30); System.out.println("Duration1: " + hours); minutes = minutes.plusMinutes(15); System.out.println("Duration2: " + minutes); seconds = seconds.plusSeconds(15); System.out.println("Duration3: " + seconds); System.out.println(); System.out.println("toHoursPart: " + hours.toHoursPart()); System.out.println("toMinutesPart: " + hours.toMinutesPart()); System.out.println("toMinutes: " + hours.toMinutes()); System.out.println(); Period date = Period.parse("P2022Y5M15D"); Period months = Period.ofMonths(5); Period years = Period.ofYears(5); System.out.println("Period1: " + date); System.out.println("Period2: " + months); System.out.println("Period3: " + years); System.out.println(); System.out.println("getYears: " + date.getYears()); System.out.println("getMonths: " + date.getMonths()); System.out.println("getDays: " + date.getDays()); System.out.println(); Period date2 = Period.parse("P2022Y15M10D"); System.out.println("Not normalized: " + date2); date2 = date2.normalized(); System.out.println("Normalized: " + date2); } } Result Duration1: PT58H40M5S Duration2: PT45M Duration3: PT30S toHoursPart: 10 toMinutesPart: 40 toMinutes: 3520 Period1: 2022Y5M15D Period2: P5M Period3: P5Y getYears: 2022 getMonths: 5 getDays: 15 Not normalized: P2022Y15M10D Normalized: P2023Y3M10DIf you read several topics before this topic, you will notice that
Duration
and Period
have similarities with the classes I've discussed previously. Duration.ofMinutes
creates a Duration
instance with the given minutes. Duration.ofSeconds
creates a Duration
instance with the given seconds. Duration.parse
obtains a Duration
from a text that follows particular textual formats such as PnDTnHnMn.nS
. More information about Duration.parse
formats can be found here.
Notice that the format
P2022Y5M15D
I put in Duration.parse
is not equivalent to the output of System.out.println("Duration1: " + hours);
. That's because Duration.toString
formats the output String
into particular formats such as PT8H6M12.345S
. More information about Duration.toString
formats can be found here.
toHoursPart
method extracts the hours part in a Duration
object. toMinutesPart
method extracts the minutes part in a Duration
object.toMinutes
gets the number of minutes in this Duration
object. plusMinutes
creates a Duration
object with the added minutes. plusSeconds
creates a Duration
object with the added seconds.
Period.ofMonths
creates a Period
instance with the given months. Period.ofYears
creates a Period
instance with the given seconds. Period.getYears
returns the extracted years from the Period
instance. Period.getMonths
returns the extracted months from the Period
instance. Period.getDays
returns the extracted days from the Period
instance.
Period.parse
obtains a Period
from a text that follows particular textual formats such as PnYnMnD
. More information about Period.parse
formats can be found here.
Period.normalized
method returns a copy of this Period
object with the years and months normalized. This normalizes the years and months units, leaving the days unit unchanged. The months unit is adjusted to have an absolute value less than 12, with the years unit being adjusted to compensate. For example, a period of "1 Year and 15 months" will be normalized to "2 years and 3 months".
No comments:
Post a Comment