Comparing dates in MMYY format - java

So I am making a Java program that processes a user's fake credit card and I am trying to make an expiration verification conditional statement. However, when I run my program, I get a "String cannot be converted to int" error. I would like to know how should I use the current month and year to check if the date entered by the user has actually expired.
do {
System.out.print("Enter the expiration date mmyy: ");
expiration = expnum.nextInt();
DateFormat dateformat = new SimpleDateFormat("MMyy");
Date date = new Date();
System.out.println(dateformat.format(date));
int currentdate = dateformat.format(date);
if (currentdate <= expiration) {
check = check + 1;
} else {
check = 1;
}
} while (check == 1);

While #ManoDestra's solution will work, I would prefer to keep the types/data sensible. Thus, rather than convert the existing date into a int (which is kind of nonsensical, and the format followed by parse feels nasty), I would prefer to parse the expiration into a date and then compare directly. Something like this:
expiration = expnum.nextInt();
DateFormat dateformat = new SimpleDateFormat("MMyy");
Date expiryDate = dateFormat.parse(expiration);
Date currentDate = new Date();
if (currentDate.isAfter(expiryDate)) {
// card has expired
} else {
// card is still active
}
You'll probably need to tweak this depending on when you think the expiry actually happens. If the expiry is specified as "0816" is that 01-Aug-2016 00:00:00.000, or 31-Aug-2016 23:59:59.999, or some point in between?
It's a call you have to make (probably by looking at the credit card spec), but that's another thing this approach has compared to the int-converting one: it's not just an abstract sense of "using proper types", but it translates to the real world too. By converting the expiration string to a Date, you need to think about exactly what instant in time that represents, and exactly which values of "now" should count as expired and which should not.

YearMonth class
The YearMonth class represents, well, a year and a month.
java.time
This class is part of the java.time framework built into Java 8 and later. These classes supplant the old troublesome date-time classes such as java.util.Date. See Oracle Tutorial. Much of the functionality has been back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.
Parsing a String
Use a DateTimeFormatter to parse your input string. When the century is omitted the 21st century 20 is assumed.
String input = "0616";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "MMyy" );
YearMonth expiration = YearMonth.parse ( input , formatter );
Dump to console.
System.out.println ( "yearMonth = " + yearMonth );
yearMonth = 2016-06
Time Zone
Compare to the current YearMonth. Determining the current year-month means getting the current date. And determining the current date requires a time zone. The Question and other Answers ignore this crucial issue. For any given moment the date varies around the globe by time zone. A few minutes after Paris is a new day while still “yesterday” in Montréal.
If omitted as an optional argument, the JVM’s current default time zone is silently implicitly applied. The current default can change at any time, even during runtime(!). Better to specify the expected/desired time zone.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
YearMonth currentYearMonth = YearMonth.now( zoneId );
If your business rules happen to use UTC as the time zone, pass the handy constant ZoneOffset.UTC as the time zone argument.
Compare
Lastly, we compare using isBefore, isAfter, or equals.
Boolean expired = currentYearMonth.isAfter( expiration );
Rather than passing around strings or integers for the year and month, pass around these YearMonth objects. You get the benefits of type safety, guaranteed valid values, and more self-documenting code.

Compare it as a String in the form yyMM, whose natural ordering is correct:
int expiration; // MMyy
String expirationStr = String.format("%02d%02d", expiration % 100, expiration / 100);
String now = new SimpleDateFormat("yyMM").format(new Date());
if (expirationStr.compareTo(now) < 0)
// expired

It's not an int. It's a string. This is wrong:
int currentdate = dateformat.format(date);
If you really WANT an int, then convert it to an int:
int currentdate = Integer.parseInt(dateformat.format(date));
However, it won't be a terribly sensible int. If you wish to make this a more sensible int for comparison, then you'd have to put the year first, THEN the month (format: yyMM, or yyyyMM). That way, it will be a consecutive numerical comparison that CAN be compared against other credit card dates. Or, you could simply convert it to a Date or Calendar instead and use date comparisons.
Also, you should make the variable, check, a Boolean value. You have no need for it to be an numeric value.

