I have to validate the date,For example If I enter 31/2/2013,It should give an error as February doesn't contains 31 days,but I am stuck at how to achieve that,I tried to use the switch statement but still in vain.Help would be much appreciated.
public class Date
{
private int PDay;
private int PMonth;
private int PYear;
public Date(int day,int month,int year)
{
setDay(day);
setMonth(month);
setYear(year);
}
public Date(int month,int year)
{
this(1,month,year);
}
public Date(int year)
{
this(1,1,year);
}
public void setDay(int day)
{
PDay=day;
}
public int getDay()
{
return PDay;
}
public void setMonth(int month)
{
if(month>=1 && month<=12)
PMonth=month;
else
System.out.println("Month Invalid-Must be Between 1 & 12");
}
public int getMonth()
{
return PMonth;
}
public void setYear(int year)
{
if(year>=1950 && year<=2049)
PYear=year;
else
System.out.println("Year Invalid-Must be Between 1950 & 2049");
}
public int getYear()
{
return PYear;
}
public String toString()
{
return PDay+"/"+PMonth+"/"+PYear;
}
}
P.S. Its not Homework :P/>
Test Program is:
public class DateTest
{
public static void main(String[] args)
{
Date newDate = new Date(7,14,2012);
Date newDate1 = new Date(2152);
System.out.println(newDate);
System.out.println(newDate1);
}
}
In the constructor, set the day after the year and month:
public Date(int day, int month, int year) {
setYear(year);
setMonth(month);
setDay(day);
}
And in the method setDay() add the validation logic:
public void setDay(int day) {
if (pMonth == 1 && (day < 1 || day > 31)) {
// throw exception or handle error
throw new IllegalArgumentException("invalid number of days");
} else if (pMonth == 2 && (day < 1 || day > 28)) {
// same thing
}
// ... rest of validations
PDay = day;
}
If you want to be even more strict, you can use the year to determine if February has 28 or 29 days.
EDIT: Don't implement date handling on your own as long as there are existing frameworks fitting your needs.
But beware of the pitfalls of some of the existing frameworks: (end of EDIT)
Don't use java.util.Date for such checks. Nearly all of the methods of Date are buggy and have been declared as deprecated. Those setters and getters have been reimplemented in the java.util.Calendar classes. But since they are still buggy in some circumstances the best choice would be to use the new Date Time API of Java 8 if this is an option or to use the "third-party" Jodatime library for correct date handling.
If you just want to have some checks on input, then you may use java.text.SimpleDateFormat that handles the parsing and the checks.
Related
Im trying to make 3 constructors to put into someone elses code. Im getting 2 weird errors. 1. its giving me a duplicate error when the names are different and 2. Its outputting only zeros even when there is user input to say other wise. Im assuming its my constructors but Im not sure. EDIT: Ive added the rest of the code for reference
public class MyDateChoice {
private static final String[] monthNames = {"January", "February",
"March", "April", "May", "June", "July", "August",
"September", "October", "November", "December"};
private static final int[] monthDays = {31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31};
private int day; // day of the month
private int month; // month in the year
private int year; // year
public MyDateChoice () {
this (0,0,0);
}
public MyDateChoice (int month, int day, int year) {
}
public MyDateChoice (String monthName, int day, int year) {
}
public MyDateChoice(int ddd, int year) {
if (year < 0) { // validate year
throw new IllegalArgumentException("year must be > 0");
}
if (ddd < 1 || ddd > 366) { // check for invalid day
throw new IllegalArgumentException("day out of range");
}
this.year = year;
int dayTotal = 0;
for (int m = 1;
m < 13 && (dayTotal + daysInMonth(m, year)) < ddd; ++m) {
dayTotal += daysInMonth(m, year);
this.month = 1;
}
this.day = ddd - dayTotal;
}
// Set the day
public void setDay(int day) {
if (day < 1 || day > daysInMonth(month, year)) {
throw new IllegalArgumentException("day out of range for current month");
}
this.day = day;
}
// Set the month
public void setMonth(int month) {
if (month <= 0 || month > 12) { // validate month
throw new IllegalArgumentException("month must be 1-12");
}
this.month = month;
}
// Set the year
public void setYear(int year) {
if (year < 0) { // validate year
throw new IllegalArgumentException("year must be > 0");
}
this.year = year;
}
// return Date in format: mm/dd/yyyy
public String toString() {
return String.format("%d/%d/%d", month, day, year);
}
// return Date in format: MonthName dd, yyyy
public String toMonthNameDateString() {
return String.format(
"%s %d, %d", monthNames[month ], day, year);
}
// return Date in format DDD YYYY
public String toDayDateString() {
return String.format("%d %d", convertToDayOfYear(), year);
}
// Return the number of days in the month
private static int daysInMonth(int month, int year) {
return leapYear(year) && month == 2 ? 29 : monthDays[month - 1];
}
// test for a leap year
private static boolean leapYear(int year) {
if (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)) {
return true;
}
else {
return false;
}
}
// convert mm and dd to ddd
private int convertToDayOfYear() {
int ddd = 0;
for (int m = 1; m < month; ++m) {
if (leapYear(year) && m == 2) {
ddd += 29;
}
else {
ddd += monthDays[m -1];
}
}
ddd += day;
return ddd;
}
// convert from month name to month number
private static int convertFromMonthName(String monthName) {
for (int subscript = 0; subscript < 12; subscript++) {
if (monthName.equals(monthNames[subscript])) {
return subscript + 1;
}
}
throw new IllegalArgumentException("Invalid month name");
}
}
For the first issue, The constructor does not really care about the name of the variables as they are aliases. It only cares about the type of the input parameters, in this case both methods have two int inputs.
For the second issue, it seems for now you have implemented the default constructor which simply initializes all your members to zero. That's the reason why you are getting everything as zero (I am guessing this is the one only used for initialization of your new objects for now)
Design tips:
Rather than having setter for every member, it is a good practice to follow the builder pattern (https://dzone.com/articles/design-patterns-the-builder-pattern)
Rather than having many constructors with different parameter types you can use generic methods (https://docs.oracle.com/javase/tutorial/extra/generics/methods.html) or even simpler way is to use conversions. To elaborate this further:
This is what you have:
public MyDateChoice (int month, int day, int year) {
}
public MyDateChoice (String monthName, int day, int year) {
}
What you can do is create a static method that converts the month name from string to int, and this allows you to eliminate the second constructor:
public MyDateChoice (int month, int day, int year) {
}
public static int getMonthNameInt(Stringt monthName) {
}
Is it bad to have one variable depend on the state of another variable?
For example, I have a class Date which has the attributes day, month, and year. Month and year can be independently validated in their respective setters, but day depends on month and / or year i.e. maximum number of days during a month / leap year. My approach is to ensure that my constructor requires all three fields, and it will call the setter for day after month and year.
Is a state dependency like this discouraged? My class is not immutable, so I need some sort of validation, and I want to encapsulate the validation in the class itself rather than do it externally.
Below is my current code :
class Date {
private int day;
private int month;
private int year;
public String toString() {
return String.format("%02d/%02d/%04d", month, day, year);
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
// Check if month is valid
if (month < 1 || month > 12) {
throw new IllegalArgumentException(
String.format("%02d is not a valid month", month)
);
} else {
this.month = month;
}
}
public int getYear() {
return year;
}
public void setYear(int year) {
// Check if year is valid
if (year < 1900 || year > 2020) {
throw new IllegalArgumentException(
String.format("%04d is not a valid year", year)
);
} else {
this.year = year;
}
}
public int getDay() {
return day;
}
public void setDay(int day) {
// Check if day is valid
if (day < 1 || day > getMaxNumDaysInMonth(this.getMonth(), this.getYear())) {
throw new IllegalAccessException(
String.format("%02d is not a valid day", day)
);
} else {
this.day = day;
}
}
Date(int month, int day, int year) {
setMonth(month);
setYear(year);
setDay(day);
}
private final int[] MAX_MONTH_DAYS = {
0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
/**
* Gets the maximum number of days in a month
* Depends on the month and whether the year is a leap year
* #param month
* #param year
* #return
*/
private int getMaxNumDaysInMonth(int month, int year) {
// Check if date is valid
if (year < 1 || month < 1 || month > 12) {
throw new IllegalArgumentException(
String.format("%04d-%02d is not a valid date", year, month)
);
} else {
// Adjust February if year is a leap year
if (month == 2) {
if (isLeapYear(year)) {
return MAX_MONTH_DAYS[month];
} else {
return MAX_MONTH_DAYS[month] - 1;
}
} else {
return MAX_MONTH_DAYS[month];
}
}
} // end getNumDaysInMonth
/**
* Returns true if year is a Leap year
* Returns false otherwise
* #param year
* #return
*/
private boolean isLeapYear(int year) {
return (year % 4) == 0 && (year % 100 !=0 || year % 400 == 0);
}
} // end Date class
Argument validation is always a great idea, there are actually classes dedicated to do this kind of thing for you as well to make it even easier.
Preconditions
public void setMonth(int month) {
Preconditions.checkArgument(month >= 1 && month <= 12, String.format("%s is not a valid month.", month));
this.month = month.
}
On an unrelated note regarding mutability
If you want this class to be immutable but still allow for methods like setMonth you can always make the fields final and return a new Date with the new month for example.
class Date {
private final int month;
private final int day;
private final int year;
// constructor
public Date setMonth(int month) {
// check argument is valid
return new Date(day, month, year);
}
}
Date date = new Date(2020, 3, 7);
date = date.setMonth(4);
Did you know this has already been done?
Do you need to create a class that manages Date? This already exists, and has been vastly improved in the last few years.
LocalDate localDate = LocalDate.now();
localDate = localDate.withMonth(4).withDayOfMonth(8).withYear(2021);
LocalDateTime localDateTime = LocalDateTime.now();
localDateTime = localDateTime.withHour(9).withMinute(45).withSecond(30).withDayOfMonth(8);
ZonedDateTime zonedDateTime = ZonedDateTime.now();
zonedDateTime = zonedDateTime.withZoneSameLocal(ZoneId.of("UTC"));
As mentioned by #Jason consider creating an immutable class. Right now your implementation is not safe. Users can break your class invariant by using different 'set' methods on already existing instance. For example, create a date of January, 31. Then set month to February. Because you only check for valid month there the resulting date will be 31 of February. Same for 29 Feb 2020 and change year to 2019.
If your class is immutable then you just need to place all validity checks inside a constructor and don't need to worry about preserving the class invariants in all 'set' methods.
Interesting question. Here's my thought: your object really only has one state variable: a date. That you must set that state variable by providing three separate numbers is a detail of your implementation.
Your validation should reject any attempt to set the state variable to some bogus value.
If you had a class with more than one state variable, it would be helpful to your users to validate them independently. But not for a date.
I'm trying to figure out, how to do input validation of text field/s when I need for example a date input. It needs to be in format like 31.8.2016 and it has to be today's date or later. So that's two different checks.
Only way I can think off is creating boolean method which will check whether the date is past or future. But I would need to create a new method to check if it's in the right format which will be harder but I think this is not the right approach.
I usually create a date class which looks like this:
public class Date{
private boolean validDay = false;
private boolean validMonth = false;
private boolean validYear = false;
private int day;
private int month;
private int year;
private Calendar cal = Calendar.getInstance();
public Date(int day, int month, int year){
/*
*All of these conditions can also be individually used to check the conditions you
*want to check. simply create 3 texfields and put them in a HBox all values must be entered
*separately !
*/
if(day>=cal.get(Calendar.DAY_OF_MONTH) && day<=cal.getActualMaximum(Calendar.DAY_OF_MONTH)){
this.day = day;
this.validDay = true;
}
else{
//TODO: handle
}
if(month>=cal.get(Calendar.MONTH) && month<=12){
this.month = month;
this.validMonth= true;
}
else{
//TODO: handle
}
if(year>=cal.get(Calendar.YEAR)){
this.year = year;
this.validYear = true;
}
else{
//TODO: handle
}
}
public boolean isDateValid(){
return validDay && validMonth && validYear;
}
public String getDate(){
return day+"."+month+"."+year;
}
public int getDay() {
return day;
}
public int getMonth() {
return month;
}
public int getYear() {
return year;
}
}
you could split the input with a String splitter and crate date!
you could also add set methods!
there are many ways to do this!
I hope this helps!
Any idea why my code won't show the result? Maybe I messed up something, just new to programming by the way.
The goal of the program is to see if the date given by the main class is valid or not, it's not that specific on details such as the month of February and leap years so its pretty simple.
This is my DataRec.java :
public class DateRec {
int month, day, year;
boolean good;
public DateRec (){
month = 1;
day = 1;
year = 2008;
good = true;
}
public DateRec (int setMonth, int setDay, int setYear){
month = setMonth;
day = setDay;
year = setYear;
}
public void validate (){
if ((month < 0) || (month > 12)){
good = false;
if (year == 0)
good = false;
if ((day < 0) || (day > 31))
good = false;
}
}
#Override
public String toString() {
if (good = true) {
return String.format("%dd/%dd/%dddd", month, day, year);
} else {
return String.format("%dd/%dd/%dddd", month, day, year);
}
}
}
This is my main class DataRecTest.java:
public class DateRecTest {
public static void main (String[] args){
DateRec today = new DateRec(1,2,2014);
DateRec anyDay = new DateRec();
DateRec noDay = new DateRec(13,31,2014);
anyDay.validate();
today.validate();
noDay.validate();
today.toString();
anyDay.toString();
noDay.toString();
}
}
Default value of boolean (good) is false so you need to initialized good in constructor or validate() function i.e
good = true; // in constructor or validate function
To see the results you need to print them.
//To print in java use System.out.print() or println();
System.out.println(today.toString());
//or simply
System.out.println(today); // println will call today.toString() internally
[EDIT]
Since you are storing result in good I recommend the following:
public DateRec (int setMonth, int setDay, int setYear){
month = setMonth;
day = setDay;
year = setYear;
good = true;
validate();
}
Now you don't need to call validate() every-time; just check the value of good
your validate() is wrong.... It will always be set t false (default value) in whenever you use the 3- argument constructor. You are not setting it to true when date is correct.
change it to,
public void validate (){
if ((month <= 0) || (month > 12) || (year==0) || (day<=0) || (day>31)){
good = false;
}
else
{
good=true;
}
I'm new to programming (doing a course on Computer Science) and one of the exercises is to have the program read a date and then print the next 30 days over and over until the end of the year.
Problem is, there are restrictions. I cannot use Date/Calendar classes, only the Scanner class. So I'm having some trouble getting the dates right... so the only way I've found is to use Switch and have a case for each month, but then there's the issue with the 30/31-day months and leap years. So the days are not the same.
Is there a simpler way to do it?
If you can't use date/calendar classes, then the question is aimed at getting you to do the kind of things that date/calendar classes do internally. I wouldn't be surprised to see switch statements as part of your answer. You will need to teach your implementation knows how many days are in a month, which years are leap years etc.
You could use something like this,
Main class:
public class AppMain {
public static void main(String[] args) {
MyDate initDate = new MyDate(14, 1, 2012);
printMyDate(initDate);
MyDate newDate = initDate;
do{
newDate = DateIncrementator.increaseDate(newDate, 30);
printMyDate(newDate);
}while(newDate.getYear() == initDate.getYear());
}
private static void printMyDate(MyDate date){
System.out.println("[Day: "+ date.getDay() + "][" + "[Mont: "+ date.getMonth() + "][" + "[Year: "+ date.getYear() + "]");
}
}
DateIncrementator class:
public class DateIncrementator {
private static final int[] MONTH_DAYS_NOP_LEAP_YEAR = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
public static MyDate increaseDate(MyDate date, int numberOfDays){
if(numberOfDays < 1 || numberOfDays > 30){
throw new IllegalArgumentException("numberOfDays must have a value between 1 and 30");
}
int numberDaysCurrentMonth = MONTH_DAYS_NOP_LEAP_YEAR[date.getMonth() - 1];
if(date.getMonth() == 2 && isLeapYear(date.getYear())){
numberDaysCurrentMonth++;
}
int newDay = date.getDay();
int newMonth = date.getMonth();
int newYear = date.getYear();
newDay += numberOfDays;
if(newDay > numberDaysCurrentMonth){
newDay = newDay % numberDaysCurrentMonth;
newMonth++;
}
if(newMonth > 12){
newMonth = 1;
newYear++;
}
return new MyDate(newDay, newMonth, newYear);
}
private static boolean isLeapYear(int year){
if(year % 4 != 0){
return false;
}
if(year % 100 != 100){
return true;
}
if(year % 400 == 0){
return true;
}else{
return false;
}
}
}
MyDate class:
public class MyDate {
private int day; // 1 to 31
private int month; // 1 to 12
private int year; // 0 to infinite
public MyDate(int day, int month, int year) {
this.day = day;
this.month = month;
this.year = year;
}
public int getDay() {
return day;
}
public int getMonth() {
return month;
}
public int getYear() {
return year;
}
}