Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Post History
There are many ways to do it, it depends on what data you already have and/or the Java version. I have only the year's numeric value If you already have a value as a number (int or long), and i...
Answer
#6: Post edited
- There are many ways to do it, it depends on what data you already have and/or the Java version.
- ---
- # I have only the year's numeric value
- If you already have a value as a number (`int` or `long`), and **is using Java >= 8**, you can use the [`java.time.Year` class](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html), which has the [static method `isLeap`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html#isLeap(long)). This is the "faster" and more straighforward, no need to create instances of any object, it's just a static method call:
- ```java
- int year = 2000;
- System.out.println(Year.isLeap(year)); // true
- ```
- For **Java <= 7**, use [`Calendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Calendar.html). Unfortunately it's not as straightforward as Java 8's solution, you have to create an instance and then check how many days the year has:
- ```java
- int year = 2000;
- Calendar cal = Calendar.getInstance();
- cal.set(Calendar.YEAR, year);
- // the year is leap if it has more than 365 days
- System.out.println(cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // true
- ```
- Another alternative is to create an instance of [`GregorianCalendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html) and use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html#isLeapYear(int)):
- ```java
- int year = 2000;
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- ---
- Both `Calendar` and `GregorianCalendar` use the [Gregorian Calendar's rules](https://en.wikipedia.org/wiki/Gregorian_calendar): if the year is divisible by 100, it's leap only if it's also divisible by 400. If it's not divisible by 100, it's leap if it's divisible by 4. You _could_ implement this rule by yourself:
- ```java
- public static boolean isLeap(int ano) {
- return (ano % 4 == 0) && (ano % 100 != 0 || ano % 400 == 0);
- }
- ```
- But it already exists in the native API, so I don't see a reason to reinvent the wheel.
**But beware**: `GregorianCalendar` has a "cut-off date", which is the year 1582 (when the Gregorian Calendar was introduced) - to be more precise, the date is `1582-10-15T00:00:00Z`: October 15th, 1582, at midnight, in [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time). This means that, for any dates before that, `GregorianCalendar` uses the [Julian Calendar's rules](https://en.wikipedia.org/wiki/Julian_calendar), in which any year divisible by 4 is leap.- Example: according to `GregorianCalendar`, 1900 isn't a leap year, because it's greater than 1582 and if follows the current rule (it's divisible by 100, but no by 400), but 1500 is leap because it's before 1582, so the divisible-by-400 rule doesn't apply.
- But you can change this behaviour by setting a different cut-off date:
- ```java
- int year = 1500;
- GregorianCalendar cal = new GregorianCalendar();
- // 1500 is before the cut-off date (1582) and uses the "old" rule (divisible by 4 == leap)
- System.out.println(cal.isLeapYear(year)); // true
- cal.setGregorianChange(new Date(-62135758799190L)); // change cut-off date to January 1st, 0001
- // 1500 isn't leap anymore (uses the current rule: if divisible by 100, it's leap only if it's also divisible by 400)
- System.out.println(cal.isLeapYear(year)); // false
- ```
- Another detail: only `GregorianCalendar` does that. But `Calendar.getInstance()` **might or might not** do it, because the `getInstance` method can return either a `GregorianCalendar`, or some other sub-classes, depending on the default locale configured in the JVM. For example: if the default locale is `th_TH` (Thailand), `getInstance` returns a `BuddhistCalendar`, and for that calendar, 1500 isn't a leap year:
- ```java
- Locale.setDefault(new Locale("th", "TH"));
- int year = 2000;
- // for locale th_TH, it doesn't create a GregorianCalendar
- Calendar cal2 = Calendar.getInstance();
- System.out.println(cal2.getClass()); // class sun.util.BuddhistCalendar
- cal2.set(Calendar.YEAR, year);
- System.out.println(cal2.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // false
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- Of course you could force it to create a `GregorianCalendar`, by using a specific locale (such as `Calendar.getInstance(new Locale("pt", "BR"))` - for most locales, it returns a `GregorianCalendar`).
- On the other hand, `Year.isLeap` can't be configured (there's no such thing as changing the cut-off date), so it will always say that 1500 isn't leap.
- That's what you'd use for any numeric value. If you want the **current** year, though, you could use `Year.now().isLeap()` for Java >= 8 (in that case, we can't avoid the creation of an instance). And for `Calendar`, simply don't set the year (don't call `set(Calendar.YEAR, ano)`), as `getInstance()` already returns the current date.
- ---
- # What if I have an object that represents a date?
- ### Java <= 7
- If you have a `Calendar` instance, use the methods above.
- if you have a `java.util.Date`, **don't** use [`getYear()`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Date.html#getYear()), for two reasons:
- - it returns a value that is the result of subtracting 1900 from the year. Therefore, if the current year is 2023, `new Date().getYear()` returns `123`, and to check if it's leap, you have to remember to add 1900
- - it's deprecated since Java 1.1 and the documentation recommends using `Calendar`
- Therefore, if you have a `Date` instance, do like this:
- ```java
- Date date = // some Date instance
- // create a Calendar and set the Date
- Calendar cal = Calendar.getInstance();
- cal.setTime(date);
- // use the Calendar's methods already explained above
- ```
- And once again, if you want the current date, don't use `Date`, just `Calendar.getInstance()`.
- ### Java >= 8 (`java.time`)
- For `LocalDate`, just use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/LocalDate.html#isLeapYear()):
- ```java
- LocalDate date = // some LocalDate instance
- System.out.println(date.isLeapYear());
- ```
- For other `java.time` types, you can get the year numeric value and pass it to `Year.isLeap`, or get a `LocalDate` if applicable (or get a `Year` instance from the object). Example:
- ```java
- LocalDateTime dt = // some LocalDateTime instance
- // get the year numeric value and pass it to Year.isLeap
- System.out.println(Year.isLeap(dt.getYear()));
- // or get a LocalDate instance
- System.out.println(dt.toLocalDate().isLeapYear());
- // or get a Year instance
- System.out.println(Year.from(dt).isLeap());
- ```
- The first one (`getYear`) is more straightforward and - IMO - simpler. All API native date/time types have this getter (except, of course, the ones that don't make sense to have a year: `LocalTime`, `OffsetTime`, `MonthDay`, `Instant`, `Month` and `DayOfWeek`).
- The second one (`dt.toLocalDate().isLeapYear()`) might seem "bad" because it creates a `LocalDate` instance, but in the current implementation, `LocalDateTime` uses composition and encapsulates an instance of `LocalDate`, which is returned by `toLocalDate`. Anyway, it's an implementation detail: don't blindly rely on it if you wish to minimize the creation of new instances.
- The third one (`Year.from`) is preffered if you're working with a `TemporalAccessor` (an interface implemented by all API date/time types), without caring too much about the actual type: if it has a year field, that's all that matters. With this, you could create a method that works with any date/time type (and any other that you create, as long as it implements `TemporalAccessor`):
- ```java
- static boolean isLeapYear(TemporalAccessor t) {
- return Year.from(t).isLeap();
- }
- ...
- // it works with any type that implements TemporalAccessor and has a year field
- System.out.println(isLeapYear(LocalDate.now()));
- System.out.println(isLeapYear(LocalDateTime.now()));
- String string = "10/01/2023"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- // I don't need to create a LocalDate instance, just pass the result of parse (which is a TemporalAccessor) directly to the method
- System.out.println(isLeapYear(fmt.parse(string)));
- ```
- As a side note, in this case, you could also create a `TemporalQuery` and pass it directly to the `parse` method:
- ```java
- TemporalQuery<Boolean> isLeapYear = (t) -> Year.from(t).isLeap();
- String string = "10/01/2020"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- System.out.println(fmt.parse(string, isLeapYear)); // true
- ```
- ---
- ## Other calendars
- All the rules described above are valid for the ISO 8601 calendar. For short, it's like applying the Gregorian Calendar rules retroactively, for dates before October 1582 (the "cut-off date", as we saw above). That's the default calendar used by [`java.time` classes](https://docs.oracle.com/javase/tutorial/datetime/iso/overview.html).
- But the `java.time` API also supports another calendars: the implementations can be found in the [`java.time.chrono` package](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/chrono/package-summary.html). For example, the class `ThaiBuddhistChronology` (implementation of [Buddhist Calendar](https://en.wikipedia.org/wiki/Buddhist_calendar)) has a different rule for leap years:
- ```java
- int year = 2020;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // true
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // false
- year = 2543;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // false
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // true
- ```
- Anyway, for other calendar systems, just use the respective class in `java.time.chrono`. Or, if you need a different one (and don't want to implement them), you can use external libraries. Two alternatives are the [ThreeTen Extra project](https://www.threeten.org/threeten-extra/apidocs/org.threeten.extra/org/threeten/extra/chrono/package-summary.html) (made by the same creator of `java.time`) and [Time4j](http://www.time4j.net/javadoc-en/): both provide nice support for other calendars.
- There are many ways to do it, it depends on what data you already have and/or the Java version.
- ---
- # I have only the year's numeric value
- If you already have a value as a number (`int` or `long`), and **is using Java >= 8**, you can use the [`java.time.Year` class](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html), which has the [static method `isLeap`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html#isLeap(long)). This is the "faster" and more straighforward, no need to create instances of any object, it's just a static method call:
- ```java
- int year = 2000;
- System.out.println(Year.isLeap(year)); // true
- ```
- For **Java <= 7**, use [`Calendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Calendar.html). Unfortunately it's not as straightforward as Java 8's solution, you have to create an instance and then check how many days the year has:
- ```java
- int year = 2000;
- Calendar cal = Calendar.getInstance();
- cal.set(Calendar.YEAR, year);
- // the year is leap if it has more than 365 days
- System.out.println(cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // true
- ```
- Another alternative is to create an instance of [`GregorianCalendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html) and use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html#isLeapYear(int)):
- ```java
- int year = 2000;
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- ---
- Both `Calendar` and `GregorianCalendar` use the [Gregorian Calendar's rules](https://en.wikipedia.org/wiki/Gregorian_calendar): if the year is divisible by 100, it's leap only if it's also divisible by 400. If it's not divisible by 100, it's leap if it's divisible by 4. You _could_ implement this rule by yourself:
- ```java
- public static boolean isLeap(int ano) {
- return (ano % 4 == 0) && (ano % 100 != 0 || ano % 400 == 0);
- }
- ```
- But it already exists in the native API, so I don't see a reason to reinvent the wheel.
- **But beware**: `GregorianCalendar` has a "cut-off date", which is the year 1582 (when the Gregorian Calendar was introduced) - to be more precise, the date is `1582-10-15T00:00:00Z`: October 15th, 1582, at midnight, in [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time) (note: [not all countries changed their calendars in 1582](https://en.wikipedia.org/wiki/Adoption_of_the_Gregorian_calendar), but that's the date configured in the class when an instance is created).
- This means that, for any dates before that, `GregorianCalendar` uses the [Julian Calendar's rules](https://en.wikipedia.org/wiki/Julian_calendar), in which any year divisible by 4 is leap.
- Example: according to `GregorianCalendar`, 1900 isn't a leap year, because it's greater than 1582 and if follows the current rule (it's divisible by 100, but no by 400), but 1500 is leap because it's before 1582, so the divisible-by-400 rule doesn't apply.
- But you can change this behaviour by setting a different cut-off date:
- ```java
- int year = 1500;
- GregorianCalendar cal = new GregorianCalendar();
- // 1500 is before the cut-off date (1582) and uses the "old" rule (divisible by 4 == leap)
- System.out.println(cal.isLeapYear(year)); // true
- cal.setGregorianChange(new Date(-62135758799190L)); // change cut-off date to January 1st, 0001
- // 1500 isn't leap anymore (uses the current rule: if divisible by 100, it's leap only if it's also divisible by 400)
- System.out.println(cal.isLeapYear(year)); // false
- ```
- Another detail: only `GregorianCalendar` does that. But `Calendar.getInstance()` **might or might not** do it, because the `getInstance` method can return either a `GregorianCalendar`, or some other sub-classes, depending on the default locale configured in the JVM. For example: if the default locale is `th_TH` (Thailand), `getInstance` returns a `BuddhistCalendar`, and for that calendar, 1500 isn't a leap year:
- ```java
- Locale.setDefault(new Locale("th", "TH"));
- int year = 2000;
- // for locale th_TH, it doesn't create a GregorianCalendar
- Calendar cal2 = Calendar.getInstance();
- System.out.println(cal2.getClass()); // class sun.util.BuddhistCalendar
- cal2.set(Calendar.YEAR, year);
- System.out.println(cal2.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // false
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- Of course you could force it to create a `GregorianCalendar`, by using a specific locale (such as `Calendar.getInstance(new Locale("pt", "BR"))` - for most locales, it returns a `GregorianCalendar`).
- On the other hand, `Year.isLeap` can't be configured (there's no such thing as changing the cut-off date), so it will always say that 1500 isn't leap.
- That's what you'd use for any numeric value. If you want the **current** year, though, you could use `Year.now().isLeap()` for Java >= 8 (in that case, we can't avoid the creation of an instance). And for `Calendar`, simply don't set the year (don't call `set(Calendar.YEAR, ano)`), as `getInstance()` already returns the current date.
- ---
- # What if I have an object that represents a date?
- ### Java <= 7
- If you have a `Calendar` instance, use the methods above.
- if you have a `java.util.Date`, **don't** use [`getYear()`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Date.html#getYear()), for two reasons:
- - it returns a value that is the result of subtracting 1900 from the year. Therefore, if the current year is 2023, `new Date().getYear()` returns `123`, and to check if it's leap, you have to remember to add 1900
- - it's deprecated since Java 1.1 and the documentation recommends using `Calendar`
- Therefore, if you have a `Date` instance, do like this:
- ```java
- Date date = // some Date instance
- // create a Calendar and set the Date
- Calendar cal = Calendar.getInstance();
- cal.setTime(date);
- // use the Calendar's methods already explained above
- ```
- And once again, if you want the current date, don't use `Date`, just `Calendar.getInstance()`.
- ### Java >= 8 (`java.time`)
- For `LocalDate`, just use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/LocalDate.html#isLeapYear()):
- ```java
- LocalDate date = // some LocalDate instance
- System.out.println(date.isLeapYear());
- ```
- For other `java.time` types, you can get the year numeric value and pass it to `Year.isLeap`, or get a `LocalDate` if applicable (or get a `Year` instance from the object). Example:
- ```java
- LocalDateTime dt = // some LocalDateTime instance
- // get the year numeric value and pass it to Year.isLeap
- System.out.println(Year.isLeap(dt.getYear()));
- // or get a LocalDate instance
- System.out.println(dt.toLocalDate().isLeapYear());
- // or get a Year instance
- System.out.println(Year.from(dt).isLeap());
- ```
- The first one (`getYear`) is more straightforward and - IMO - simpler. All API native date/time types have this getter (except, of course, the ones that don't make sense to have a year: `LocalTime`, `OffsetTime`, `MonthDay`, `Instant`, `Month` and `DayOfWeek`).
- The second one (`dt.toLocalDate().isLeapYear()`) might seem "bad" because it creates a `LocalDate` instance, but in the current implementation, `LocalDateTime` uses composition and encapsulates an instance of `LocalDate`, which is returned by `toLocalDate`. Anyway, it's an implementation detail: don't blindly rely on it if you wish to minimize the creation of new instances.
- The third one (`Year.from`) is preffered if you're working with a `TemporalAccessor` (an interface implemented by all API date/time types), without caring too much about the actual type: if it has a year field, that's all that matters. With this, you could create a method that works with any date/time type (and any other that you create, as long as it implements `TemporalAccessor`):
- ```java
- static boolean isLeapYear(TemporalAccessor t) {
- return Year.from(t).isLeap();
- }
- ...
- // it works with any type that implements TemporalAccessor and has a year field
- System.out.println(isLeapYear(LocalDate.now()));
- System.out.println(isLeapYear(LocalDateTime.now()));
- String string = "10/01/2023"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- // I don't need to create a LocalDate instance, just pass the result of parse (which is a TemporalAccessor) directly to the method
- System.out.println(isLeapYear(fmt.parse(string)));
- ```
- As a side note, in this case, you could also create a `TemporalQuery` and pass it directly to the `parse` method:
- ```java
- TemporalQuery<Boolean> isLeapYear = (t) -> Year.from(t).isLeap();
- String string = "10/01/2020"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- System.out.println(fmt.parse(string, isLeapYear)); // true
- ```
- ---
- ## Other calendars
- All the rules described above are valid for the ISO 8601 calendar. For short, it's like applying the Gregorian Calendar rules retroactively, for dates before October 1582 (the "cut-off date", as we saw above). That's the default calendar used by [`java.time` classes](https://docs.oracle.com/javase/tutorial/datetime/iso/overview.html).
- But the `java.time` API also supports another calendars: the implementations can be found in the [`java.time.chrono` package](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/chrono/package-summary.html). For example, the class `ThaiBuddhistChronology` (implementation of [Buddhist Calendar](https://en.wikipedia.org/wiki/Buddhist_calendar)) has a different rule for leap years:
- ```java
- int year = 2020;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // true
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // false
- year = 2543;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // false
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // true
- ```
- Anyway, for other calendar systems, just use the respective class in `java.time.chrono`. Or, if you need a different one (and don't want to implement them), you can use external libraries. Two alternatives are the [ThreeTen Extra project](https://www.threeten.org/threeten-extra/apidocs/org.threeten.extra/org/threeten/extra/chrono/package-summary.html) (made by the same creator of `java.time`) and [Time4j](http://www.time4j.net/javadoc-en/): both provide nice support for other calendars.
#5: Post edited
- There are many ways to do it, it depends on what data you already have and/or the Java version.
- ---
- # I have only the year's numeric value
- If you already have a value as a number (`int` or `long`), and **is using Java >= 8**, you can use the [`java.time.Year` class](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html), which has the [static method `isLeap`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html#isLeap(long)). This is the "faster" and more straighforward, no need to create instances of any object, it's just a static method call:
- ```java
- int year = 2000;
- System.out.println(Year.isLeap(year)); // true
- ```
- For **Java <= 7**, use [`Calendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Calendar.html). Unfortunately it's not as straightforward as Java 8's solution, you have to create an instance and then check how many days the year has:
- ```java
- int year = 2000;
- Calendar cal = Calendar.getInstance();
- cal.set(Calendar.YEAR, year);
- // the year is leap if it has more than 365 days
- System.out.println(cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // true
- ```
- Another alternative is to create an instance of [`GregorianCalendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html) and use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html#isLeapYear(int)):
- ```java
- int year = 2000;
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- ---
- Both `Calendar` and `GregorianCalendar` use the [Gregorian Calendar's rules](https://en.wikipedia.org/wiki/Gregorian_calendar): if the year is divisible by 100, it's leap only if it's also divisible by 400. If it's not divisible by 100, it's leap if it's divisible by 4. You _could_ implement this rule by yourself:
- ```java
- public static boolean isLeap(int ano) {
- return (ano % 4 == 0) && (ano % 100 != 0 || ano % 400 == 0);
- }
- ```
- But it already exists in the native API, so I don't see a reason to reinvent the wheel.
- **But beware**: `GregorianCalendar` has a "cut-off date", which is the year 1582 (when the Gregorian Calendar was introduced) - to be more precise, the date is `1582-10-15T00:00:00Z`: October 15th, 1582, at midnight, in [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time). This means that, for any dates before that, `GregorianCalendar` uses the [Julian Calendar's rules](https://en.wikipedia.org/wiki/Julian_calendar), in which any year divisible by 4 is leap.
- Example: according to `GregorianCalendar`, 1900 isn't a leap year, because it's greater than 1582 and if follows the current rule (it's divisible by 100, but no by 400), but 1500 is leap because it's before 1582, so the divisible-by-400 rule doesn't apply.
- But you can change this behaviour by setting a different cut-off date:
- ```java
- int year = 1500;
- GregorianCalendar cal = new GregorianCalendar();
- // 1500 is before the cut-off date (1582) and uses the "old" rule (divisible by 4 == leap)
- System.out.println(cal.isLeapYear(year)); // true
- cal.setGregorianChange(new Date(-62135758799190L)); // change cut-off date to January 1st, 0001
- // 1500 isn't leap anymore (uses the current rule: if divisible by 100, it's leap only if it's also divisible by 400)
- System.out.println(cal.isLeapYear(year)); // false
- ```
- Another detail: only `GregorianCalendar` does that. But `Calendar.getInstance()` **might or might not** do it, because the `getInstance` method can return either a `GregorianCalendar`, or some other sub-classes, depending on the default locale configured in the JVM. For example: if the default locale is `th_TH` (Thailand), `getInstance` returns a `BuddhistCalendar`, and for that calendar, 1500 isn't a leap year:
- ```java
- Locale.setDefault(new Locale("th", "TH"));
- int year = 2000;
- // for locale th_TH, it doesn't create a GregorianCalendar
- Calendar cal2 = Calendar.getInstance();
- System.out.println(cal2.getClass()); // class sun.util.BuddhistCalendar
- cal2.set(Calendar.YEAR, year);
- System.out.println(cal2.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // false
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- Of course you could force it to create a `GregorianCalendar`, by using a specific locale (such as `Calendar.getInstance(new Locale("pt", "BR"))` - for most locales, it returns a `GregorianCalendar`).
- On the other hand, `Year.isLeap` can't be configured (there's no such thing as changing the cut-off date), so it will always say that 1500 isn't leap.
That's what you'd use for any numeric value. If you want the **current** year, though, you could use `Year.now().isLeap()` for Java >= 8 (in that case, we can't avoid the creation of an instance). And for `Calendar`, simply don't set the year (don't call `set(Calendar.YEAR, ano)`), as `getInstance()` already returns the current date).- ---
- # What if I have an object that represents a date?
- ### Java <= 7
- If you have a `Calendar` instance, use the methods above.
- if you have a `java.util.Date`, **don't** use [`getYear()`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Date.html#getYear()), for two reasons:
- - it returns a value that is the result of subtracting 1900 from the year. Therefore, if the current year is 2023, `new Date().getYear()` returns `123`, and to check if it's leap, you have to remember to add 1900
- - it's deprecated since Java 1.1 and the documentation recommends using `Calendar`
- Therefore, if you have a `Date` instance, do like this:
- ```java
- Date date = // some Date instance
- // create a Calendar and set the Date
- Calendar cal = Calendar.getInstance();
- cal.setTime(date);
- // use the Calendar's methods already explained above
- ```
- And once again, if you want the current date, don't use `Date`, just `Calendar.getInstance()`.
- ### Java >= 8 (`java.time`)
- For `LocalDate`, just use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/LocalDate.html#isLeapYear()):
- ```java
- LocalDate date = // some LocalDate instance
- System.out.println(date.isLeapYear());
- ```
- For other `java.time` types, you can get the year numeric value and pass it to `Year.isLeap`, or get a `LocalDate` if applicable (or get a `Year` instance from the object). Example:
- ```java
- LocalDateTime dt = // some LocalDateTime instance
- // get the year numeric value and pass it to Year.isLeap
- System.out.println(Year.isLeap(dt.getYear()));
- // or get a LocalDate instance
- System.out.println(dt.toLocalDate().isLeapYear());
- // or get a Year instance
- System.out.println(Year.from(dt).isLeap());
- ```
- The first one (`getYear`) is more straightforward and - IMO - simpler. All API native date/time types have this getter (except, of course, the ones that don't make sense to have a year: `LocalTime`, `OffsetTime`, `MonthDay`, `Instant`, `Month` and `DayOfWeek`).
- The second one (`dt.toLocalDate().isLeapYear()`) might seem "bad" because it creates a `LocalDate` instance, but in the current implementation, `LocalDateTime` uses composition and encapsulates an instance of `LocalDate`, which is returned by `toLocalDate`. Anyway, it's an implementation detail: don't blindly rely on it if you wish to minimize the creation of new instances.
- The third one (`Year.from`) is preffered if you're working with a `TemporalAccessor` (an interface implemented by all API date/time types), without caring too much about the actual type: if it has a year field, that's all that matters. With this, you could create a method that works with any date/time type (and any other that you create, as long as it implements `TemporalAccessor`):
- ```java
- static boolean isLeapYear(TemporalAccessor t) {
- return Year.from(t).isLeap();
- }
- ...
- // it works with any type that implements TemporalAccessor and has a year field
- System.out.println(isLeapYear(LocalDate.now()));
- System.out.println(isLeapYear(LocalDateTime.now()));
- String string = "10/01/2023"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- // I don't need to create a LocalDate instance, just pass the result of parse (which is a TemporalAccessor) directly to the method
- System.out.println(isLeapYear(fmt.parse(string)));
- ```
- As a side note, in this case, you could also create a `TemporalQuery` and pass it directly to the `parse` method:
- ```java
- TemporalQuery<Boolean> isLeapYear = (t) -> Year.from(t).isLeap();
- String string = "10/01/2020"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- System.out.println(fmt.parse(string, isLeapYear)); // true
- ```
- ---
- ## Other calendars
- All the rules described above are valid for the ISO 8601 calendar. For short, it's like applying the Gregorian Calendar rules retroactively, for dates before October 1582 (the "cut-off date", as we saw above). That's the default calendar used by [`java.time` classes](https://docs.oracle.com/javase/tutorial/datetime/iso/overview.html).
But the `java.time` API also supports another calendars: the implementations can be found in the [`java.time.chrono` package](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/chrono/package-summary.html). For example, there's the class `ThaiBuddhistChronology` (implementation of [Buddhist Calendar](https://en.wikipedia.org/wiki/Buddhist_calendar)) has a different rule for leay years:- ```java
- int year = 2020;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // true
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // false
- year = 2543;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // false
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // true
- ```
- Anyway, for other calendar systems, just use the respective class in `java.time.chrono`. Or, if you need a different one (and don't want to implement them), you can use external libraries. Two alternatives are the [ThreeTen Extra project](https://www.threeten.org/threeten-extra/apidocs/org.threeten.extra/org/threeten/extra/chrono/package-summary.html) (made by the same creator of `java.time`) and [Time4j](http://www.time4j.net/javadoc-en/): both provide nice support for other calendars.
- There are many ways to do it, it depends on what data you already have and/or the Java version.
- ---
- # I have only the year's numeric value
- If you already have a value as a number (`int` or `long`), and **is using Java >= 8**, you can use the [`java.time.Year` class](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html), which has the [static method `isLeap`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html#isLeap(long)). This is the "faster" and more straighforward, no need to create instances of any object, it's just a static method call:
- ```java
- int year = 2000;
- System.out.println(Year.isLeap(year)); // true
- ```
- For **Java <= 7**, use [`Calendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Calendar.html). Unfortunately it's not as straightforward as Java 8's solution, you have to create an instance and then check how many days the year has:
- ```java
- int year = 2000;
- Calendar cal = Calendar.getInstance();
- cal.set(Calendar.YEAR, year);
- // the year is leap if it has more than 365 days
- System.out.println(cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // true
- ```
- Another alternative is to create an instance of [`GregorianCalendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html) and use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html#isLeapYear(int)):
- ```java
- int year = 2000;
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- ---
- Both `Calendar` and `GregorianCalendar` use the [Gregorian Calendar's rules](https://en.wikipedia.org/wiki/Gregorian_calendar): if the year is divisible by 100, it's leap only if it's also divisible by 400. If it's not divisible by 100, it's leap if it's divisible by 4. You _could_ implement this rule by yourself:
- ```java
- public static boolean isLeap(int ano) {
- return (ano % 4 == 0) && (ano % 100 != 0 || ano % 400 == 0);
- }
- ```
- But it already exists in the native API, so I don't see a reason to reinvent the wheel.
- **But beware**: `GregorianCalendar` has a "cut-off date", which is the year 1582 (when the Gregorian Calendar was introduced) - to be more precise, the date is `1582-10-15T00:00:00Z`: October 15th, 1582, at midnight, in [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time). This means that, for any dates before that, `GregorianCalendar` uses the [Julian Calendar's rules](https://en.wikipedia.org/wiki/Julian_calendar), in which any year divisible by 4 is leap.
- Example: according to `GregorianCalendar`, 1900 isn't a leap year, because it's greater than 1582 and if follows the current rule (it's divisible by 100, but no by 400), but 1500 is leap because it's before 1582, so the divisible-by-400 rule doesn't apply.
- But you can change this behaviour by setting a different cut-off date:
- ```java
- int year = 1500;
- GregorianCalendar cal = new GregorianCalendar();
- // 1500 is before the cut-off date (1582) and uses the "old" rule (divisible by 4 == leap)
- System.out.println(cal.isLeapYear(year)); // true
- cal.setGregorianChange(new Date(-62135758799190L)); // change cut-off date to January 1st, 0001
- // 1500 isn't leap anymore (uses the current rule: if divisible by 100, it's leap only if it's also divisible by 400)
- System.out.println(cal.isLeapYear(year)); // false
- ```
- Another detail: only `GregorianCalendar` does that. But `Calendar.getInstance()` **might or might not** do it, because the `getInstance` method can return either a `GregorianCalendar`, or some other sub-classes, depending on the default locale configured in the JVM. For example: if the default locale is `th_TH` (Thailand), `getInstance` returns a `BuddhistCalendar`, and for that calendar, 1500 isn't a leap year:
- ```java
- Locale.setDefault(new Locale("th", "TH"));
- int year = 2000;
- // for locale th_TH, it doesn't create a GregorianCalendar
- Calendar cal2 = Calendar.getInstance();
- System.out.println(cal2.getClass()); // class sun.util.BuddhistCalendar
- cal2.set(Calendar.YEAR, year);
- System.out.println(cal2.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // false
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- Of course you could force it to create a `GregorianCalendar`, by using a specific locale (such as `Calendar.getInstance(new Locale("pt", "BR"))` - for most locales, it returns a `GregorianCalendar`).
- On the other hand, `Year.isLeap` can't be configured (there's no such thing as changing the cut-off date), so it will always say that 1500 isn't leap.
- That's what you'd use for any numeric value. If you want the **current** year, though, you could use `Year.now().isLeap()` for Java >= 8 (in that case, we can't avoid the creation of an instance). And for `Calendar`, simply don't set the year (don't call `set(Calendar.YEAR, ano)`), as `getInstance()` already returns the current date.
- ---
- # What if I have an object that represents a date?
- ### Java <= 7
- If you have a `Calendar` instance, use the methods above.
- if you have a `java.util.Date`, **don't** use [`getYear()`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Date.html#getYear()), for two reasons:
- - it returns a value that is the result of subtracting 1900 from the year. Therefore, if the current year is 2023, `new Date().getYear()` returns `123`, and to check if it's leap, you have to remember to add 1900
- - it's deprecated since Java 1.1 and the documentation recommends using `Calendar`
- Therefore, if you have a `Date` instance, do like this:
- ```java
- Date date = // some Date instance
- // create a Calendar and set the Date
- Calendar cal = Calendar.getInstance();
- cal.setTime(date);
- // use the Calendar's methods already explained above
- ```
- And once again, if you want the current date, don't use `Date`, just `Calendar.getInstance()`.
- ### Java >= 8 (`java.time`)
- For `LocalDate`, just use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/LocalDate.html#isLeapYear()):
- ```java
- LocalDate date = // some LocalDate instance
- System.out.println(date.isLeapYear());
- ```
- For other `java.time` types, you can get the year numeric value and pass it to `Year.isLeap`, or get a `LocalDate` if applicable (or get a `Year` instance from the object). Example:
- ```java
- LocalDateTime dt = // some LocalDateTime instance
- // get the year numeric value and pass it to Year.isLeap
- System.out.println(Year.isLeap(dt.getYear()));
- // or get a LocalDate instance
- System.out.println(dt.toLocalDate().isLeapYear());
- // or get a Year instance
- System.out.println(Year.from(dt).isLeap());
- ```
- The first one (`getYear`) is more straightforward and - IMO - simpler. All API native date/time types have this getter (except, of course, the ones that don't make sense to have a year: `LocalTime`, `OffsetTime`, `MonthDay`, `Instant`, `Month` and `DayOfWeek`).
- The second one (`dt.toLocalDate().isLeapYear()`) might seem "bad" because it creates a `LocalDate` instance, but in the current implementation, `LocalDateTime` uses composition and encapsulates an instance of `LocalDate`, which is returned by `toLocalDate`. Anyway, it's an implementation detail: don't blindly rely on it if you wish to minimize the creation of new instances.
- The third one (`Year.from`) is preffered if you're working with a `TemporalAccessor` (an interface implemented by all API date/time types), without caring too much about the actual type: if it has a year field, that's all that matters. With this, you could create a method that works with any date/time type (and any other that you create, as long as it implements `TemporalAccessor`):
- ```java
- static boolean isLeapYear(TemporalAccessor t) {
- return Year.from(t).isLeap();
- }
- ...
- // it works with any type that implements TemporalAccessor and has a year field
- System.out.println(isLeapYear(LocalDate.now()));
- System.out.println(isLeapYear(LocalDateTime.now()));
- String string = "10/01/2023"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- // I don't need to create a LocalDate instance, just pass the result of parse (which is a TemporalAccessor) directly to the method
- System.out.println(isLeapYear(fmt.parse(string)));
- ```
- As a side note, in this case, you could also create a `TemporalQuery` and pass it directly to the `parse` method:
- ```java
- TemporalQuery<Boolean> isLeapYear = (t) -> Year.from(t).isLeap();
- String string = "10/01/2020"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- System.out.println(fmt.parse(string, isLeapYear)); // true
- ```
- ---
- ## Other calendars
- All the rules described above are valid for the ISO 8601 calendar. For short, it's like applying the Gregorian Calendar rules retroactively, for dates before October 1582 (the "cut-off date", as we saw above). That's the default calendar used by [`java.time` classes](https://docs.oracle.com/javase/tutorial/datetime/iso/overview.html).
- But the `java.time` API also supports another calendars: the implementations can be found in the [`java.time.chrono` package](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/chrono/package-summary.html). For example, the class `ThaiBuddhistChronology` (implementation of [Buddhist Calendar](https://en.wikipedia.org/wiki/Buddhist_calendar)) has a different rule for leap years:
- ```java
- int year = 2020;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // true
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // false
- year = 2543;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // false
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // true
- ```
- Anyway, for other calendar systems, just use the respective class in `java.time.chrono`. Or, if you need a different one (and don't want to implement them), you can use external libraries. Two alternatives are the [ThreeTen Extra project](https://www.threeten.org/threeten-extra/apidocs/org.threeten.extra/org/threeten/extra/chrono/package-summary.html) (made by the same creator of `java.time`) and [Time4j](http://www.time4j.net/javadoc-en/): both provide nice support for other calendars.
#4: Post edited
- There are many ways to do it, it depends on what data you already have and/or the Java version.
- ---
- # I have only the year's numeric value
- If you already have a value as a number (`int` or `long`), and **is using Java >= 8**, you can use the [`java.time.Year` class](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html), which has the [static method `isLeap`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html#isLeap(long)). This is the "faster" and more straighforward, no need to create instances of any object, it's just a static method call:
- ```java
- int year = 2000;
- System.out.println(Year.isLeap(year)); // true
- ```
- For **Java <= 7**, use [`Calendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Calendar.html). Unfortunately it's not as straightforward as Java 8's solution, you have to create an instance and then check how many days the year has:
- ```java
- int year = 2000;
- Calendar cal = Calendar.getInstance();
- cal.set(Calendar.YEAR, year);
- // the year is leap if it has more than 365 days
- System.out.println(cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // true
- ```
- Another alternative is to create an instance of [`GregorianCalendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html) and use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html#isLeapYear(int)):
- ```java
- int year = 2000;
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- ---
- Both `Calendar` and `GregorianCalendar` use the [Gregorian Calendar's rules](https://en.wikipedia.org/wiki/Gregorian_calendar): if the year is divisible by 100, it's leap only if it's also divisible by 400. If it's not divisible by 100, it's leap if it's divisible by 4. You _could_ implement this rule by yourself:
- ```java
- public static boolean isLeap(int ano) {
- return (ano % 4 == 0) && (ano % 100 != 0 || ano % 400 == 0);
- }
- ```
- But it already exists in the native API, so I don't see a reason to reinvent the wheel.
**But beware**: `GregorianCalendar` has a "cut-off date", which is the year 1582 (when the Gregorian Calendar was introduced) - to be more precise, the date is `1582-10-15T00:00:00Z`: October 15th, 1582, at midnight, in [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time). This means that, for any dates before that, `GregorianCalendar` uses the [Julian Calendar's rules](https://en.wikipedia.org/wiki/Julian_calendar), in which any year divisible by 4 is leap. Example: according to `GregorianCalendar`, 1900 isn't a leap year, because it's greater than 1582 and if follows the current rule (it's divisible by 100, but no by 400), but 1500 is leap because it's before 1582, so the divisible-by-400 rule doesn't apply.But you change this behaviour by setting a different cut-off date:- ```java
- int year = 1500;
- GregorianCalendar cal = new GregorianCalendar();
- // 1500 is before the cut-off date (1582) and uses the "old" rule (divisible by 4 == leap)
- System.out.println(cal.isLeapYear(year)); // true
- cal.setGregorianChange(new Date(-62135758799190L)); // change cut-off date to January 1st, 0001
- // 1500 isn't leap anymore (uses the current rule: if divisible by 100, it's leap only if it's also divisible by 400)
- System.out.println(cal.isLeapYear(year)); // false
- ```
- Another detail: only `GregorianCalendar` does that. But `Calendar.getInstance()` **might or might not** do it, because the `getInstance` method can return either a `GregorianCalendar`, or some other sub-classes, depending on the default locale configured in the JVM. For example: if the default locale is `th_TH` (Thailand), `getInstance` returns a `BuddhistCalendar`, and for that calendar, 1500 isn't a leap year:
- ```java
- Locale.setDefault(new Locale("th", "TH"));
- int year = 2000;
- // for locale th_TH, it doesn't create a GregorianCalendar
- Calendar cal2 = Calendar.getInstance();
- System.out.println(cal2.getClass()); // class sun.util.BuddhistCalendar
- cal2.set(Calendar.YEAR, year);
- System.out.println(cal2.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // false
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- Of course you could force it to create a `GregorianCalendar`, by using a specific locale (such as `Calendar.getInstance(new Locale("pt", "BR"))` - for most locales, it returns a `GregorianCalendar`).
- On the other hand, `Year.isLeap` can't be configured (there's no such thing as changing the cut-off date), so it will always say that 1500 isn't leap.
- That's what you'd use for any numeric value. If you want the **current** year, though, you could use `Year.now().isLeap()` for Java >= 8 (in that case, we can't avoid the creation of an instance). And for `Calendar`, simply don't set the year (don't call `set(Calendar.YEAR, ano)`), as `getInstance()` already returns the current date).
- ---
- # What if I have an object that represents a date?
- ### Java <= 7
- If you have a `Calendar` instance, use the methods above.
- if you have a `java.util.Date`, **don't** use [`getYear()`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Date.html#getYear()), for two reasons:
- - it returns a value that is the result of subtracting 1900 from the year. Therefore, if the current year is 2023, `new Date().getYear()` returns `123`, and to check if it's leap, you have to remember to add 1900
- - it's deprecated since Java 1.1 and the documentation recommends using `Calendar`
- Therefore, if you have a `Date` instance, do like this:
- ```java
- Date date = // some Date instance
- // create a Calendar and set the Date
- Calendar cal = Calendar.getInstance();
- cal.setTime(date);
- // use the Calendar's methods already explained above
- ```
- And once again, if you want the current date, don't use `Date`, just `Calendar.getInstance()`.
- ### Java >= 8 (`java.time`)
- For `LocalDate`, just use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/LocalDate.html#isLeapYear()):
- ```java
- LocalDate date = // some LocalDate instance
- System.out.println(date.isLeapYear());
- ```
- For other `java.time` types, you can get the year numeric value and pass it to `Year.isLeap`, or get a `LocalDate` if applicable (or get a `Year` instance from the object). Example:
- ```java
- LocalDateTime dt = // some LocalDateTime instance
- // get the year numeric value and pass it to Year.isLeap
- System.out.println(Year.isLeap(dt.getYear()));
- // or get a LocalDate instance
- System.out.println(dt.toLocalDate().isLeapYear());
- // or get a Year instance
- System.out.println(Year.from(dt).isLeap());
- ```
- The first one (`getYear`) is more straightforward and - IMO - simpler. All API native date/time types have this getter (except, of course, the ones that don't make sense to have a year: `LocalTime`, `OffsetTime`, `MonthDay`, `Instant`, `Month` and `DayOfWeek`).
- The second one (`dt.toLocalDate().isLeapYear()`) might seem "bad" because it creates a `LocalDate` instance, but in the current implementation, `LocalDateTime` uses composition and encapsulates an instance of `LocalDate`, which is returned by `toLocalDate`. Anyway, it's an implementation detail: don't blindly rely on it if you wish to minimize the creation of new instances.
- The third one (`Year.from`) is preffered if you're working with a `TemporalAccessor` (an interface implemented by all API date/time types), without caring too much about the actual type: if it has a year field, that's all that matters. With this, you could create a method that works with any date/time type (and any other that you create, as long as it implements `TemporalAccessor`):
- ```java
- static boolean isLeapYear(TemporalAccessor t) {
- return Year.from(t).isLeap();
- }
- ...
- // it works with any type that implements TemporalAccessor and has a year field
- System.out.println(isLeapYear(LocalDate.now()));
- System.out.println(isLeapYear(LocalDateTime.now()));
- String string = "10/01/2023"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- // I don't need to create a LocalDate instance, just pass the result of parse (which is a TemporalAccessor) directly to the method
- System.out.println(isLeapYear(fmt.parse(string)));
- ```
- As a side note, in this case, you could also create a `TemporalQuery` and pass it directly to the `parse` method:
- ```java
- TemporalQuery<Boolean> isLeapYear = (t) -> Year.from(t).isLeap();
- String string = "10/01/2020"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- System.out.println(fmt.parse(string, isLeapYear)); // true
- ```
- ---
- ## Other calendars
- All the rules described above are valid for the ISO 8601 calendar. For short, it's like applying the Gregorian Calendar rules retroactively, for dates before October 1582 (the "cut-off date", as we saw above). That's the default calendar used by [`java.time` classes](https://docs.oracle.com/javase/tutorial/datetime/iso/overview.html).
- But the `java.time` API also supports another calendars: the implementations can be found in the [`java.time.chrono` package](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/chrono/package-summary.html). For example, there's the class `ThaiBuddhistChronology` (implementation of [Buddhist Calendar](https://en.wikipedia.org/wiki/Buddhist_calendar)) has a different rule for leay years:
- ```java
- int year = 2020;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // true
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // false
- year = 2543;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // false
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // true
- ```
- Anyway, for other calendar systems, just use the respective class in `java.time.chrono`. Or, if you need a different one (and don't want to implement them), you can use external libraries. Two alternatives are the [ThreeTen Extra project](https://www.threeten.org/threeten-extra/apidocs/org.threeten.extra/org/threeten/extra/chrono/package-summary.html) (made by the same creator of `java.time`) and [Time4j](http://www.time4j.net/javadoc-en/): both provide nice support for other calendars.
- There are many ways to do it, it depends on what data you already have and/or the Java version.
- ---
- # I have only the year's numeric value
- If you already have a value as a number (`int` or `long`), and **is using Java >= 8**, you can use the [`java.time.Year` class](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html), which has the [static method `isLeap`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html#isLeap(long)). This is the "faster" and more straighforward, no need to create instances of any object, it's just a static method call:
- ```java
- int year = 2000;
- System.out.println(Year.isLeap(year)); // true
- ```
- For **Java <= 7**, use [`Calendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Calendar.html). Unfortunately it's not as straightforward as Java 8's solution, you have to create an instance and then check how many days the year has:
- ```java
- int year = 2000;
- Calendar cal = Calendar.getInstance();
- cal.set(Calendar.YEAR, year);
- // the year is leap if it has more than 365 days
- System.out.println(cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // true
- ```
- Another alternative is to create an instance of [`GregorianCalendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html) and use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html#isLeapYear(int)):
- ```java
- int year = 2000;
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- ---
- Both `Calendar` and `GregorianCalendar` use the [Gregorian Calendar's rules](https://en.wikipedia.org/wiki/Gregorian_calendar): if the year is divisible by 100, it's leap only if it's also divisible by 400. If it's not divisible by 100, it's leap if it's divisible by 4. You _could_ implement this rule by yourself:
- ```java
- public static boolean isLeap(int ano) {
- return (ano % 4 == 0) && (ano % 100 != 0 || ano % 400 == 0);
- }
- ```
- But it already exists in the native API, so I don't see a reason to reinvent the wheel.
- **But beware**: `GregorianCalendar` has a "cut-off date", which is the year 1582 (when the Gregorian Calendar was introduced) - to be more precise, the date is `1582-10-15T00:00:00Z`: October 15th, 1582, at midnight, in [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time). This means that, for any dates before that, `GregorianCalendar` uses the [Julian Calendar's rules](https://en.wikipedia.org/wiki/Julian_calendar), in which any year divisible by 4 is leap.
- Example: according to `GregorianCalendar`, 1900 isn't a leap year, because it's greater than 1582 and if follows the current rule (it's divisible by 100, but no by 400), but 1500 is leap because it's before 1582, so the divisible-by-400 rule doesn't apply.
- But you can change this behaviour by setting a different cut-off date:
- ```java
- int year = 1500;
- GregorianCalendar cal = new GregorianCalendar();
- // 1500 is before the cut-off date (1582) and uses the "old" rule (divisible by 4 == leap)
- System.out.println(cal.isLeapYear(year)); // true
- cal.setGregorianChange(new Date(-62135758799190L)); // change cut-off date to January 1st, 0001
- // 1500 isn't leap anymore (uses the current rule: if divisible by 100, it's leap only if it's also divisible by 400)
- System.out.println(cal.isLeapYear(year)); // false
- ```
- Another detail: only `GregorianCalendar` does that. But `Calendar.getInstance()` **might or might not** do it, because the `getInstance` method can return either a `GregorianCalendar`, or some other sub-classes, depending on the default locale configured in the JVM. For example: if the default locale is `th_TH` (Thailand), `getInstance` returns a `BuddhistCalendar`, and for that calendar, 1500 isn't a leap year:
- ```java
- Locale.setDefault(new Locale("th", "TH"));
- int year = 2000;
- // for locale th_TH, it doesn't create a GregorianCalendar
- Calendar cal2 = Calendar.getInstance();
- System.out.println(cal2.getClass()); // class sun.util.BuddhistCalendar
- cal2.set(Calendar.YEAR, year);
- System.out.println(cal2.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // false
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- Of course you could force it to create a `GregorianCalendar`, by using a specific locale (such as `Calendar.getInstance(new Locale("pt", "BR"))` - for most locales, it returns a `GregorianCalendar`).
- On the other hand, `Year.isLeap` can't be configured (there's no such thing as changing the cut-off date), so it will always say that 1500 isn't leap.
- That's what you'd use for any numeric value. If you want the **current** year, though, you could use `Year.now().isLeap()` for Java >= 8 (in that case, we can't avoid the creation of an instance). And for `Calendar`, simply don't set the year (don't call `set(Calendar.YEAR, ano)`), as `getInstance()` already returns the current date).
- ---
- # What if I have an object that represents a date?
- ### Java <= 7
- If you have a `Calendar` instance, use the methods above.
- if you have a `java.util.Date`, **don't** use [`getYear()`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Date.html#getYear()), for two reasons:
- - it returns a value that is the result of subtracting 1900 from the year. Therefore, if the current year is 2023, `new Date().getYear()` returns `123`, and to check if it's leap, you have to remember to add 1900
- - it's deprecated since Java 1.1 and the documentation recommends using `Calendar`
- Therefore, if you have a `Date` instance, do like this:
- ```java
- Date date = // some Date instance
- // create a Calendar and set the Date
- Calendar cal = Calendar.getInstance();
- cal.setTime(date);
- // use the Calendar's methods already explained above
- ```
- And once again, if you want the current date, don't use `Date`, just `Calendar.getInstance()`.
- ### Java >= 8 (`java.time`)
- For `LocalDate`, just use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/LocalDate.html#isLeapYear()):
- ```java
- LocalDate date = // some LocalDate instance
- System.out.println(date.isLeapYear());
- ```
- For other `java.time` types, you can get the year numeric value and pass it to `Year.isLeap`, or get a `LocalDate` if applicable (or get a `Year` instance from the object). Example:
- ```java
- LocalDateTime dt = // some LocalDateTime instance
- // get the year numeric value and pass it to Year.isLeap
- System.out.println(Year.isLeap(dt.getYear()));
- // or get a LocalDate instance
- System.out.println(dt.toLocalDate().isLeapYear());
- // or get a Year instance
- System.out.println(Year.from(dt).isLeap());
- ```
- The first one (`getYear`) is more straightforward and - IMO - simpler. All API native date/time types have this getter (except, of course, the ones that don't make sense to have a year: `LocalTime`, `OffsetTime`, `MonthDay`, `Instant`, `Month` and `DayOfWeek`).
- The second one (`dt.toLocalDate().isLeapYear()`) might seem "bad" because it creates a `LocalDate` instance, but in the current implementation, `LocalDateTime` uses composition and encapsulates an instance of `LocalDate`, which is returned by `toLocalDate`. Anyway, it's an implementation detail: don't blindly rely on it if you wish to minimize the creation of new instances.
- The third one (`Year.from`) is preffered if you're working with a `TemporalAccessor` (an interface implemented by all API date/time types), without caring too much about the actual type: if it has a year field, that's all that matters. With this, you could create a method that works with any date/time type (and any other that you create, as long as it implements `TemporalAccessor`):
- ```java
- static boolean isLeapYear(TemporalAccessor t) {
- return Year.from(t).isLeap();
- }
- ...
- // it works with any type that implements TemporalAccessor and has a year field
- System.out.println(isLeapYear(LocalDate.now()));
- System.out.println(isLeapYear(LocalDateTime.now()));
- String string = "10/01/2023"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- // I don't need to create a LocalDate instance, just pass the result of parse (which is a TemporalAccessor) directly to the method
- System.out.println(isLeapYear(fmt.parse(string)));
- ```
- As a side note, in this case, you could also create a `TemporalQuery` and pass it directly to the `parse` method:
- ```java
- TemporalQuery<Boolean> isLeapYear = (t) -> Year.from(t).isLeap();
- String string = "10/01/2020"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- System.out.println(fmt.parse(string, isLeapYear)); // true
- ```
- ---
- ## Other calendars
- All the rules described above are valid for the ISO 8601 calendar. For short, it's like applying the Gregorian Calendar rules retroactively, for dates before October 1582 (the "cut-off date", as we saw above). That's the default calendar used by [`java.time` classes](https://docs.oracle.com/javase/tutorial/datetime/iso/overview.html).
- But the `java.time` API also supports another calendars: the implementations can be found in the [`java.time.chrono` package](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/chrono/package-summary.html). For example, there's the class `ThaiBuddhistChronology` (implementation of [Buddhist Calendar](https://en.wikipedia.org/wiki/Buddhist_calendar)) has a different rule for leay years:
- ```java
- int year = 2020;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // true
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // false
- year = 2543;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // false
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // true
- ```
- Anyway, for other calendar systems, just use the respective class in `java.time.chrono`. Or, if you need a different one (and don't want to implement them), you can use external libraries. Two alternatives are the [ThreeTen Extra project](https://www.threeten.org/threeten-extra/apidocs/org.threeten.extra/org/threeten/extra/chrono/package-summary.html) (made by the same creator of `java.time`) and [Time4j](http://www.time4j.net/javadoc-en/): both provide nice support for other calendars.
#3: Post edited
- There are many ways to do it, it depends on what data you already have and/or the Java version.
- ---
- # I have only the year's numeric value
- If you already have a value as a number (`int` or `long`), and **is using Java >= 8**, you can use the [`java.time.Year` class](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html), which has the [static method `isLeap`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html#isLeap(long)). This is the "faster" and more straighforward, no need to create instances of any object, it's just a static method call:
- ```java
- int year = 2000;
- System.out.println(Year.isLeap(year)); // true
- ```
- For **Java <= 7**, use [`Calendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Calendar.html). Unfortunately it's not as straightforward as Java 8's solution, you have to create an instance and then check how many days the year has:
- ```java
- int year = 2000;
- Calendar cal = Calendar.getInstance();
- cal.set(Calendar.YEAR, year);
- // the year is leap if it has more than 365 days
- System.out.println(cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // true
- ```
- Another alternative is to create an instance of [`GregorianCalendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html) and use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html#isLeapYear(int)):
- ```java
- int year = 2000;
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- ---
Atention: both `Calendar` and `GregorianCalendar` use the [Gregorian Calendar's rules](https://en.wikipedia.org/wiki/Gregorian_calendar): if the year is divisible by 100, it's leap only if it's also divisible by 400. If it's not divisible by 100, it's leap if it's divisible by 4. You _could_ implement this rule by yourself:- ```java
public static boolean bissexto(int ano) {- return (ano % 4 == 0) && (ano % 100 != 0 || ano % 400 == 0);
- }
- ```
- But it already exists in the native API, so I don't see a reason to reinvent the wheel.
- **But beware**: `GregorianCalendar` has a "cut-off date", which is the year 1582 (when the Gregorian Calendar was introduced) - to be more precise, the date is `1582-10-15T00:00:00Z`: October 15th, 1582, at midnight, in [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time). This means that, for any dates before that, `GregorianCalendar` uses the [Julian Calendar's rules](https://en.wikipedia.org/wiki/Julian_calendar), in which any year divisible by 4 is leap. Example: according to `GregorianCalendar`, 1900 isn't a leap year, because it's greater than 1582 and if follows the current rule (it's divisible by 100, but no by 400), but 1500 is leap because it's before 1582, so the divisible-by-400 rule doesn't apply.
- But you change this behaviour by setting a different cut-off date:
- ```java
- int year = 1500;
- GregorianCalendar cal = new GregorianCalendar();
- // 1500 is before the cut-off date (1582) and uses the "old" rule (divisible by 4 == leap)
- System.out.println(cal.isLeapYear(year)); // true
- cal.setGregorianChange(new Date(-62135758799190L)); // change cut-off date to January 1st, 0001
- // 1500 isn't leap anymore (uses the current rule: if divisible by 100, it's leap only if it's also divisible by 400)
- System.out.println(cal.isLeapYear(year)); // false
- ```
- Another detail: only `GregorianCalendar` does that. But `Calendar.getInstance()` **might or might not** do it, because the `getInstance` method can return either a `GregorianCalendar`, or some other sub-classes, depending on the default locale configured in the JVM. For example: if the default locale is `th_TH` (Thailand), `getInstance` returns a `BuddhistCalendar`, and for that calendar, 1500 isn't a leap year:
- ```java
- Locale.setDefault(new Locale("th", "TH"));
- int year = 2000;
- // for locale th_TH, it doesn't create a GregorianCalendar
- Calendar cal2 = Calendar.getInstance();
- System.out.println(cal2.getClass()); // class sun.util.BuddhistCalendar
- cal2.set(Calendar.YEAR, year);
- System.out.println(cal2.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // false
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- Of course you could force it to create a `GregorianCalendar`, by using a specific locale (such as `Calendar.getInstance(new Locale("pt", "BR"))` - for most locales, it returns a `GregorianCalendar`).
- On the other hand, `Year.isLeap` can't be configured (there's no such thing as changing the cut-off date), so it will always say that 1500 isn't leap.
- That's what you'd use for any numeric value. If you want the **current** year, though, you could use `Year.now().isLeap()` for Java >= 8 (in that case, we can't avoid the creation of an instance). And for `Calendar`, simply don't set the year (don't call `set(Calendar.YEAR, ano)`), as `getInstance()` already returns the current date).
- ---
- # What if I have an object that represents a date?
- ### Java <= 7
- If you have a `Calendar` instance, use the methods above.
- if you have a `java.util.Date`, **don't** use [`getYear()`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Date.html#getYear()), for two reasons:
- - it returns a value that is the result of subtracting 1900 from the year. Therefore, if the current year is 2023, `new Date().getYear()` returns `123`, and to check if it's leap, you have to remember to add 1900
- - it's deprecated since Java 1.1 and the documentation recommends using `Calendar`
- Therefore, if you have a `Date` instance, do like this:
- ```java
- Date date = // some Date instance
- // create a Calendar and set the Date
- Calendar cal = Calendar.getInstance();
- cal.setTime(date);
- // use the Calendar's methods already explained above
- ```
- And once again, if you want the current date, don't use `Date`, just `Calendar.getInstance()`.
- ### Java >= 8 (`java.time`)
- For `LocalDate`, just use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/LocalDate.html#isLeapYear()):
- ```java
- LocalDate date = // some LocalDate instance
- System.out.println(date.isLeapYear());
- ```
- For other `java.time` types, you can get the year numeric value and pass it to `Year.isLeap`, or get a `LocalDate` if applicable (or get a `Year` instance from the object). Example:
- ```java
- LocalDateTime dt = // some LocalDateTime instance
- // get the year numeric value and pass it to Year.isLeap
- System.out.println(Year.isLeap(dt.getYear()));
- // or get a LocalDate instance
- System.out.println(dt.toLocalDate().isLeapYear());
- // or get a Year instance
- System.out.println(Year.from(dt).isLeap());
- ```
- The first one (`getYear`) is more straightforward and - IMO - simpler. All API native date/time types have this getter (except, of course, the ones that don't make sense to have a year: `LocalTime`, `OffsetTime`, `MonthDay`, `Instant`, `Month` and `DayOfWeek`).
- The second one (`dt.toLocalDate().isLeapYear()`) might seem "bad" because it creates a `LocalDate` instance, but in the current implementation, `LocalDateTime` uses composition and encapsulates an instance of `LocalDate`, which is returned by `toLocalDate`. Anyway, it's an implementation detail: don't blindly rely on it if you wish to minimize the creation of new instances.
- The third one (`Year.from`) is preffered if you're working with a `TemporalAccessor` (an interface implemented by all API date/time types), without caring too much about the actual type: if it has a year field, that's all that matters. With this, you could create a method that works with any date/time type (and any other that you create, as long as it implements `TemporalAccessor`):
- ```java
- static boolean isLeapYear(TemporalAccessor t) {
- return Year.from(t).isLeap();
- }
- ...
- // it works with any type that implements TemporalAccessor and has a year field
- System.out.println(isLeapYear(LocalDate.now()));
- System.out.println(isLeapYear(LocalDateTime.now()));
- String string = "10/01/2023"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- // I don't need to create a LocalDate instance, just pass the result of parse (which is a TemporalAccessor) directly to the method
- System.out.println(isLeapYear(fmt.parse(string)));
- ```
- As a side note, in this case, you could also create a `TemporalQuery` and pass it directly to the `parse` method:
- ```java
- TemporalQuery<Boolean> isLeapYear = (t) -> Year.from(t).isLeap();
- String string = "10/01/2020"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- System.out.println(fmt.parse(string, isLeapYear)); // true
- ```
- ---
- ## Other calendars
- All the rules described above are valid for the ISO 8601 calendar. For short, it's like applying the Gregorian Calendar rules retroactively, for dates before October 1582 (the "cut-off date", as we saw above). That's the default calendar used by [`java.time` classes](https://docs.oracle.com/javase/tutorial/datetime/iso/overview.html).
- But the `java.time` API also supports another calendars: the implementations can be found in the [`java.time.chrono` package](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/chrono/package-summary.html). For example, there's the class `ThaiBuddhistChronology` (implementation of [Buddhist Calendar](https://en.wikipedia.org/wiki/Buddhist_calendar)) has a different rule for leay years:
- ```java
- int year = 2020;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // true
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // false
- year = 2543;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // false
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // true
- ```
- Anyway, for other calendar systems, just use the respective class in `java.time.chrono`. Or, if you need a different one (and don't want to implement them), you can use external libraries. Two alternatives are the [ThreeTen Extra project](https://www.threeten.org/threeten-extra/apidocs/org.threeten.extra/org/threeten/extra/chrono/package-summary.html) (made by the same creator of `java.time`) and [Time4j](http://www.time4j.net/javadoc-en/): both provide nice support for other calendars.
- There are many ways to do it, it depends on what data you already have and/or the Java version.
- ---
- # I have only the year's numeric value
- If you already have a value as a number (`int` or `long`), and **is using Java >= 8**, you can use the [`java.time.Year` class](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html), which has the [static method `isLeap`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html#isLeap(long)). This is the "faster" and more straighforward, no need to create instances of any object, it's just a static method call:
- ```java
- int year = 2000;
- System.out.println(Year.isLeap(year)); // true
- ```
- For **Java <= 7**, use [`Calendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Calendar.html). Unfortunately it's not as straightforward as Java 8's solution, you have to create an instance and then check how many days the year has:
- ```java
- int year = 2000;
- Calendar cal = Calendar.getInstance();
- cal.set(Calendar.YEAR, year);
- // the year is leap if it has more than 365 days
- System.out.println(cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // true
- ```
- Another alternative is to create an instance of [`GregorianCalendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html) and use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html#isLeapYear(int)):
- ```java
- int year = 2000;
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- ---
- Both `Calendar` and `GregorianCalendar` use the [Gregorian Calendar's rules](https://en.wikipedia.org/wiki/Gregorian_calendar): if the year is divisible by 100, it's leap only if it's also divisible by 400. If it's not divisible by 100, it's leap if it's divisible by 4. You _could_ implement this rule by yourself:
- ```java
- public static boolean isLeap(int ano) {
- return (ano % 4 == 0) && (ano % 100 != 0 || ano % 400 == 0);
- }
- ```
- But it already exists in the native API, so I don't see a reason to reinvent the wheel.
- **But beware**: `GregorianCalendar` has a "cut-off date", which is the year 1582 (when the Gregorian Calendar was introduced) - to be more precise, the date is `1582-10-15T00:00:00Z`: October 15th, 1582, at midnight, in [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time). This means that, for any dates before that, `GregorianCalendar` uses the [Julian Calendar's rules](https://en.wikipedia.org/wiki/Julian_calendar), in which any year divisible by 4 is leap. Example: according to `GregorianCalendar`, 1900 isn't a leap year, because it's greater than 1582 and if follows the current rule (it's divisible by 100, but no by 400), but 1500 is leap because it's before 1582, so the divisible-by-400 rule doesn't apply.
- But you change this behaviour by setting a different cut-off date:
- ```java
- int year = 1500;
- GregorianCalendar cal = new GregorianCalendar();
- // 1500 is before the cut-off date (1582) and uses the "old" rule (divisible by 4 == leap)
- System.out.println(cal.isLeapYear(year)); // true
- cal.setGregorianChange(new Date(-62135758799190L)); // change cut-off date to January 1st, 0001
- // 1500 isn't leap anymore (uses the current rule: if divisible by 100, it's leap only if it's also divisible by 400)
- System.out.println(cal.isLeapYear(year)); // false
- ```
- Another detail: only `GregorianCalendar` does that. But `Calendar.getInstance()` **might or might not** do it, because the `getInstance` method can return either a `GregorianCalendar`, or some other sub-classes, depending on the default locale configured in the JVM. For example: if the default locale is `th_TH` (Thailand), `getInstance` returns a `BuddhistCalendar`, and for that calendar, 1500 isn't a leap year:
- ```java
- Locale.setDefault(new Locale("th", "TH"));
- int year = 2000;
- // for locale th_TH, it doesn't create a GregorianCalendar
- Calendar cal2 = Calendar.getInstance();
- System.out.println(cal2.getClass()); // class sun.util.BuddhistCalendar
- cal2.set(Calendar.YEAR, year);
- System.out.println(cal2.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // false
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- Of course you could force it to create a `GregorianCalendar`, by using a specific locale (such as `Calendar.getInstance(new Locale("pt", "BR"))` - for most locales, it returns a `GregorianCalendar`).
- On the other hand, `Year.isLeap` can't be configured (there's no such thing as changing the cut-off date), so it will always say that 1500 isn't leap.
- That's what you'd use for any numeric value. If you want the **current** year, though, you could use `Year.now().isLeap()` for Java >= 8 (in that case, we can't avoid the creation of an instance). And for `Calendar`, simply don't set the year (don't call `set(Calendar.YEAR, ano)`), as `getInstance()` already returns the current date).
- ---
- # What if I have an object that represents a date?
- ### Java <= 7
- If you have a `Calendar` instance, use the methods above.
- if you have a `java.util.Date`, **don't** use [`getYear()`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Date.html#getYear()), for two reasons:
- - it returns a value that is the result of subtracting 1900 from the year. Therefore, if the current year is 2023, `new Date().getYear()` returns `123`, and to check if it's leap, you have to remember to add 1900
- - it's deprecated since Java 1.1 and the documentation recommends using `Calendar`
- Therefore, if you have a `Date` instance, do like this:
- ```java
- Date date = // some Date instance
- // create a Calendar and set the Date
- Calendar cal = Calendar.getInstance();
- cal.setTime(date);
- // use the Calendar's methods already explained above
- ```
- And once again, if you want the current date, don't use `Date`, just `Calendar.getInstance()`.
- ### Java >= 8 (`java.time`)
- For `LocalDate`, just use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/LocalDate.html#isLeapYear()):
- ```java
- LocalDate date = // some LocalDate instance
- System.out.println(date.isLeapYear());
- ```
- For other `java.time` types, you can get the year numeric value and pass it to `Year.isLeap`, or get a `LocalDate` if applicable (or get a `Year` instance from the object). Example:
- ```java
- LocalDateTime dt = // some LocalDateTime instance
- // get the year numeric value and pass it to Year.isLeap
- System.out.println(Year.isLeap(dt.getYear()));
- // or get a LocalDate instance
- System.out.println(dt.toLocalDate().isLeapYear());
- // or get a Year instance
- System.out.println(Year.from(dt).isLeap());
- ```
- The first one (`getYear`) is more straightforward and - IMO - simpler. All API native date/time types have this getter (except, of course, the ones that don't make sense to have a year: `LocalTime`, `OffsetTime`, `MonthDay`, `Instant`, `Month` and `DayOfWeek`).
- The second one (`dt.toLocalDate().isLeapYear()`) might seem "bad" because it creates a `LocalDate` instance, but in the current implementation, `LocalDateTime` uses composition and encapsulates an instance of `LocalDate`, which is returned by `toLocalDate`. Anyway, it's an implementation detail: don't blindly rely on it if you wish to minimize the creation of new instances.
- The third one (`Year.from`) is preffered if you're working with a `TemporalAccessor` (an interface implemented by all API date/time types), without caring too much about the actual type: if it has a year field, that's all that matters. With this, you could create a method that works with any date/time type (and any other that you create, as long as it implements `TemporalAccessor`):
- ```java
- static boolean isLeapYear(TemporalAccessor t) {
- return Year.from(t).isLeap();
- }
- ...
- // it works with any type that implements TemporalAccessor and has a year field
- System.out.println(isLeapYear(LocalDate.now()));
- System.out.println(isLeapYear(LocalDateTime.now()));
- String string = "10/01/2023"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- // I don't need to create a LocalDate instance, just pass the result of parse (which is a TemporalAccessor) directly to the method
- System.out.println(isLeapYear(fmt.parse(string)));
- ```
- As a side note, in this case, you could also create a `TemporalQuery` and pass it directly to the `parse` method:
- ```java
- TemporalQuery<Boolean> isLeapYear = (t) -> Year.from(t).isLeap();
- String string = "10/01/2020"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- System.out.println(fmt.parse(string, isLeapYear)); // true
- ```
- ---
- ## Other calendars
- All the rules described above are valid for the ISO 8601 calendar. For short, it's like applying the Gregorian Calendar rules retroactively, for dates before October 1582 (the "cut-off date", as we saw above). That's the default calendar used by [`java.time` classes](https://docs.oracle.com/javase/tutorial/datetime/iso/overview.html).
- But the `java.time` API also supports another calendars: the implementations can be found in the [`java.time.chrono` package](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/chrono/package-summary.html). For example, there's the class `ThaiBuddhistChronology` (implementation of [Buddhist Calendar](https://en.wikipedia.org/wiki/Buddhist_calendar)) has a different rule for leay years:
- ```java
- int year = 2020;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // true
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // false
- year = 2543;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // false
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // true
- ```
- Anyway, for other calendar systems, just use the respective class in `java.time.chrono`. Or, if you need a different one (and don't want to implement them), you can use external libraries. Two alternatives are the [ThreeTen Extra project](https://www.threeten.org/threeten-extra/apidocs/org.threeten.extra/org/threeten/extra/chrono/package-summary.html) (made by the same creator of `java.time`) and [Time4j](http://www.time4j.net/javadoc-en/): both provide nice support for other calendars.
#2: Post edited
- There are many ways to do it, it depends on what data you already have and/or the Java version.
- ---
- # I have only the year's numeric value
If you already have a value as a number (`int` or `long`, for instance), and **is using Java >= 8**, you can use the [`java.time.Year` class](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html), which has the [static method `isLeap`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html#isLeap(long)). This is the "faster" and more straighforward, no need to create instances of any object, it's just a static method call:- ```java
- int year = 2000;
- System.out.println(Year.isLeap(year)); // true
- ```
- For **Java <= 7**, use [`Calendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Calendar.html). Unfortunately it's not as straightforward as Java 8's solution, you have to create an instance and then check how many days the year has:
- ```java
- int year = 2000;
- Calendar cal = Calendar.getInstance();
- cal.set(Calendar.YEAR, year);
- // the year is leap if it has more than 365 days
- System.out.println(cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // true
- ```
- Another alternative is to create an instance of [`GregorianCalendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html) and use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html#isLeapYear(int)):
- ```java
- int year = 2000;
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- ---
- Atention: both `Calendar` and `GregorianCalendar` use the [Gregorian Calendar's rules](https://en.wikipedia.org/wiki/Gregorian_calendar): if the year is divisible by 100, it's leap only if it's also divisible by 400. If it's not divisible by 100, it's leap if it's divisible by 4. You _could_ implement this rule by yourself:
- ```java
- public static boolean bissexto(int ano) {
- return (ano % 4 == 0) && (ano % 100 != 0 || ano % 400 == 0);
- }
- ```
- But it already exists in the native API, so I don't see a reason to reinvent the wheel.
- **But beware**: `GregorianCalendar` has a "cut-off date", which is the year 1582 (when the Gregorian Calendar was introduced) - to be more precise, the date is `1582-10-15T00:00:00Z`: October 15th, 1582, at midnight, in [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time). This means that, for any dates before that, `GregorianCalendar` uses the [Julian Calendar's rules](https://en.wikipedia.org/wiki/Julian_calendar), in which any year divisible by 4 is leap. Example: according to `GregorianCalendar`, 1900 isn't a leap year, because it's greater than 1582 and if follows the current rule (it's divisible by 100, but no by 400), but 1500 is leap because it's before 1582, so the divisible-by-400 rule doesn't apply.
- But you change this behaviour by setting a different cut-off date:
- ```java
- int year = 1500;
- GregorianCalendar cal = new GregorianCalendar();
- // 1500 is before the cut-off date (1582) and uses the "old" rule (divisible by 4 == leap)
- System.out.println(cal.isLeapYear(year)); // true
- cal.setGregorianChange(new Date(-62135758799190L)); // change cut-off date to January 1st, 0001
- // 1500 isn't leap anymore (uses the current rule: if divisible by 100, it's leap only if it's also divisible by 400)
- System.out.println(cal.isLeapYear(year)); // false
- ```
- Another detail: only `GregorianCalendar` does that. But `Calendar.getInstance()` **might or might not** do it, because the `getInstance` method can return either a `GregorianCalendar`, or some other sub-classes, depending on the default locale configured in the JVM. For example: if the default locale is `th_TH` (Thailand), `getInstance` returns a `BuddhistCalendar`, and for that calendar, 1500 isn't a leap year:
- ```java
- Locale.setDefault(new Locale("th", "TH"));
- int year = 2000;
- // for locale th_TH, it doesn't create a GregorianCalendar
- Calendar cal2 = Calendar.getInstance();
- System.out.println(cal2.getClass()); // class sun.util.BuddhistCalendar
- cal2.set(Calendar.YEAR, year);
- System.out.println(cal2.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // false
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- Of course you could force it to create a `GregorianCalendar`, by using a specific locale (such as `Calendar.getInstance(new Locale("pt", "BR"))` - for most locales, it returns a `GregorianCalendar`).
- On the other hand, `Year.isLeap` can't be configured (there's no such thing as changing the cut-off date), so it will always say that 1500 isn't leap.
- That's what you'd use for any numeric value. If you want the **current** year, though, you could use `Year.now().isLeap()` for Java >= 8 (in that case, we can't avoid the creation of an instance). And for `Calendar`, simply don't set the year (don't call `set(Calendar.YEAR, ano)`), as `getInstance()` already returns the current date).
- ---
- # What if I have an object that represents a date?
- ### Java <= 7
- If you have a `Calendar` instance, use the methods above.
- if you have a `java.util.Date`, **don't** use [`getYear()`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Date.html#getYear()), for two reasons:
- - it returns a value that is the result of subtracting 1900 from the year. Therefore, if the current year is 2023, `new Date().getYear()` returns `123`, and to check if it's leap, you have to remember to add 1900
- - it's deprecated since Java 1.1 and the documentation recommends using `Calendar`
- Therefore, if you have a `Date` instance, do like this:
- ```java
- Date date = // some Date instance
- // create a Calendar and set the Date
- Calendar cal = Calendar.getInstance();
- cal.setTime(date);
- // use the Calendar's methods already explained above
- ```
- And once again, if you want the current date, don't use `Date`, just `Calendar.getInstance()`.
- ### Java >= 8 (`java.time`)
- For `LocalDate`, just use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/LocalDate.html#isLeapYear()):
- ```java
- LocalDate date = // some LocalDate instance
- System.out.println(date.isLeapYear());
- ```
- For other `java.time` types, you can get the year numeric value and pass it to `Year.isLeap`, or get a `LocalDate` if applicable (or get a `Year` instance from the object). Example:
- ```java
- LocalDateTime dt = // some LocalDateTime instance
- // get the year numeric value and pass it to Year.isLeap
- System.out.println(Year.isLeap(dt.getYear()));
- // or get a LocalDate instance
- System.out.println(dt.toLocalDate().isLeapYear());
- // or get a Year instance
- System.out.println(Year.from(dt).isLeap());
- ```
- The first one (`getYear`) is more straightforward and - IMO - simpler. All API native date/time types have this getter (except, of course, the ones that don't make sense to have a year: `LocalTime`, `OffsetTime`, `MonthDay`, `Instant`, `Month` and `DayOfWeek`).
- The second one (`dt.toLocalDate().isLeapYear()`) might seem "bad" because it creates a `LocalDate` instance, but in the current implementation, `LocalDateTime` uses composition and encapsulates an instance of `LocalDate`, which is returned by `toLocalDate`. Anyway, it's an implementation detail: don't blindly rely on it if you wish to minimize the creation of new instances.
- The third one (`Year.from`) is preffered if you're working with a `TemporalAccessor` (an interface implemented by all API date/time types), without caring too much about the actual type: if it has a year field, that's all that matters. With this, you could create a method that works with any date/time type (and any other that you create, as long as it implements `TemporalAccessor`):
- ```java
- static boolean isLeapYear(TemporalAccessor t) {
- return Year.from(t).isLeap();
- }
- ...
- // it works with any type that implements TemporalAccessor and has a year field
- System.out.println(isLeapYear(LocalDate.now()));
- System.out.println(isLeapYear(LocalDateTime.now()));
- String string = "10/01/2023"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- // I don't need to create a LocalDate instance, just pass the result of parse (which is a TemporalAccessor) directly to the method
- System.out.println(isLeapYear(fmt.parse(string)));
- ```
- As a side note, in this case, you could also create a `TemporalQuery` and pass it directly to the `parse` method:
- ```java
- TemporalQuery<Boolean> isLeapYear = (t) -> Year.from(t).isLeap();
- String string = "10/01/2020"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- System.out.println(fmt.parse(string, isLeapYear)); // true
- ```
- ---
- ## Other calendars
- All the rules described above are valid for the ISO 8601 calendar. For short, it's like applying the Gregorian Calendar rules retroactively, for dates before October 1582 (the "cut-off date", as we saw above). That's the default calendar used by [`java.time` classes](https://docs.oracle.com/javase/tutorial/datetime/iso/overview.html).
- But the `java.time` API also supports another calendars: the implementations can be found in the [`java.time.chrono` package](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/chrono/package-summary.html). For example, there's the class `ThaiBuddhistChronology` (implementation of [Buddhist Calendar](https://en.wikipedia.org/wiki/Buddhist_calendar)) has a different rule for leay years:
- ```java
- int year = 2020;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // true
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // false
- year = 2543;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // false
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // true
- ```
- Anyway, for other calendar systems, just use the respective class in `java.time.chrono`. Or, if you need a different one (and don't want to implement them), you can use external libraries. Two alternatives are the [ThreeTen Extra project](https://www.threeten.org/threeten-extra/apidocs/org.threeten.extra/org/threeten/extra/chrono/package-summary.html) (made by the same creator of `java.time`) and [Time4j](http://www.time4j.net/javadoc-en/): both provide nice support for other calendars.
- There are many ways to do it, it depends on what data you already have and/or the Java version.
- ---
- # I have only the year's numeric value
- If you already have a value as a number (`int` or `long`), and **is using Java >= 8**, you can use the [`java.time.Year` class](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html), which has the [static method `isLeap`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html#isLeap(long)). This is the "faster" and more straighforward, no need to create instances of any object, it's just a static method call:
- ```java
- int year = 2000;
- System.out.println(Year.isLeap(year)); // true
- ```
- For **Java <= 7**, use [`Calendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Calendar.html). Unfortunately it's not as straightforward as Java 8's solution, you have to create an instance and then check how many days the year has:
- ```java
- int year = 2000;
- Calendar cal = Calendar.getInstance();
- cal.set(Calendar.YEAR, year);
- // the year is leap if it has more than 365 days
- System.out.println(cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // true
- ```
- Another alternative is to create an instance of [`GregorianCalendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html) and use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html#isLeapYear(int)):
- ```java
- int year = 2000;
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- ---
- Atention: both `Calendar` and `GregorianCalendar` use the [Gregorian Calendar's rules](https://en.wikipedia.org/wiki/Gregorian_calendar): if the year is divisible by 100, it's leap only if it's also divisible by 400. If it's not divisible by 100, it's leap if it's divisible by 4. You _could_ implement this rule by yourself:
- ```java
- public static boolean bissexto(int ano) {
- return (ano % 4 == 0) && (ano % 100 != 0 || ano % 400 == 0);
- }
- ```
- But it already exists in the native API, so I don't see a reason to reinvent the wheel.
- **But beware**: `GregorianCalendar` has a "cut-off date", which is the year 1582 (when the Gregorian Calendar was introduced) - to be more precise, the date is `1582-10-15T00:00:00Z`: October 15th, 1582, at midnight, in [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time). This means that, for any dates before that, `GregorianCalendar` uses the [Julian Calendar's rules](https://en.wikipedia.org/wiki/Julian_calendar), in which any year divisible by 4 is leap. Example: according to `GregorianCalendar`, 1900 isn't a leap year, because it's greater than 1582 and if follows the current rule (it's divisible by 100, but no by 400), but 1500 is leap because it's before 1582, so the divisible-by-400 rule doesn't apply.
- But you change this behaviour by setting a different cut-off date:
- ```java
- int year = 1500;
- GregorianCalendar cal = new GregorianCalendar();
- // 1500 is before the cut-off date (1582) and uses the "old" rule (divisible by 4 == leap)
- System.out.println(cal.isLeapYear(year)); // true
- cal.setGregorianChange(new Date(-62135758799190L)); // change cut-off date to January 1st, 0001
- // 1500 isn't leap anymore (uses the current rule: if divisible by 100, it's leap only if it's also divisible by 400)
- System.out.println(cal.isLeapYear(year)); // false
- ```
- Another detail: only `GregorianCalendar` does that. But `Calendar.getInstance()` **might or might not** do it, because the `getInstance` method can return either a `GregorianCalendar`, or some other sub-classes, depending on the default locale configured in the JVM. For example: if the default locale is `th_TH` (Thailand), `getInstance` returns a `BuddhistCalendar`, and for that calendar, 1500 isn't a leap year:
- ```java
- Locale.setDefault(new Locale("th", "TH"));
- int year = 2000;
- // for locale th_TH, it doesn't create a GregorianCalendar
- Calendar cal2 = Calendar.getInstance();
- System.out.println(cal2.getClass()); // class sun.util.BuddhistCalendar
- cal2.set(Calendar.YEAR, year);
- System.out.println(cal2.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // false
- GregorianCalendar cal = new GregorianCalendar();
- System.out.println(cal.isLeapYear(year)); // true
- ```
- Of course you could force it to create a `GregorianCalendar`, by using a specific locale (such as `Calendar.getInstance(new Locale("pt", "BR"))` - for most locales, it returns a `GregorianCalendar`).
- On the other hand, `Year.isLeap` can't be configured (there's no such thing as changing the cut-off date), so it will always say that 1500 isn't leap.
- That's what you'd use for any numeric value. If you want the **current** year, though, you could use `Year.now().isLeap()` for Java >= 8 (in that case, we can't avoid the creation of an instance). And for `Calendar`, simply don't set the year (don't call `set(Calendar.YEAR, ano)`), as `getInstance()` already returns the current date).
- ---
- # What if I have an object that represents a date?
- ### Java <= 7
- If you have a `Calendar` instance, use the methods above.
- if you have a `java.util.Date`, **don't** use [`getYear()`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Date.html#getYear()), for two reasons:
- - it returns a value that is the result of subtracting 1900 from the year. Therefore, if the current year is 2023, `new Date().getYear()` returns `123`, and to check if it's leap, you have to remember to add 1900
- - it's deprecated since Java 1.1 and the documentation recommends using `Calendar`
- Therefore, if you have a `Date` instance, do like this:
- ```java
- Date date = // some Date instance
- // create a Calendar and set the Date
- Calendar cal = Calendar.getInstance();
- cal.setTime(date);
- // use the Calendar's methods already explained above
- ```
- And once again, if you want the current date, don't use `Date`, just `Calendar.getInstance()`.
- ### Java >= 8 (`java.time`)
- For `LocalDate`, just use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/LocalDate.html#isLeapYear()):
- ```java
- LocalDate date = // some LocalDate instance
- System.out.println(date.isLeapYear());
- ```
- For other `java.time` types, you can get the year numeric value and pass it to `Year.isLeap`, or get a `LocalDate` if applicable (or get a `Year` instance from the object). Example:
- ```java
- LocalDateTime dt = // some LocalDateTime instance
- // get the year numeric value and pass it to Year.isLeap
- System.out.println(Year.isLeap(dt.getYear()));
- // or get a LocalDate instance
- System.out.println(dt.toLocalDate().isLeapYear());
- // or get a Year instance
- System.out.println(Year.from(dt).isLeap());
- ```
- The first one (`getYear`) is more straightforward and - IMO - simpler. All API native date/time types have this getter (except, of course, the ones that don't make sense to have a year: `LocalTime`, `OffsetTime`, `MonthDay`, `Instant`, `Month` and `DayOfWeek`).
- The second one (`dt.toLocalDate().isLeapYear()`) might seem "bad" because it creates a `LocalDate` instance, but in the current implementation, `LocalDateTime` uses composition and encapsulates an instance of `LocalDate`, which is returned by `toLocalDate`. Anyway, it's an implementation detail: don't blindly rely on it if you wish to minimize the creation of new instances.
- The third one (`Year.from`) is preffered if you're working with a `TemporalAccessor` (an interface implemented by all API date/time types), without caring too much about the actual type: if it has a year field, that's all that matters. With this, you could create a method that works with any date/time type (and any other that you create, as long as it implements `TemporalAccessor`):
- ```java
- static boolean isLeapYear(TemporalAccessor t) {
- return Year.from(t).isLeap();
- }
- ...
- // it works with any type that implements TemporalAccessor and has a year field
- System.out.println(isLeapYear(LocalDate.now()));
- System.out.println(isLeapYear(LocalDateTime.now()));
- String string = "10/01/2023"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- // I don't need to create a LocalDate instance, just pass the result of parse (which is a TemporalAccessor) directly to the method
- System.out.println(isLeapYear(fmt.parse(string)));
- ```
- As a side note, in this case, you could also create a `TemporalQuery` and pass it directly to the `parse` method:
- ```java
- TemporalQuery<Boolean> isLeapYear = (t) -> Year.from(t).isLeap();
- String string = "10/01/2020"; // some date in day/month/year format
- DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu");
- System.out.println(fmt.parse(string, isLeapYear)); // true
- ```
- ---
- ## Other calendars
- All the rules described above are valid for the ISO 8601 calendar. For short, it's like applying the Gregorian Calendar rules retroactively, for dates before October 1582 (the "cut-off date", as we saw above). That's the default calendar used by [`java.time` classes](https://docs.oracle.com/javase/tutorial/datetime/iso/overview.html).
- But the `java.time` API also supports another calendars: the implementations can be found in the [`java.time.chrono` package](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/chrono/package-summary.html). For example, there's the class `ThaiBuddhistChronology` (implementation of [Buddhist Calendar](https://en.wikipedia.org/wiki/Buddhist_calendar)) has a different rule for leay years:
- ```java
- int year = 2020;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // true
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // false
- year = 2543;
- System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // false
- System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // true
- ```
- Anyway, for other calendar systems, just use the respective class in `java.time.chrono`. Or, if you need a different one (and don't want to implement them), you can use external libraries. Two alternatives are the [ThreeTen Extra project](https://www.threeten.org/threeten-extra/apidocs/org.threeten.extra/org/threeten/extra/chrono/package-summary.html) (made by the same creator of `java.time`) and [Time4j](http://www.time4j.net/javadoc-en/): both provide nice support for other calendars.
#1: Initial revision
There are many ways to do it, it depends on what data you already have and/or the Java version. --- # I have only the year's numeric value If you already have a value as a number (`int` or `long`, for instance), and **is using Java >= 8**, you can use the [`java.time.Year` class](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html), which has the [static method `isLeap`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/Year.html#isLeap(long)). This is the "faster" and more straighforward, no need to create instances of any object, it's just a static method call: ```java int year = 2000; System.out.println(Year.isLeap(year)); // true ``` For **Java <= 7**, use [`Calendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Calendar.html). Unfortunately it's not as straightforward as Java 8's solution, you have to create an instance and then check how many days the year has: ```java int year = 2000; Calendar cal = Calendar.getInstance(); cal.set(Calendar.YEAR, year); // the year is leap if it has more than 365 days System.out.println(cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // true ``` Another alternative is to create an instance of [`GregorianCalendar`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html) and use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/GregorianCalendar.html#isLeapYear(int)): ```java int year = 2000; GregorianCalendar cal = new GregorianCalendar(); System.out.println(cal.isLeapYear(year)); // true ``` --- Atention: both `Calendar` and `GregorianCalendar` use the [Gregorian Calendar's rules](https://en.wikipedia.org/wiki/Gregorian_calendar): if the year is divisible by 100, it's leap only if it's also divisible by 400. If it's not divisible by 100, it's leap if it's divisible by 4. You _could_ implement this rule by yourself: ```java public static boolean bissexto(int ano) { return (ano % 4 == 0) && (ano % 100 != 0 || ano % 400 == 0); } ``` But it already exists in the native API, so I don't see a reason to reinvent the wheel. **But beware**: `GregorianCalendar` has a "cut-off date", which is the year 1582 (when the Gregorian Calendar was introduced) - to be more precise, the date is `1582-10-15T00:00:00Z`: October 15th, 1582, at midnight, in [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time). This means that, for any dates before that, `GregorianCalendar` uses the [Julian Calendar's rules](https://en.wikipedia.org/wiki/Julian_calendar), in which any year divisible by 4 is leap. Example: according to `GregorianCalendar`, 1900 isn't a leap year, because it's greater than 1582 and if follows the current rule (it's divisible by 100, but no by 400), but 1500 is leap because it's before 1582, so the divisible-by-400 rule doesn't apply. But you change this behaviour by setting a different cut-off date: ```java int year = 1500; GregorianCalendar cal = new GregorianCalendar(); // 1500 is before the cut-off date (1582) and uses the "old" rule (divisible by 4 == leap) System.out.println(cal.isLeapYear(year)); // true cal.setGregorianChange(new Date(-62135758799190L)); // change cut-off date to January 1st, 0001 // 1500 isn't leap anymore (uses the current rule: if divisible by 100, it's leap only if it's also divisible by 400) System.out.println(cal.isLeapYear(year)); // false ``` Another detail: only `GregorianCalendar` does that. But `Calendar.getInstance()` **might or might not** do it, because the `getInstance` method can return either a `GregorianCalendar`, or some other sub-classes, depending on the default locale configured in the JVM. For example: if the default locale is `th_TH` (Thailand), `getInstance` returns a `BuddhistCalendar`, and for that calendar, 1500 isn't a leap year: ```java Locale.setDefault(new Locale("th", "TH")); int year = 2000; // for locale th_TH, it doesn't create a GregorianCalendar Calendar cal2 = Calendar.getInstance(); System.out.println(cal2.getClass()); // class sun.util.BuddhistCalendar cal2.set(Calendar.YEAR, year); System.out.println(cal2.getActualMaximum(Calendar.DAY_OF_YEAR) > 365); // false GregorianCalendar cal = new GregorianCalendar(); System.out.println(cal.isLeapYear(year)); // true ``` Of course you could force it to create a `GregorianCalendar`, by using a specific locale (such as `Calendar.getInstance(new Locale("pt", "BR"))` - for most locales, it returns a `GregorianCalendar`). On the other hand, `Year.isLeap` can't be configured (there's no such thing as changing the cut-off date), so it will always say that 1500 isn't leap. That's what you'd use for any numeric value. If you want the **current** year, though, you could use `Year.now().isLeap()` for Java >= 8 (in that case, we can't avoid the creation of an instance). And for `Calendar`, simply don't set the year (don't call `set(Calendar.YEAR, ano)`), as `getInstance()` already returns the current date). --- # What if I have an object that represents a date? ### Java <= 7 If you have a `Calendar` instance, use the methods above. if you have a `java.util.Date`, **don't** use [`getYear()`](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Date.html#getYear()), for two reasons: - it returns a value that is the result of subtracting 1900 from the year. Therefore, if the current year is 2023, `new Date().getYear()` returns `123`, and to check if it's leap, you have to remember to add 1900 - it's deprecated since Java 1.1 and the documentation recommends using `Calendar` Therefore, if you have a `Date` instance, do like this: ```java Date date = // some Date instance // create a Calendar and set the Date Calendar cal = Calendar.getInstance(); cal.setTime(date); // use the Calendar's methods already explained above ``` And once again, if you want the current date, don't use `Date`, just `Calendar.getInstance()`. ### Java >= 8 (`java.time`) For `LocalDate`, just use the [`isLeapYear` method](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/LocalDate.html#isLeapYear()): ```java LocalDate date = // some LocalDate instance System.out.println(date.isLeapYear()); ``` For other `java.time` types, you can get the year numeric value and pass it to `Year.isLeap`, or get a `LocalDate` if applicable (or get a `Year` instance from the object). Example: ```java LocalDateTime dt = // some LocalDateTime instance // get the year numeric value and pass it to Year.isLeap System.out.println(Year.isLeap(dt.getYear())); // or get a LocalDate instance System.out.println(dt.toLocalDate().isLeapYear()); // or get a Year instance System.out.println(Year.from(dt).isLeap()); ``` The first one (`getYear`) is more straightforward and - IMO - simpler. All API native date/time types have this getter (except, of course, the ones that don't make sense to have a year: `LocalTime`, `OffsetTime`, `MonthDay`, `Instant`, `Month` and `DayOfWeek`). The second one (`dt.toLocalDate().isLeapYear()`) might seem "bad" because it creates a `LocalDate` instance, but in the current implementation, `LocalDateTime` uses composition and encapsulates an instance of `LocalDate`, which is returned by `toLocalDate`. Anyway, it's an implementation detail: don't blindly rely on it if you wish to minimize the creation of new instances. The third one (`Year.from`) is preffered if you're working with a `TemporalAccessor` (an interface implemented by all API date/time types), without caring too much about the actual type: if it has a year field, that's all that matters. With this, you could create a method that works with any date/time type (and any other that you create, as long as it implements `TemporalAccessor`): ```java static boolean isLeapYear(TemporalAccessor t) { return Year.from(t).isLeap(); } ... // it works with any type that implements TemporalAccessor and has a year field System.out.println(isLeapYear(LocalDate.now())); System.out.println(isLeapYear(LocalDateTime.now())); String string = "10/01/2023"; // some date in day/month/year format DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu"); // I don't need to create a LocalDate instance, just pass the result of parse (which is a TemporalAccessor) directly to the method System.out.println(isLeapYear(fmt.parse(string))); ``` As a side note, in this case, you could also create a `TemporalQuery` and pass it directly to the `parse` method: ```java TemporalQuery<Boolean> isLeapYear = (t) -> Year.from(t).isLeap(); String string = "10/01/2020"; // some date in day/month/year format DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/uuuu"); System.out.println(fmt.parse(string, isLeapYear)); // true ``` --- ## Other calendars All the rules described above are valid for the ISO 8601 calendar. For short, it's like applying the Gregorian Calendar rules retroactively, for dates before October 1582 (the "cut-off date", as we saw above). That's the default calendar used by [`java.time` classes](https://docs.oracle.com/javase/tutorial/datetime/iso/overview.html). But the `java.time` API also supports another calendars: the implementations can be found in the [`java.time.chrono` package](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/time/chrono/package-summary.html). For example, there's the class `ThaiBuddhistChronology` (implementation of [Buddhist Calendar](https://en.wikipedia.org/wiki/Buddhist_calendar)) has a different rule for leay years: ```java int year = 2020; System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // true System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // false year = 2543; System.out.println(IsoChronology.INSTANCE.isLeapYear(year)); // false System.out.println(ThaiBuddhistChronology.INSTANCE.isLeapYear(year)); // true ``` Anyway, for other calendar systems, just use the respective class in `java.time.chrono`. Or, if you need a different one (and don't want to implement them), you can use external libraries. Two alternatives are the [ThreeTen Extra project](https://www.threeten.org/threeten-extra/apidocs/org.threeten.extra/org/threeten/extra/chrono/package-summary.html) (made by the same creator of `java.time`) and [Time4j](http://www.time4j.net/javadoc-en/): both provide nice support for other calendars.