You can replace expnum.nextInt(); with Integer.parseInt(expnum.nextLine()); This will take the input string, and convert it to an int. If they don't put an int though, it will through an exception, so you can put that inside a try-catch statement (and put that in like in a while loop or something so it keeps asking till they answer in the proper format if you want).

Related

DateTime Manipulation in C# vs Java

I'm new to Java. I have a time I am getting from a web-page, this is in the "hh:mm" format (not 24 hour). This comes to me as a string. I then want to combine this string with todays date in order to make a Java Date I can use.
In C#:
string s = "5:45 PM";
DateTime d;
DateTime.TryParse(s, out d);
in Java I have attempted:
String s = "5:45 PM";
Date d = new Date(); // Which instantiates with the current date/time.
String[] arr = s.split(" ");
boolean isPm = arr[1].compareToIgnoreCase("PM") == 0;
arr = arr[0].split(":");
int hours = Integer.parseInt(arr[0]);
d.setHours(isPm ? hours + 12 : hours);
d.setMinutes(Integer.parseInt(arr[1]));
d.setSeconds(0);
Is there a better way to achieve what I want?
Is there a better way to achieve what I want?
Absolutely - in both .NET and in Java, in fact. In .NET I'd (in a biased way) recommend using Noda Time so you can represent just a time of day as a LocalTime, parsing precisely the pattern you expect.
In Java 8 you can do the same thing with java.time.LocalTime:
import java.time.*;
import java.time.format.*;
public class Test {
public static void main(String[] args) {
String text = "5:45 PM";
DateTimeFormatter format = DateTimeFormatter.ofPattern("h:mm a");
LocalTime time = LocalTime.parse(text, format);
System.out.println(time);
}
}
Once you've parsed the text you've got into an appropriate type, you can combine it with other types. For example, to get a ZonedDateTime in the system time zone, using today's date and the specified time of day, you might use:
ZonedDateTime zoned = ZonedDateTime.now().with(time);
That uses the system time zone and clock by default, making it hard to test - I'd recommend passing in a Clock for testability.
(The same sort of thing is available in Noda Time, but slightly differently. Let me know if you need details.)
I would strongly recommend against using java.util.Date, which just represents an instant in time and has an awful API.
The key points here are:
Parse the text with a well-specified format
Parse the text into a type that represents the information it conveys: a time of day
Combine that value with another value which should also be carefully specified (in terms of clock and time zone)
All of these will lead to clear, reliable, testable code. (And the existing .NET code doesn't meet any of those bullet points, IMO.)
To parse the time, you can do as explained in #Jon Skeet's answer:
String input = "5:45 PM";
DateTimeFormatter parser = DateTimeFormatter.ofPattern("h:mm a", Locale.ENGLISH);
LocalTime time = LocalTime.parse(input, parser);
Note that I also used a java.util.Locale because if you don't specify it, it'll use the system's default locale - and some locales can use different symbols for AM/PM field. Using an explicit locale avoids this corner-case (and the default locale can also be changed, even at runtime, so it's better to use an explicit one).
To combine with the today's date, you'll need a java.time.LocalDate (to get the date) and combine with the LocalTime, to get a LocalDateTime:
// combine with today's date
LocalDateTime combined = LocalDate.now().atTime(time);
Then you can format the LocalDateTime using another formatter:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
System.out.println(combined.format(fmt));
The output is:
16/08/2017 17:45
If you want to convert the LocalDateTime to a java.util.Date, you must take care of some details.
A java.util.Date represents the number of milliseconds since 1970-01-01T00:00Z (aka Unix Epoch). It's an instant (a specific point in time). Check this article for more info.
So, the same Date object can represent different dates or times, depending on where you are: think that, right now, at this moment, everybody in the world are in the same instant (the same number of milliseconds since 1970-01-01T00:00Z), but the local date and time is different in each part of the world.
A LocalDateTime represents this concept of "local": it's a date (day, month and year) and a time (hour, minute, second and nanosecond), but without any relation to a specific timezone.
The same LocalDateTime object can represent different instants in time in different timezones. So, to convert it to a Date, you must define in what timezone you want it.
One option is to use the system's default timezone:
// convert to system's default timezone
ZonedDateTime atDefaultTimezone = combined.atZone(ZoneId.systemDefault());
// convert to java.util.Date
Date date = Date.from(atDefaultTimezone.toInstant());
But the default can vary from system/environment, and can also be changed, even at runtime. To not depend on that and have more control over it, you can use an explicit zone:
// convert to a specific timezone
ZonedDateTime zdt = combined.atZone(ZoneId.of("Europe/London"));
// convert to java.util.Date
Date date = Date.from(zdt.toInstant());
Note that I used Europe/London. The API uses IANA timezones names (always in the format Region/City, like America/Sao_Paulo or Europe/Berlin).
Avoid using the 3-letter abbreviations (like CST or PST) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds().
And there's also the corner cases of Daylight Saving Time (when a LocalDateTime can exist twice or can't exist due to overlaps and gaps). In this case, Jon's solution using ZonedDateTime avoids this problem).

