AlThePal78
AlThePal78

Reputation: 1122

Java Checking number months

I think I figure out what question I am trying to ask I dont want get(Calendar.MONTH) I want the month not to be greater than the last caldendar month that is why i am doing month -1, i realize that if i use get(calendar.MONTH) it is getting month of november I just want to see check and make sure its not a greater then the december to us 12 to the computer 11. that is why every other month is invalid that is the question I am trying to get an answer for!?

public Date(String inString)  
{
    int month;// is define by cutting up the inString like this
    int day; // same as month
    int getSlash;

    getSlash = inStr.indexOf('/');
    month = Integer.parseInt(inStr.substring(0,getSlash));
    day = Integer.parseInt(inStr.substring(getSlash + 1));

    inStr = String.format("%02d/%02d%n",month,day);// padformatting string
    inStr=  new SimpleDateFormat("MM/dd").format(new SimpleDateFormat("MM/dd").parse(inStr));// checking to see if a actualdate

    GregorianCalendar cal = new GregorianCalendar();

    // this is what I don't understand after reading

    if (month -1 > cal.get(Calendar.MONTH ) // how do I get it to say int of the month if the user types in 12 it gets invalid
    {
        System.out.Println("Invalid Month -> " + month");
    }

}

when I do this all but month 11 is considered not vailid any one know why? I can not figure it out.

Upvotes: 0

Views: 5062

Answers (6)

Manua Kuy
Manua Kuy

Reputation: 71

java.time

For new and old readers to this question I strongly recommend that since Java 8 you use java.time, the modern Java date and time API, for your date work. The classes Date, SimpleDateFormat, GregorianCalendar and Calendar that you were trying to use were troublesome and are fortunately long outdated. So nowadays avoid them.

So it’s about time this question gets answers that demonstrate the use of java.time. There is a good one by Basil Bourque. And here’s my shot.

Interpretation of your question and assumptions

I know that the moderators and some users don’t like reservations and disclaimers like this section. So this section risks getting deleted. They say I should instead ask for clarification in comments. I’m not sure it works with a 15 years old question that nevertheless still has readers. So I understand from your question that you want a method that does two things:

  1. Validates your input string.
  2. Converts it to a Date.

I assume:

  • Your input string should contain a month number and a day of month separated by a slash and nothing else.

  • Month and day should each be in one or two digits without sign. You wrote:

    I want the month not to be greater than the last cal(d)endar month …

    I take this to mean that the parsed month must not be later in the calendar year than the current month (in December you’ll accept January, but not the other way around). So Month should be 1 through current month, and day of month should be 1 through the last day of the month in question. I assume that you want to accept 2/29 since we don’t know whether it is in a leap year. You want to forbid February 30 and April 31.

Validate

Using the comment by @Anonymous under the answer by Basil Bourque:

private static final DateTimeFormatter parser
        = DateTimeFormatter.ofPattern("M/d", Locale.ROOT);

/**
 * Parses and validates month-day string in M/d format.
 * Prints a message if month is after current month.
 * @throws DateTimeParseException if the string is not a valid mointh-day at all.
 */
public static MonthDay parseMonthDay(String inString) {
    MonthDay monthDay = MonthDay.parse(inString, parser);
    int thisMonth = MonthDay.now(ZoneId.systemDefault()).getMonthValue();;
    if (monthDay.getMonthValue() > thisMonth) {
        System.out.format(Locale.ROOT,
                "Invalid month %d (%tB); must not be after current month, %d%n",
                monthDay.getMonthValue(), monthDay, thisMonth);
    }
    return monthDay;
}

Trying it out:

System.out.println(parseMonthDay("2/29"));

Output:

--02-29

Since I am writing this in December, no input will result in the message being printed. At other times of year an example message could be:

Invalid month 12 (Dec); must not be after current month, 11

The method rejects for example 2/30, 0/30, 1/32 and 1/31 and some nonsense. Funnily it accepts 001/031.

You can’t convert to a Date object

As I said, you should not use Date. Unless you indispensably need a Date for a legacy API that you cannot upgrade to java.time just now, that is. But! You basically cannot convert your string to a Date. A Date is a point in time and despite the name cannot represent a date, not to mention a date without a year. What the troublesome old SimpleDateFormat would do would be give us the first moment of the date in its default year of 1970 in the default time zone of the JVM. Since 1970 was not a leap year this implies that 2/29 and 3/1 were both parsed into Sun Mar 01 00:00:00 (your time zone) 1970, that is, you cannot distinguish.

So unless you have specific requirements that I cannot guess, I recommend that you stay with the MonthDay object returned from my method above. If you need a Date and the date is assumed to be in the current year (as the restriction on the month might indicate), you may of course use the current year to decide whether to forbid Feb 29 and then convert to a Date in the current year. If your legacy API accepts this.

What went wrong in your code?

  • Forgive the repetition, you were using the troublesome, old and error-prone classes. That typically leads to buggy code.

  • Your method needs both a return type and a method name, for example:

    public Date parseMonthDay(String inString) throws ParseException {
    

    When using SimpleDateFormat.parse() you also need to declare throws ParseException as shown unless you catch that exception in the method.

  • Since your method doesn’t use anything from the surrounding object, I recommend you declare it static.

  • When the method parameter is declared as inString, you need to use that name in the method body (you cannot refer to it as just inStr).

  • As others have said you should use the built in library to parse the string, not parse it by hand. Since Java 8 the built-in library means java.time. In particular converting your string from M/d to MM/dd format seems just a waste.

  • As I said, you are parsing 2/29 and 3/1 into the same Date.

  • You are not checking for negative numbers or 0 in the input. In my time zone your method just parsed 0/-1 into Sun Nov 29 00:00:00 CET 1970 and did not issue any error message.

  • The right bracket ) of your if statement is inside a comment, so the compiler doesn’t see it.

  • In System.out.println, println must be with a lower case p. In the same statement there is a double quote " too many after month.

  • If your method is to return a Date, you must include a return statement.

Link

Oracle tutorial: Trail: Date Time explaining how to use java.time.

Upvotes: 1

Basil Bourque
Basil Bourque

Reputation: 339362

tl;dr

MonthDay.parse ( "--" + input.replace( "/" , "-" ) ) ;  // Example: --12-31 for last day of year, and --01-01 for first day of year.

Avoid legacy date-time classes

The legacy date-time classes in Java are terribly flawed. Avoid them. Never use GregorianCalendar (replaced by java.time.ZonedDateTime).

java.time

Use only the java.time classes built into Java 8 and later.

Do not chop up and parse your input string. Let java.time do the parsing for you.

MonthDay

Apparently you are working with a month and day-of-month rather than a date. We have a class for that, appropriately named: MonthDay.

ISO 8601

We have an international standard defining textual formats for date-time values: ISO 8601.

The format for a date is year-month-day using a hyphen as delimiter. For a month-day, simply substitute another hyphen for the missing year.

For example, your input might be 10/23 for October 23. To comply with ISO 8601, we can replace the / with a hyphen, then prepend a pair of hyphens.

String input = "10/23" ;
String modified = "--" + input.replace( "/" , "-" ) ;

Then parse as a MonthDay.

MonthDay md = MonthDay.parse( modified ) ;

Tip: Educate the publisher of your data about using ISO 8601 formats rather than inventing new ones.

Detecting invalid inputs

If invalid inputs are a possibility, then trap for a DateTimeParseException. The MonthDay.parse method knows to expect month numbers of 1-12, and day numbers of 1-31 as appropriate per month.

try 
{
    MonthDay.parse ( "--" + input.replace( "/" , "-" ) ) ; 
    …
}
catch ( DateTimeParseException e )
{
    …
}

Sane numbering

You said:

i am doing month -1 … the december to us 12 to the computer 11.

In contrast to the legacy classes, the java.time classes use sane numbering. Months are 1-12 for January-December.

Upvotes: 1

Sarah K
Sarah K

Reputation: 59

I agree with the other answers; use SimpleDateFormat to parse, if the assignment allows you to.

You can also use the Calendar class to do something like this by switching lenient interpretation of values off, then setting the fields, and then forcing the Calendar instance to recompute it's internal counters. This will throw an exception if the field values are inconsistent. Like so:

import java.util.*;

public class Test {
    public static void main(String[] args) {
        Calendar c1=new GregorianCalendar();
        c1.setLenient(false);
        c1.set(2010,1,23);        // should work: Jan. 23, 2010
        c1.getTimeInMillis();     // will revalidate the calendar fields
        System.out.println("OK: "+c1.toString());
        try {
            c1.set(2010,2,35);     // invalid date: Feb. 35, 2010
            c1.getTimeInMillis();  // revalidate... should throw exception!
            System.out.println("OK: "+c1.toString()); // never happens
        } catch(IllegalArgumentException t) {
            System.out.println("FAIL: "+c1.toString());
            t.printStackTrace();
        }
    }
}

Here the calls to getTimeInMillis() force Calendar to revalidate its internal fields; the call will throw IllegalArgumentException if the values are inconsistent with normal Calendar time (i.e. out of range). If you'd left the default of lenient=true enabled, the second call would succeed, but you'd end up with a day in March as Calendar tries to figure out what you meant!

The only real catch is that to do a computation like this properly, you must know what the year is so that Calendar can do leap year calculations for you to get the number of days in February correct.

If for some reason you really don't care about the year, you could just hardcode a year that you know isn't a leap year... like 2009. Be sure to document that you're doing something weird like that though! It's a bit of a hack, and that's often an indication that you're doing something in a less than optimal way!

But, as mentioned in the other answers, either SimpleDateFormat, or a little array-based lookup table is probably the easy and clean answer here.

Hope you find this educational.

Upvotes: -1

Jon Skeet
Jon Skeet

Reputation: 1502086

Parsing dates and times yourself is a bad idea - as is using Java's built-in functionality.

I can't tell exactly what you want to do or what's wrong, but I think it's safe to say that using Joda Time would be a good idea.

Then instead of using get with mystical constants - and adjusting for months being 0-based - you can just use ReadableDateTime.getMonthOfYear() or some other pleasantly-named method. Much nicer.

For instance, to get the current month, you'd use:

int currentMonth = new DateTime().getMonthOfYear();

That uses the ISO calendar system and the current default time zone. Personally I'm not too fond of defaulting the time zone, so you may want:

int currentMonth = new DateTime(DateTimeZone.UTC).getMonthOfYear();

I suspect the ISO calendar system will be fine for you though :)

(As ChssPly76 says, cal.get(Calendar.MONTH) will get you the 0-based month number with your current code, but that's a horrible API in my opinion.)

EDIT: Now you've updated your question, you should diagnose the problem by breaking it down a bit more. Is this a problem in terms of the user input, or comparing with the current month? If it's the "comparing with the current month" bit that's the problem, that's easy to show in a short but complete program which doesn't have all the rest of the stuff around it:

import java.util.*;

public class Test
{
    public static void main(String[] args)
    {
        // It's currently November
        int userInput = 11;

        Calendar cal = Calendar.getInstance();
        int currentMonth = cal.get(Calendar.MONTH) + 1;

        if (userInput == currentMonth)
        {
            System.out.println("Yes, that's the current month");
        }
        else
        {
            System.out.println("No, the current month is " + currentMonth);
        }
    }
}

I've compensated for java.util.Calendar using 0 months by adding one to the value returned rather than subtracting one from the user input, but it's the same basic premise.

Now, if it's getting the user input which is the problem, you don't need to bother with the current month - just check whether you're getting what you expect.

A lot of programming is basically dividing a problem into two pieces. Here you have two issues:

  • Parsing user input
  • Comparing with the current month

Work out which one is the problem, and edit your question to address just that aspect.

Upvotes: 4

OscarRyz
OscarRyz

Reputation: 199264

Judging by these related questions, I think you have a number of problems before that.

You're trying to code something that is already coded using existing libraries. This redundant code is causing conflicts.

You may

A) Use an existing library.

B) Code the solution yourself to learn.

