java: represent Date with optional month and day - java

I would like to store Date with optional month and day in java.
I know of the java.time.LocalYear to store just the year.
Shall I create my own custom class to hold dates with optional month and day or are there any custom library to solve the problem.
public class Date {
private LocalYear year;
private int month;
private int day;
public Date(LocalYear year) {
this.year = year;
}
public Date(LocalYear year, int month) {
this.year = year;
this.month = month;
}
public Date(LocalYear year, int month, iny day) {
this.year = year;
this.month = month;
this.day = day;
}
}

It’s hard to guide you without knowing your use case. One option is using the TemporalAccessor interface as a common type for dates with and without month and/or day of month and then put either a LocalDate, a YearMonth or a Year into your variable (the last class is just called Year (not LocalYear, though it would have been in line with the naming scheme)). For example:
List<TemporalAccessor> dates = List.of(
LocalDate.of(2019, Month.OCTOBER, 3), // full date
YearMonth.of(2019, Month.NOVEMBER), // no day of month
Year.of(2020)); // no month or day of month
What can we use this for? One example:
for (TemporalAccessor ta : dates) {
System.out.println(ta);
System.out.println("Year: " + ta.get(ChronoField.YEAR));
if (ta.isSupported(ChronoField.MONTH_OF_YEAR)) {
System.out.println("Month: " + ta.get(ChronoField.MONTH_OF_YEAR));
} else {
System.out.println("Month: undefined");
}
if (ta.isSupported(ChronoField.DAY_OF_MONTH)) {
System.out.println("Day: " + ta.get(ChronoField.DAY_OF_MONTH));
} else {
System.out.println("Day: undefined");
}
System.out.println();
}
This outputs:
2019-10-03
Year: 2019
Month: 10
Day: 3
2019-11
Year: 2019
Month: 11
Day: undefined
2020
Year: 2020
Month: undefined
Day: undefined
Whether or how well it fulfils your requirements I cannot tell.
Using ChronoField constants for access is low-level, so you may want to wrap the TemporalAccessor in a nice class with nice getters. For example:
public class PartialDate {
private TemporalAccessor date;
public PartialDate(Year year) {
date = year;
}
public PartialDate(Year year, int month) {
date = year.atMonth(month);
}
public PartialDate(Year year, int month, int day) {
date = year.atMonth(month).atDay(day);
}
public Year getYear() {
return Year.from(date);
}
public OptionalInt getMonthValue() {
if (date.isSupported(ChronoField.MONTH_OF_YEAR)) {
return OptionalInt.of(date.get(ChronoField.MONTH_OF_YEAR));
} else {
return OptionalInt.empty();
}
}
// A similar getDay method
}
You may extend the class to your needs. Maybe you want constructors that accept a Month enum constant and/or a YearMonth object directly and/or getters that return those types wrapped in Optionals.
Link: Oracle tutorial: Date Time explaining how to use java.time.

Related

Java get a date by the given day name [duplicate]