Java Convert Interval of Days to Dates

I have a dates in format such as:
- dd. - dd.MM.yyyy
Based on this information I want to receive from date and to date.
Naive implementation in pseudo code is:
Split the date into first part and second part
Create a SimpleDateFormat for second part
Take a look, whether the number in the first part is higher than the number in a second part.
If it is:
Decrement month for the first date
Create from date which will contain the dd from the first part, decremented month from the second part and year from the second part.
If it isn't:
Create from date which will contain the dd from the first part and month and year from second part.
This solution would probably work most of the time, but it feels rather awkward. Isn't there any better solution?
I'd suggest you to check the package
org.joda.time
In particular the following classes
DateTime
Period
java.time
The java.time framework is built into Java 8 and later. Much of that functionality is back-ported to Java 6 & 7 and to Android.
I provide some scraps of example code here, but untested – never run. Should get you going in the right direction.
Two tricky parts to this problem:
YearThe Question says to assign the year. That is not the case for a stop date in January with a start date that turns out to be in previous month, December. You want the previous year in such a case. Solution is to let java.time subtract a month and handle the Jan-to-Dec math for you.
Month lengthVarious months have different lengths, different number of days, obviously. Keep mind that you cannot try to put day-of-month 31 on to month of April. If your input data is always clean and valid, and our algorithm below is correct, this should be a non-issue. Nevertheless, I would certainly add some exception-catching code to my example code below to trap any attempt to form an invalid date.
I'll skip the string-splitting part, and assume you have a number (the day-of-month) from the first part, and a string of the date from the second part.
long dayOfMonth = Long.longValue( "31" );
That date is not in standard format, so we must specify a formatting pattern. A LocalDate represents a date-only value without time-of-day and without time zone.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "dd.MM.yyyy" );
LocalDate stop = LocalDate.parse( "21.05.2016" , formatter );
We can extract the day-of-month to compare.
LocalDate start = null;
int d = localDate.getDayOfMonth();
Do the comparison.
if( dayOfMonth >=d ) { // If start is in previous month…
start = stop.minusMonths( 1 ).withDayOfMonth( dayOfMonth );
} else if (dayOfMonth < d ) { // If start is in same month…
start = stop.withDayOfMonth( dayOfMonth );
} else {
// FIXME: handle impossible condition as error. The 'if' statements are flawed.
}
By the way, the format of this input data is awkward and, frankly, silly. This kind of precious “cleverness” creates extra work, gives opportunity for confusion and errors, is completely needless without providing any benefits, and drives me nuts. If you have any control of this input data I strongly suggest either of two possible changes.
First, if exchanging data within your app, do not use strings. Use objects. Above you have seen the LocalDate object. You could pass those around. Or even define your own class LocalDateRange to house a pair of LocalDate objects. Or see this Question and especially this Answer that talks about using the Google Guava class Range to hold the pair of LocalDate objects.
Secondly, when you must serialize date-time values to strings, use the standard ISO 8601 formats. Such use is simple as the java.time classes by default use these formats when parsing/generating strings. A date-only value should be in YYYY-MM-DD order. A date range interval is a pair of those strings mated with a slash (SOLIDUS) or alternatively a pair of hyphens when a slash is inappropriate (such as file or folder naming within a Unix-related file system).