If you go for A you just need.

public Date toDate( String inStr ) {
    return new SimpleDateFormat("MM/dd").parse(inStr);
}

And that's it!

If you go for B, what you need to to is first create an algorithm ( in a paper sheet ) and describe what steps do you need to solve the problem.

like

  • Take the string
  • Split it in part by "/"
  • Convert them to numbers
  • Validate if the first number is between 1 and 31
  • Validate the month ( is in 1 and 12 )
  • If month is 2 then the day must be < 29

etc etc et

Once you have all those steps clearly defined, you may the proceed to the codification. Some of them will translate seamlessly and some other will need more work. For some steps you may create code that is not as good as it could be but it works and for other parts you just won't have a clue.

But if you don't have an algorithm, you'll be hitting walls over and over. And getting problems as the one you're describing.

First make it run in the paper in your own words, later find out how to code it.

Here's a related entry: Process to pass from problem to code: How did you learn?

Upvotes: 1

ChssPly76
ChssPly76

Reputation: 100736

I'm guessing what you want is

cal.get(Calendar.MONTH)

which will return you zero-based index of month within year.

Your question is very unclear, however, and your parsing code seems a bit more involved than it has to be. Perhaps if you'd explained better what you're trying to do, someone would be able to suggest a better approach.

Upvotes: 3

Related Questions