This question already has answers here:
Is there a good way to get the date of the coming Wednesday?
(6 answers)
Closed 1 year ago.
How can I get a date by day's name?
For example:
Input: Monday
Output: 02/08/2021
Input: Tuesday
Output: 03/08/2021
I want to get the closest date of the day.
This is my understanding of what the OP wants -
Given a day of the week as input, print the date (having the same day of the week as the input) which is closest to today.
We can do this using LocalDate, DayOfWeek and TemporalAdjuster.
The logic is -
Convert the input day of week to an instance of DayOfWeek.
If today is the same day of week as the input, print today's date and stop, else proceed to the next steps.
Get the date of the same day of the week in the previous week.
Get the date of the same day of the week in the next week.
Check which day is closer to today by using .toEpochDay().
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
public static void main(String[] args) {
String inputDayOfWeekString = "SUNDAY";
DayOfWeek inputDayOfWeek = DayOfWeek.valueOf(inputDayOfWeekString);
LocalDate today = LocalDate.now();
if (today.getDayOfWeek().equals(inputDayOfWeek)) {
System.out.println(today);
} else {
LocalDate sameDayNextWeek = today.with(TemporalAdjusters.next(inputDayOfWeek));
LocalDate sameDayPreviousWeek = today.with(TemporalAdjusters.previous(inputDayOfWeek));
LocalDate dateCloserToToday = (sameDayNextWeek.toEpochDay() - today.toEpochDay()) < (today.toEpochDay() - sameDayPreviousWeek.toEpochDay()) ? sameDayNextWeek : sameDayPreviousWeek;
System.out.println(dateCloserToToday);
}
}
Assuming that you want to find the closest day from today that has a specific day of week, one way to do this is to compute both the next and previous day from today that has that day of week, and compare them:
private static LocalDate closestDOW(DayOfWeek dow) {
LocalDate today = LocalDate.now();
LocalDate next = today.with(TemporalAdjusters.nextOrSame(dow));
LocalDate previous = today.with(TemporalAdjusters.previousOrSame(dow));
if (ChronoUnit.DAYS.between(today, next) < ChronoUnit.DAYS.between(previous, today)) {
return next;
} else {
return previous;
}
}
Alternatively, work out whether the next such day is at most three days away. If it is, then it is closer than the previous such day.
private static LocalDate closestDOW(DayOfWeek dow) {
LocalDate today = LocalDate.now();
int daysDiff = today.getDayOfWeek().getValue() - dow.getValue();
int daysUntilNextDOW = daysDiff >= 0 ? 7 - daysDiff : -daysDiff;
if (daysUntilNextDOW <= 3) {
return today.plusDays(daysUntilNextDOW);
} else {
return today.with(TemporalAdjusters.previousOrSame(dow));
}
}

Java Constructor date set

how to set DATE using constructor?
public Date(int day, int month, int year)
{
this.day=day; // set day to 1-31
this.month=month; // set month to 1-12
this.year=year; // set year to 1900-9999
}
if i use
if(day<=1 || day >=31)
this.day=day;
else if(month<=1 || month>=12)
this.month=month;
else if(year<=1900 || year>=9999)
this.year=year
the problem is if I do this it will only result to 0 however if I removed the conditional statements the day will accept until 32 and months will be accept 13 up and so on
Well, you tell me. What do you want to happen? Here are some common options:
(recommended): Decree that a given date that clearly cannot exist, is not a legal invocation of the constructor. In other words, new Date(32, 2, 2051) isn't valid. To do that, throw an exception. For example:
if (day > 31) throw new IllegalArgumentException("Days must be between 1 and 31, inclusive");
Use rollover behaviour, and round off years. This is a 'I don't care it makes no sense do SOMETHING and just don't crash' attitude, and is not recommended. For example, you'd make that date as above act like march 4th, 2051.
Something else of your choosing. You are the programmer, after all.
Note that you are reinventing a very common wheel, and thus, unless this is homework or a pure learning exercise ('pure' in the sense of: This code will be tossed in the garbage once you have completed it), it is a mistake. use LocalDate instead.
NB: Dates are HARD. Harder than you think.
You don't have to create date attributes for holding date information. You can just use either java 8 LocalDate or Date API . LocalDate is thread safe and immutable, most recommended way of handling date use cases.
If you insist on creating your own Date class you can just easily do the following.
import java.time.*;
public class Date {
final LocalDate date;
public Date(int day, int month, int year) {
date = LocalDate.of(year, month, day);
}
public int getYear() {
return date.getYear();
}
public Month getMonth() {
return date.getMonth();
}
public int getDay() {
return date.getDayOfMonth();
}
}
Using the Date class.
public class Main {
public static void main(String[] args) {
try{
Date date1 = new Date(2020, 13, 33);
} catch(DateTimeException ex){
System.out.println("Invalid input");
}
}
}

Using the Comparable interface to sort through dates