Printing Time and Date in both Universal Time and Standard Time

Writing a Java application that takes user input into a Time and Date class, but I am not sure how to take this user input and convert it into Universal and Standard time... I have spent multiple hours surfing the web and stack overflow and have not been able to find a solution.
I have hours, minutes, seconds, year, month, day all in separate integer variables and need to display them in Universal and Standard time.
Thanks for taking a look...
There are two solutions:
first is place all of input in the string and parse it:
String dateStr = ""
//put your input in this string in some format/ example:
//dateSttr = year + "." + month + "." + day + " " + hour + ":" + minute;
//It is better to use StringBuilder
DateFormat inputFormat = new SimpleDateFormat("yyyy.MM.dd hh:mm");
//note that hh is 12h-format and HH is 24h-format
DateFormat outputFormat1 = new SimpleDateFormat("your_outputFormat");
DateFormat outputFormat2 = new SimpleDateFormat("your_another_outputFormat");
Date date = inputFormat.parse(dateStr);
String o1, o2;
o1 = outputFormat1.format(date);
o2 = outputFormat2.format(date);
//o1 and o2 is your result.
For the rules, how this formats is done, see javadoc
The second solution is to get a new date and set your parameters:
Calendar cln = Calendar.getInstance().clear();
//by default you get a calendar with current system time
//now set the fields. for example, day:
cln.set(Calendar.YEAR, 2015);
cln.set(Calendar.MONTH, Calendar.FEBRUARY);
cln.set(Calendar.DAY_OF_MONTH, 17);
cln.set(Calendar.HOUR_OF_DAY, 18);//Calendar.HOUR for 12h-format
cln.set(Calendar.MINUTE, 27);
See more about setting calendar in javadoc
Note, that in the second variant, you might have some fields undefiend.
If #JonSkeet 's assumption and mine is correct, you're starting with either UTC or your local time. Displaying it is just a matter of formatting your output.
For the other type of time, you add or subtract a number of hours, which you can find on the web. The tricky part is that this may push you into the next calendar day, or pull you back into the previous one. To deal with that, I figure you want to either
implement an adder for year, month, day, hour--or
convert those to decimal somethings (Excel uses days, for instance, where as I write this it's 42328.08813), shift the value by the appropriate number of hours, and convert it back.
java.time
The Answer by TEXHIK is correct, but outdated. Also, as others mentioned, I do not know what you mean by "Universal and Standard time". But I'll try to get you part way there.
As of Java 8, the old java.util.Date/.Calendar classes have been supplanted by the new java.time framework. The new classes are inspired by the highly successful Joda-Time framework, intended as its successor, similar in concept but re-architected. Defined by JSR 310. Extended by the ThreeTen-Extra project. See the Tutorial.
The ZonedDateTime class has a factory method taking numbers for year, month, and so on.
Plus you must specify a time zone. If your numbers represent a date-time in UTC, use the ZoneOffset.UTC constant. For other time zones, specify a ZoneId object by using a proper time zone name; never use the 3-4 letter codes such as EST or IST as their are neither standardized nor unique.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
// ZoneId zoneId = ZoneOffset.UTC; // ZoneOffset is a subclass of ZoneId.
ZonedDateTime zdt = ZonedDateTime.of( 2015 , 1 , 2 , 3 , 4 , 5 , 6 , zoneId );
zdt: 2015-01-02T03:04:05.000000006-05:00[America/Montreal]
You can convert to UTC or another time zone.
ZonedDateTime zdt_Kolkata = zdt.withZoneSameInstant ( ZoneId.of("Asia/Kolkata") );
ZonedDateTime zdt_Utc = zdt.withZoneSameInstant ( ZoneOffset.UTC );
zdt_Kolkata: 2015-01-02T13:34:05.000000006+05:30[Asia/Kolkata]
zdt_Utc: 2015-01-02T08:04:05.000000006Z
If working with classes not yet updated for java.time, convert to a java.util.Date. First extract a Instant object, a moment on the timeline always in UTC.
java.util.Date date = java.util.Date.from ( zdt.toInstant () );

Date comparison confusion

I have due_date = 2014-05-09 11:36:41.816.
I want to check condition that if today date is same as due_date or 1 day less then due_date then user can renew other wise have to show message that too early to renew.
means if I renew on date 8 then user can do but if user do it on date 7 then he is not allowed and display message.
I know that to check for same day means date 9, i can use :
Timestamp t = new Timestamp(new Date().getTime());
if (t.compareTo(due_date)==0){
//renew book
}
but i don't know that how to do for 1 day before calculation.
So any guidance to do for that.
Decent Date-Time Library
You should be using either Joda-Time or the new java.time in Java 8, as the old java.util.Date and .Calendar classes are notoriously troublesome.
Time Zone
You should not ignore the issue of time zone. Omitting time zone means your JVM's (host computer's) default time zone will apply. Your results will vary.
The definition of a "day" and "yesterday" depends on your particular time zone.
Use a proper time zone name (mostly continent slash city). Avoid the 3 or 4 letter codes as they are neither standardized nor unique.
If your input string has no time zone offset, meaning it is in UTC, then specify using the built-in constant DateTimeZone.UTC.
Interval
Joda-Time offers the Interval class to define a span of time. In your case the span is two days, the due date's day plus the day before. (By the way, both your posted questions and your programming will improve if you work harder at focusing and simplifying your problem as I just did in that preceding sentence.)
Half-Open
Usually in date-time work we use the "half-open" approach to define a span. That means the beginning is inclusive and the ending in exclusive for purposes of comparison. So for your purpose we want to run from the first moment of the day before due date up to, but not including, the first moment of the day *after* due date.
ISO 8601
Your input string is nearly in ISO 8601 standard format. Just replace the SPACE with a T. Joda-Time has built-in parsers for ISO 8601 formats.
Example Code
Example code in Joda-Time 2.3.
String inputDueDateRaw = "2014-05-09 11:36:41.816"
String inputDueDate = inputDueDateRaw.replace( " ", "T" );
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime due = new DateTime( inputDueDate, timeZone ); // Note the time zone by which to interpret the parsing of the string.
DateTime dayBeforeDue = due.withTimeAtStartOfDay().minusDays( 1 ).withTimeAtStartOfDay();
DateTime dayAfterDue = due.withTimeAtStartOfDay().plusDays( 1 ).withTimeAtStartOfDay(); // Half-open. Up to but not including day after.
Interval renewalInterval = new Interval( dayBeforeDue, dayAfterDue );
Test if the current moment is within that interval, using half-open approach to comparison.
boolean isNowEligibleForRenewal = renewalInterval.contains( DateTime.now() );
The actual value a.compareTo(b) returns is meaningless. The only thing you can trust is that if it's positive a is "larger" than b, and if it's negative, a is "smaller". You can't count on its absolute value to determine the difference between the two.
You could, however, just compare the unix time representation of both dates:
TimeStamp due_date = ...;
long dueDateMillis = due_date.getTime();
long t = System.currTimeMillis();
long threshold = 24L * 60L * 60L * 1000L; // One day in milliseconds
if (dueDateMillis - t <= threshold) {
// Renew book
}
Another way to do this is using the Calendar object:
Calendar today = Calendar.getInstance();
today.setTimeInMillis(System.currentTimeMillis()); // time today
Timestamp dueDateTs = new Timestamp(...);
Calendar dueDate = Calendar.getInstance();
dueDate.setTimeInMillis(dueDateTs.getTime());
dueDate.roll(Calendar.DAY_OF_YEAR, false); // to subtract 1 day
if(today.after(dueDate)) {
// do your magic
}

How to generate a Date from just Month and Year in Java?

I need to generate a new Date object for credit card expiration date, I only have a month and a year, how can I generate a Date based on those two? I need the easiest way possible. I was reading some other answers on here, but they all seem too sophisticated.
You could use java.util.Calendar:
Calendar calendar = Calendar.getInstance();
calendar.clear();
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.YEAR, year);
Date date = calendar.getTime();
java.time
Using java.time framework built into Java 8
import java.time.YearMonth;
int year = 2015;
int month = 12;
YearMonth.of(year,month); // 2015-12
from String
YearMonth.parse("2015-12"); // 2015-12
with custom DateTimeFormatter
import java.time.format.DateTimeFormatter;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM yyyy");
YearMonth.parse("12 2015", formatter); // 2015-12
Conversions
To convert YearMonth to more standard date representation which is LocalDate.
LocalDate startMonth = date.atDay(1); //2015-12-01
LocalDate endMonth = date.atEndOfMonth(); //2015-12-31
Possibly a non-answer since you asked for a java.util.Date, but it seems like a good opportunity to point out that most work with dates and times and calendars in Java should probably be done with the Joda-Time library, in which case
new LocalDate(year, month, 1)
comes to mind.
Joda-Time has a number of other nice things regarding days of the month. For example if you wanted to know the first day of the current month, you can write
LocalDate firstOfThisMonth = new LocalDate().withDayOfMonth(1);
In your comment you ask about passing a string to the java.util.Date constructor, for example:
new Date("2012-09-19")
This version of the constructor is deprecated, so don't use it. You should create a date formatter and call parse. This is good advice because you will probably have year and month as integer values, and will need to make a good string, properly padded and delimited and all that, which is incredibly hard to get right in all cases. For that reason use the date formatter which knows how to take care of all that stuff perfectly.
Other earlier answers showed how to do this.
Like
SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM");
Date utilDate = formatter.parse(year + "/" + month);
Copied from Create a java.util.Date Object from a Year, Month, Day Forma
or maybe like
DateTime aDate = new DateTime(year, month, 1, 0, 0, 0);
Copied from What's the Right Way to Create a Date in Java?
The most common sense approach would be to use the Date("YYYY-MM-DD") constructor even though it is deprecated. This is the easiest way to create a date on the fly. Screw whoever decided to deprecate it. Long live Date("YYYY-MM-DD")!!!
Don’t use this answer. Use the answers by Przemek and Ray Toel. As Przemek says, prefer to use a YearMonth for representing year and month. As both say, if you must use a date, use LocalDate, it’s a date without time of day.
If you absolutely indispensably need an old-fashioned java.util.Date object for a legacy API that you cannot change, here’s one easy way to get one. It may not work as desired, it may not give you exactly the date that you need, it depends on your exact requirements.
YearMonth expiration = YearMonth.of(2021, 8); // or .of(2021, Month.AUGUST);
Date oldFashionedDateObject = Date.from(expiration
.atDay(1)
.atStartOfDay(ZoneId.systemDefault())
.toInstant());
System.out.println(oldFashionedDateObject);
On my computer this prints
Sun Aug 01 00:00:00 CEST 2021
What we got is the first of the month at midnight in my local time zone — more precisely, my JVM’s time zone setting. This is one good guess at what your legacy API expects, but it is also dangerous. The JVM’s time zone setting may be changed under our feet by other parts of the program or by other programs running in the same JVM. In other words, we cannot really be sure what we get.
The time zone issue gets even worse if the date is transmitted to a computer running a different time zone, like from client to server or vice versa, or to a database running its own time zone. There’s about 50 % risk that your Date will come through as a time in the previous month.
If you know the time zone required in the end, it will help to specify for example ZoneId.of("America/New_York") instead of the system default in the above snippet.
If your API is lenient and just needs some point within the correct month, you’ll be better off giving it the 2nd of the month UTC or the 3rd of the month in your own time zone. Here’s how to do the former:
Date oldFashionedDateObject = Date.from(expiration
.atDay(2)
.atStartOfDay(ZoneOffset.UTC)
.toInstant());

Categories