I'm trying to write a simple program that will sort the order of given dates, by sorting from earliest date to latest.
I'm able to sort the dates by their years, however when 2 dates have the same year and I need to sort by month, it starts having problems. I've been trying to nest if-statements and tried implementing while loops but I can't quite seem to get it right. I know that in my if statements, I'm missing some kind of statement that tells java to sort by month < other.month and day < other.day but I can't quite get it right...
Input/Output currently:
[10/5 1999, 19/5 1999, 10/3 1999, 19/3 1999, 10/5 2000, 19/5 2000, 10/3 2000, 19/3 2000]
[10/3 1999, 10/3 2000, 19/3 1999, 10/5 1999, 10/5 2000, 19/5 1999, 19/3 2000, 19/5 2000]
class Date implements Comparable<Date> {
private int year;
private int month;
private int day;
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
#Override
public int compareTo(Date other) {
if(year < other.year) {
return Integer.compare(this.year, other.year);
}
if(year == other.year) {
return Integer.compare(this.month, other.month);
}
if(month == other.month) {
return Integer.compare(this.day, other.day);
}
return day;
}
public String toString() {
return day + "/" + month + " " + year;
}
}
You're not checking all cases for years, year < other.year should be year != other.year In addition to that there's some other issues. What you want to be doing is:
if years aren't same
return sort by year
else, if months aren't same
return sort by months
else
return sort by days
Coding comparison for three fields is error-prone. To minimize the risk of bugs use the comparingInt and thenComparingInt methods of the Comparator interface, like Aomine already mentioned in a comment:
private static final Comparator<Date> dateComparator
= Comparator.comparingInt((Date d) -> d.year)
.thenComparingInt(d -> d.month)
.thenComparingInt(d -> d.day);
#Override
public int compareTo(Date other) {
return dateComparator.compare(this, other);
}
Even better, provide getters for the fields and use Date::getYear instead of (Date d) -> d.year and similarly for month and day.
The advantage is not so much that it’s shorter. The greatest advantage is it’s pretty hard to get wrong.

Unexpected date calculation result

I have a method to view a calendar in Java that calculates the date by year, day of the week and week-number.
Now when I calculates the dates from 2017 everything works. But when I calculates the dates from January 2018 it takes the dates of year 2017.
My code looks like
import java.time.temporal.IsoFields;
import java.time.temporal.ChronoField;
import java.time.LocalDate;
// .....
LocalDate desiredDate = LocalDate.now()
.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 1)
.with(ChronoField.DAY_OF_WEEK, 1)
.withYear(2018);
Which results in 2018-01-02 and it should be 2018-01-01. How is this possible?
The order of invoked methods seems matter.
It you invoke them by descending time-granularity (year, week of week and day of week), you get the correct result :
long weekNumber = 1;
long dayOfWeek = 1;
int year = 2018;
LocalDate desiredDate = LocalDate.now()
.withYear(year)
.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, weekNumber)
.with(ChronoField.DAY_OF_WEEK, dayOfWeek );
System.out.println(desiredDate);
2018-01-01
Note that the problem origin comes from :
.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, weekNumber)
that sets the week number (1 to 53) according to the current year.
The Java LocalDate API cannot adapt this value if then you change the year with .withYear(year) as the week number information is not kept in the LocalDate instance.
You can indeed see in LocalDate implementation that LocalDate instances are defined by only 3 field : year, month and day.
public final class LocalDate
implements Temporal, TemporalAdjuster, ChronoLocalDate, Serializable {
...
private final int year;
/**
* The month-of-year.
*/
private final short month;
/**
* The day-of-month.
*/
private final short day;
...
}
So to be precise, the important thing is that :
.withYear(year) be invoked before
.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, weekNumber);
I want to mention, that there is another Problem(?) with LocalDate.
This Code does also create a wrong result:
int jahr = Integer.parseInt(str[0]);
int woche = Integer.parseInt(str[1]);
LocalDate year = LocalDate.of(jahr, 1, 1);
LocalDate week = year.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, woche);
LocalDate day = week.with(wochentag);
return day;
If you change the creation of the year variable to
LocalDate year = LocalDate.now().withYear(jahr);
the code returns the expected result. It seems as the way you construct a LocalDate matters. I guess the timezone is omitted in the ".of()" version.

How can I return int date,month, year in a single method

I have written a java program which will return date, month and year
(datatype int)
import java.util.*;
class DateTime {
int day,month,year;
GregorianCalendar date = new GregorianCalendar();
public DateTime () {
public int TimeT() {
day = date.get(Calendar.DAY_OF_MONTH);
month = date.get(Calendar.MONTH);
year = date.get(Calendar.YEAR);
// return day;
}
}
I am really confuse how to return day, month and year.
I have to return it, so I can use it many times.
Define a type to encapsulate the three attributes, and change method return type to that class, like this:
public class YearMonthDay {
private final year;
private final month;
private final day;
public int getYear() {return year;}
public int getMonth() {return month;}
public int getDay() {return day;}
public YearMonthDay(int y, int m, int d) {
year = y;
month = m;
day = d;
}
// Consider overriding toString, and optionally hashCode and equals
}
Now you can change your class to return YearMonthDay:
public YearMonthDay TimeT(){
return new YearMonthDay(
date.get(Calendar.YEAR)
, date.get(Calendar.MONTH)
, date.get(Calendar.DAY_OF_MONTH)
);
}
A caller can now obtain the YearMonthDay once, and then access its properties from multiple places.
I think I may see what you are trying to say. 'Braj' had the right idea in the code he offered. However, I am not sure if that fully answers your needs or not. I am unsure as to whether you want to return each value (day, month, year) separately, or all together using one method? I will try my best regardless.
Below I will show you the code for DateTime:
import java.util.GregorianCalendar;
class DateTime {
private int day, month, year;
GregorianCalendar date = new GregorianCalendar();
public DateTime() {
day = date.get(GregorianCalendar.DAY_OF_MONTH);
month = date.get(GregorianCalendar.MONTH) + 1;
year = date.get(GregorianCalendar.YEAR);
}
public DateTime(int day, int month, int year){
this.day = day;
this.month = month;
this.year = year;
}
// Accessors ///
public int getDay(){
return day;
}
public int getMonth(){
return month;
}
public int getYear(){
return year;
}
// Mutators ///
public int setDay(){
return day;
}
public int setMonth(){
return month;
}
public int setYear(){
return year;
}
// String ///
public String toString(){
return getMonth() + "/" + getDay() + "/" + getYear();
}
}
Now to explain.
I made the day, month, and year private to begin with and kept your default constructor practically the same except for one thing.
I added 1 to the month because months January - December are indexed at 0 - 11 rather than 1 - 12. This means your month wouldn't follow conventional means. I never knew something such as the GregorianCalendar existed in java's import libraries, so I was initially confused for a couple of seconds when I tested my code.
Moving on I made an additional constructor in case you ever wanted to input your own dates for whatever reason. If you do not plan on making separate DateTime objects to hold random dates, then this is completely unnecessary.
Afterwards I made several accessors - getDay(), getMonth(), getYear() - in case you ever needed to grab the specific aspect of the date.
To finish it off I made a toString() method to print out your DateTime's day month and year all in one String in a conventional format.
If you don't want to change the individual aspects of the date (such as the day, the month, or the year) I'd recommend getting rid of the 'Mutators'. If you don't want to access the individual aspects of the date, then I'd recommend getting rid of the 'Accessors'.
NOW, if for some reason, you want to be able to edit and access the date's components inside your CLIENT, then you should not make the instances of day, month, and year private, but instead public.
Here is the code for the DateTime class that will allow you to change the values inside the CLIENT. (I don't recommend this. I suggest you get used to making accessors and mutators instead.) I decided to call it DateTime2:
import java.util.GregorianCalendar;
class DateTime2 {
public int day, month, year;
GregorianCalendar date = new GregorianCalendar();
public DateTime2() {
day = date.get(GregorianCalendar.DAY_OF_MONTH);
month = date.get(GregorianCalendar.MONTH) + 1;
year = date.get(GregorianCalendar.YEAR);
}
public DateTime2(int day, int month, int year){
this.day = day;
this.month = month;
this.year = year;
}
public String toString(){
return month + "/" + day + "/" + year;
}
}
I left the second constructor in there in case you still want to make separate DateTime2 objects with different dates...
And here if the client code that tests the two classes out, followed by the output:
CLIENT:
public class DateTime_CLIENT{
public static void main(){
DateTime dt = new DateTime();
System.out.println("- DateTime -");
System.out.println("Date: " + dt);
System.out.println("Month: " + dt.getMonth());
System.out.println("Day: " + dt.getDay());
System.out.println("Year: " + dt.getYear() + "\n");
DateTime2 dt2 = new DateTime2();
System.out.println("- DateTime2 -");
System.out.println("Date: " + dt2);
System.out.println("Month: " + dt2.month);
System.out.println("Day: " + dt2.day);
System.out.println("Year: " + dt2.year + "\n");
dt2.day = 400000;
System.out.println("- DateTime2 - CHANGE FROM CLIENT");
System.out.println("Date: " + dt2);
System.out.println("Month: " + dt2.month);
System.out.println("Day: " + dt2.day);
System.out.print("Year: " + dt2.year);
}
}
OUTPUT:
- DateTime -
Date: 7/16/2014
Month: 7
Day: 16
Year: 2014
- DateTime2 -
Date: 7/16/2014
Month: 7
Day: 16
Year: 2014
- DateTime2 - CHANGE FROM CLIENT
Date: 7/400000/2014
Month: 7
Day: 400000
Year: 2014
Hope this helped! ;D
Create different method for each one
public int getDay(Date date){
return date.get(Calendar.DAY_OF_MONTH);
}
public int getMonth(Date date){
return date.get(Calendar.MONTH);
}
public int getYear(Date date){
return date.get(Calendar.YEAR);
}
Create a class out of the three fields and return an object of this type.
Java can't actually return multiple values with a method. However, you can have your method return a string with the necessary information, then parse the string. Alternatively, create a daymonthyear object with all of the necessary attributes and return the object.
Use a HashMap:
public HashMap<String, Integer> TimeT() {
HashMap<String, Integer> dateMap = new HashMap<String, Integer>();
dateMap.put("day",date.get(Calendar.DAY_OF_MONTH));
dateMap.put("month",date.get(Calendar.MONTH));
dateMap.put("year",date.get(Calendar.YEAR));
return dateMap;
}
String In Standard Format, ISO 8601
If you want to return the date information as text, use the Joda-Time library or the new java.time package in Java 8 to convert a date-time value into a String using the format defined by the ISO 8601 standard.
Those two libraries use ISO 8601 format by default. Such as 2014-07-13T18:02:49+05:30:
String output = DateTime.now().toString(); // YYYY-MM-DDTHH:MM:SS.SSS+-00:00:00 format.
But it is best to specify a desired time zone rather than rely implicitly on the JVM's default time zone.
DateTimeZone timeZone = DateTimeZone.forID( "America/Montreal" );
String output = DateTime.now( timeZone ).toString();
If you want just year, month, and date only, then use an alternate formatter rather than the default one. The following code produces a string like 2014-07-13.
DateTimeZone timeZone = DateTimeZone.forID( "America/Montreal" );
DateTime now = DateTime.now( timeZone );
DateTimeFormatter formatter = ISODateTimeFormat.date(); // YYYY-MM-DD formatter.
String output = formatter.print( now );
Pass DateTime Object
If instead of a string, you want the number values communicated to another object, then just pass the Joda-Time DateTime object. The receiving object can call methods on the DateTime such as getYear, getMonthOfYear, and getDayOfMonth. As a bonus, the receiving object will have additional information in its hands such as time zone.
The other answers are correct for the general case: Create a class or use a Collection to communicate multiple values as Java can return only a single item from a method. But in the specific case of date-time values, it seems silly to invent a class when we already have such classes at our disposal.
Avoid java.Util.Date and .Calendar
The java.util.Date and .Calendar classes are notoriously troublesome. Avoid them. Using Joda-Time as a replacement is quite common. The new java.time package in Java 8 is another replacement, and was inspired by Joda-Time.
LocalDate Class
Both Joda-Time and java.time offer a LocalDate class to represent the date-only without time-of-day and without time zone. You may want to use this class. Usually a date-time is better if doing any comparisons across time zones, as a day’s beginning and ending are defined by a time zone.
Best case is make a class with members year, month and day, initialized in the method an returned.
If only and int have to be used you could merge the data with bit operations.
year | (month << 16) | (day << 24)
Don't known if it's valid java.
Reading would be:
year = date & 0xffff;
month = (date >> 16) & 0xff;
day = (date >> 24) & 0xff;
This is more common in native language.
It saems like the Calendar class already has everything the OP requires. There is no need to place the returned values into a hashmap or array or anything else. In the OP code, there is no return statement, but he wants to be able to re-use the code. Below is a simple method that demonstrates what the Calendar class gives us. Any method that returns a Calendar object is giving us everything we need because we can pull out the required values using get.
public static void main(String[] args) {
int day,month,year;
GregorianCalendar date = new GregorianCalendar();
day = date.get(Calendar.DAY_OF_MONTH);
month = date.get(Calendar.MONTH);
year = date.get(Calendar.YEAR);
System.out.println("Day:" + day);
System.out.println("month:" + month);
System.out.println("year:" + year);
}
Keep it simple. Return either the Date object (it contains everything you need) or an ISO string corresponding to this date in order to rebuild it later.

Categories

Resources