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).
Related
I have an Instant derived from a Java Calendar, e.g. c.toInstant(), and now in a different location convert that to a custom Date object that needs a day of the month, month, and year. I tried:
if (instance.isSupported(ChronoField.DAY_OF_MONTH) && instance.isSupported(ChronoField.MONTH_OF_YEAR) && instance.isSupported(ChronoField.YEAR)) {
return new com.company.common.Date()
.setDay(instance.get(ChronoField.DAY_OF_MONTH))
.setMonth(instance.get(ChronoField.MONTH_OF_YEAR))
.setYear(instance.get(ChronoField.YEAR));
}
But when I try to compile ErrorProne throws:
...impl/utils/DateUtils.java:21: error: [TemporalAccessorGetChronoField] TemporalAccessor.get() only works for certain values of ChronoField.
.setDay(instance.get(ChronoField.DAY_OF_MONTH))
^
I'm not sure why I can't find an answer for this anywhere, but after some searching I came up with nothing helpful -- though I probably missed something.
Instant is only a timestamp - it only provides seconds, millis and nanos.
If you start with a Calendar instance in the first place you should be able to simply use
calendar.get(Calendar.YEAR)
calendar.get(Calendar.MONTH) + 1
calendar.get(Calendar.DAY_OF_MONTH)
directly to fetch the date values and skip the conversion to Instant.
Note that in Calendar month is a zero-based value. You usually have to add one to get to value one would commonly expect.
If you prefer working with the newer time API you can fetch dates and time from a ZonedDateTime like
Calendar calendar = Calendar.getInstance();
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(calendar.toInstant(), calendar.getTimeZone().toZoneId());
zonedDateTime.getYear();
zonedDateTime.getMonthValue();
zonedDateTime.getDayOfMonth();
I don't see a direct advantage for the example given, beyond getting the "correct" value for month here directly.
It could be useful if you want to do any additional work with the date value beyond just reading its contents.
Generally speaking ZonedDateTime and all other types from the java.time package provide the more robust API and functionality compared to the older Calendar type. Therefore avoid the calendar type for any new code
If I take current date from my application, it comes with variation like below:
scenario 1: when the date is less than 10th of the month, a month is less than 10 of the year --> example: 5/9/18
scenario 2: when the date is >= 10th of the month, a month is less >= 10 of the year --> example: 10/11/18
Note: all the examples are in MM/DD/YY format and timezone is the USA
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE,-2);
DateFormat dateFormat = new SimpleDateFormat("MM/dd/yy HH:mm a");
String PastDate = dateFormat.format(cal.getTime());
info("Date is displayed as : "+ PastDate );
The above piece of code throwing me an error when the scenario 1 is in place. But if I format the date-time as "M/d/yy H:mm a" it works for both the scenario. I need the date add also.
Will it be a good practice to use the 2nd format? or there is any other way to get it done. Expert guidance please..
java.time
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
.withLocale(Locale.US);
ZonedDateTime dayBeforeYesterday = ZonedDateTime.now(ZoneId.of("America/St_Thomas"))
.minusDays(2);
System.out.println(dayBeforeYesterday.format(formatter));
Running just now I got this output:
5/7/18, 8:44 AM
Please specify your desired time zone where I put America/St_Thomas. Think twice before you use ZoneId.systemDefault() for your JVM’s time zone setting since this setting may be changed at any time from other parts of your program or other programs running in the same JVM; but if you trust the setting reflects the user’s time zone, it’s the correct thing to use.
Rather than defining your own output format prefer using one of the built-in formats you get from DateTimeFormatter.ofLocalizedDateTime. Do specify locale (no matter if you use a built-in format or roll your own). Again, use Locale.getDefault() if you trust the JVM’s setting is correct.
Avoid the old date and time classes like Calendar, DateFormat and SimpleDateFormat. They are not only long outdated, they are also poorly designed and the last two in particular notoriously troublesome. Today we have so much better in java.time, the modern Java date and time API.
Link: Oracle tutorial: Date Time explaining how to use java.time.
The number of characters in the format MM indicates that two digits are required in the input. A single character M will match one or two digits. Use M/d/yy H:mm a to support your desired formats.
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).
This question already has answers here:
How can I determine if a date is between two dates in Java? [duplicate]
(11 answers)
Closed 7 years ago.
I am trying to create an app, and got stuck at calculating if today is in the school year. The user enters two dates, with no year, that reoccur annually. These are the start and end dates of the school year.
I want to check if the current date, is between these two, even if it overlaps two years. So if school starts on November, and ends on june, if today is January 22nd, it should return true. But if its july, it should return false.
I did find this question: Php - work out what academic year it is, But it works on academic years, which don't have a holiday.
BTW I have joda time, if that helps.
Thanks in advance.
//parse both enddate/startdate to same year
if(enddate>startdate)
enddate+=1 year;
if(today>startdate and today<enddate)
return true;
Well, I thought for a bit and found a few solutions, but this seemed simplest.
private boolean isInSchoolYear(DateTime now, DateTime schoolYearStart, DateTime schoolYearEnd){
int thisYear = now.getYear();
schoolYearStart = schoolYearStart.withYear(thisYear);
schoolYearEnd = schoolYearEnd.withYear(thisYear);
if(schoolYearStart.isBefore(schoolYearEnd)){
return new Interval(schoolYearStart, schoolYearEnd).contains(now);
}
else{
return !(new Interval(schoolYearEnd, schoolYearStart).contains(now));
}
}
This way it works both if the school year overlaps 2 years or 1.
Question Is Unclear
Your question is unclear.
For one thing, if the user is entering dates without a year, then they are not entering dates. They are entering a month number and a day-of-month number.
Perhaps the question is:
Determine if a given target date (perhaps today) is contained within either of two intervals defined by a pair of month & day-of-month numbers (a "MonthDay") where we consider the pair to either (a) end in the date’s year or (b) begin in the date’s year. We assume the pair represents a span of time of less than one year.
Joda-Time
If that is the Question, then here is some code using Joda-Time 2.7. Two caveats:
You could probably do something shorter using clever comparisons of month numbers and day-of-month numbers. But to be educational, and allow more flexibility for similar problems, I'm using Joda-Time features.
Only minimal thought and testing has gone into this code. Try various values for more testing, and re-consider the logic before using in production.
Turns out that Joda-Time has a MonthDay class, just what you need.
MonthDay mdStart = new MonthDay ( DateTimeConstants.NOVEMBER, 1 );
MonthDay mdStop = new MonthDay ( DateTimeConstants.JUNE, 1 );
Note that time zone is critical in determining the month & day of a moment such as now. For example, at a moment after midnight in Paris, it is still ‘yesterday’ in Montréal. Use proper time zone names, never the 3-4 letter codes.
DateTime target = DateTime.now ( DateTimeZone.forID ( "Africa/Casablanca" ) ); // Using current date-time, but could be any date-time.
The toDateTime method takes data fields from the MonthDay (the month number and the day number) and applies them to an existing DateTime to create a new DateTime instance. Joda-Time uses immutable objects, so we create a new instance based on the old instance rather than modify the old.
Let’s consider two possible span of times:
Where the pair of MonthDay objects are a span of time ending in the year of the target DateTime. (an earlier interval)
Where the pair of MonthDay objects are a span of time beginning in the year of the target DateTime. (a later interval)
First, the earlier interval.
// Determine a span of time, moving the Start to previous year if need be.
DateTime earlierStart = mdStart.toDateTime ( target ); // Overlay the data fields of the MonthDay on top of the data fields of a DateTime to create a new DateTime object.
DateTime earlierStop = mdStop.toDateTime ( target );
if ( earlierStart.isAfter ( earlierStop ) ) { // If the start would land after the stop, then adjust the start to *previous* year.
earlierStart = earlierStart.minusYears ( 1 );
}
Joda-Time also offers the Interval class for defining a span of time as a pair of specific points along the timeline.
Interval earlierInterval = new Interval ( earlierStart, earlierStop );
// Determine a span of time, moving the Stop to next year if need be.
DateTime laterStart = mdStart.toDateTime ( target ); // Overlay the data fields of the MonthDay on top of the data fields of a DateTime to create a new DateTime object.
DateTime laterStop = mdStop.toDateTime ( target );
if ( laterStart.isAfter ( laterStop ) ) { // If the start would land after the stop, then adjust the start to *next* year.
laterStop = laterStop.plusYears ( 1 );
}
Interval laterInterval = new Interval ( laterStart, laterStop );
Now test if the target DateTime is contained within the Interval. Note that Joda-Time wisely uses the Half-Open approach to defining a span of time, where the beginning is inclusive while the ending is exclusive. So, in our example June 1st is not within our range. That means if the target were November 1st we would have a hit, but if the target were June 1st we would have a miss.
Boolean earlierHasTarget = earlierInterval.contains ( target );
Boolean laterHasTarget = laterInterval.contains ( target );
Boolean targetContained = ( earlierHasTarget || laterHasTarget );
java.time
Java 8 has java.time, a new date-time framework inspired by Joda-Time. While largely similar to Joda-Time, it is not a direct replacement. Each has features the other lacks. In particular, java.time lacks any equivalent to Interval.
How can I add two dates in Java?
Example: The sum of "2010-01-14 19:16:17" "0000-10-03 01:10:05"
would result in "2010-11-17 20:26:22".
I know how to do it using Calendar and adding field by field.
Is any other way to sum them all (year/month/day/hour/minute/second) at once?
If you are using the Date object, you can just do:
Date d1 = ...
Date d2 = ...
long sum = d1.getTime() + d2.getTime();
Date sumDate = new Date(sum);
The code uses the .getTime() method that returns the number of milliseconds since the epoch.
Needless to say the Date class has a lot of problems and should be avoided when possible.
Do you want to sum other types instead?
Update: for Calendar, I would do the following (based on javadocs):
Calendar c1 = ...
Calendar c2 = ...
long sum = c1.getTimeInMillis() + c2.getTimeInMillis();
Calendar sumCalendar = (Calendar)c1.clone();
sumCalendar.setTimeInMillis(sum);
UPDATED: As Steve stated, this works if the Date you presented here assumes that the second date is with respect to the Java epoch. If you do want to start with year "0", then you need to account for that (by subtracting your epoch time).
Don't sum the time in millis of the two dates!
Date d1 = new Date();
Date d2 = new Date();
Date dTotal = new Date(d1.getTime() + d2.getTime());
System.out.println(dTotal); // Incorrect! Misses about 1970 years.
Just clone the Calendar and add the datetime parts one by one.
Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
Calendar cTotal = (Calendar) c1.clone();
cTotal.add(Calendar.YEAR, c2.get(Calendar.YEAR));
cTotal.add(Calendar.MONTH, c2.get(Calendar.MONTH) + 1); // Months are zero-based!
cTotal.add(Calendar.DATE, c2.get(Calendar.DATE));
cTotal.add(Calendar.HOUR_OF_DAY, c2.get(Calendar.HOUR_OF_DAY));
cTotal.add(Calendar.MINUTE, c2.get(Calendar.MINUTE));
cTotal.add(Calendar.SECOND, c2.get(Calendar.SECOND));
cTotal.add(Calendar.MILLISECOND, c2.get(Calendar.MILLISECOND));
System.out.println(cTotal.getTime()); // Correct!
Needless to say, JodaTime is smarter and cleaner with this.
As always, I would recommend the Java 8 date/time APIs or Joda for date/time work, since they are much more powerful and intuitive.
You can add durations and periods to a DateTime object trivially. You can add minutes/seconds/months equally easily.
However, you can't add two dates directly, since that doesn't really make sense. This is a powerful illustration of why Joda is a help - it stops you doing stuff that you really shouldn't be doing.
tl;dr
LocalDateTime later =
LocalDateTime
.parse (
"2010-01-14 19:16:17"
.replace ( " " , "T" )
)
.plus( Period.parse ( "P10M3D" ) )
.plus( Duration.parse ( "PT1H10M5S" ) )
;
ISO 8601
The representation of a span-of-time using the same format as a moment is creating confusion. A span is not at all the same as a moment.
Instead of using YYYY-MM-DD HH-MM-SS format for a span of time, I suggest using the standard ISO 8601 format of PnYnMnDTnHnMnS. In this format, the P marks the beginning (for "Period" presumably) and the T separates the years-month-days portion from the hours-minutes-seconds portion.
Example values:
PT1H30M → One and a half hours.
P3Y6M4DT12H30M5S → Three years, six months, four days, twelve hours, thirty minutes, and five seconds.
P10M3DT1H10M5S → Your Question’s duration of 0000-10-03 01:10:05.
java.time
The Question and the other Answers use troublesome old date-time classes now outmoded by the java.time framework built into Java 8 and later. See Oracle Tutorial. Much of the java.time functionality has been back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.
The java.time classes use ISO 8601 formats by default when parsing and generating Strings that represent date-time values.
The Question does not provide any time zone info, so here we use the LocalDateTime class. If we know an offset-from-UTC we would use the OffsetDateTime class, and if even better we knew a time zone, we would use the ZonedDateTime class.
Spans of time in java.time are divided amongst a pair of classes. Years-months-days are represented by the Period class, and hours-minutes-seconds are handled by the Duration class.
Combining these times, we can indeed perform date-time math. Here we add a span of time to an starting date-time to get a resulting date-time. And we do so in very few lines of code. The result is indeed that expected by the Question.
We convert the input strings to canonical ISO 8601 format by replacing the SPACE in the middle with a T.
LocalDateTime ldt = LocalDateTime.parse ( "2010-01-14 19:16:17".replace ( " " , "T" ) );
//"0000-10-03 01:10:05"
Period period = Period.parse ( "P10M3D" );
Duration duration = Duration.parse ( "PT1H10M5S" );
LocalDateTime result = ldt.plus ( period ).plus ( duration );
Compare to the result expected in the Question.
LocalDateTime expectation = LocalDateTime.parse ( "2010-11-17 20:26:22".replace ( " " , "T" ) );
Boolean isSame = result.equals ( expectation );
Dump to console.
System.out.println ( "ldt: " + ldt + " + period: " + period + " + duration: " + duration + " is result: " + result + " compared to expectation: " + expectation + " is the same: " + isSame );
ldt: 2010-01-14T19:16:17 + period: P10M3D + duration: PT1H10M5S is result: 2010-11-17T20:26:22 compared to expectation: 2010-11-17T20:26:22 is the same: true
You want to do getTimeInMillis() on both those Calendars so you'll have two honest-to-goodness long values you can add up. You can then take the sum and stash it in a new Calendar using that Calendar's setTimeInMillis() method.
Whether you want to add two Calendars as shown above or two Dates as shown in notnoop's answer is up to you, of course. The effect is similar, it just depends on what you want to do with the result. A Date is mostly just good for storing and/or converting to a String for printing out or displaying, whereas a Calendar will let you fiddle with the individual time values should you so choose.
As others have mentioned, you're committing some conceptual no-no's in using a Date or Calendar, which are meant to store "real" dates and times, e.g. ones in the 20th or 21st century, as intervals, i.e. time spans. The classes in the standard Java library don't give you really useful tools to handle this, which is why the Joda classes were developed. All the cool kids in date/time processing use those; but on the other hand that involves downloading and managing a 3rd party library.
notnoop answer is definitely correct. However, if you are going to do lots of processing of dates, times and intervals, I suggest that you look at class DateUtils in apache commons lang and at joda-time library.
JDK7 will come with better support for some of the features that joda-time provides. Just saying ... it might be a consideration if your app makes heavy usage of this stuff.
You need to define your EPOCH. The Java epoch (like Unix) is 1 Jan 1970 GMT/UTC. I assume you think you're adding ten months, 3 days and some odd hours from 1 Jan 0000 but you have a epoch offset until 1970. The maths may not necessarily work.
Use Calendar or Joda (as mentioned). If you just simply want to add a number of seconds and days (&c) then feel free to add said # of milliseconds to your first date object.
Use calendar class add method to add two dates in java.
Calendar calendar=Calendar.getInstance();
calendar.add(Calendar.Date,23);
calendar.add(Calendar.Month,13);
calendar.add(Calendar.Year,15);
By using add method in Calendar class we can add day,month,year to the existing date.
click here for complete program.
I am occasionally guilty of this practice too, storing time interval in a date object and using getTime() as suggested by notnoop.
It works. Contrary to certain opinion, it certainly works. I just ignore that the interval could be representative of an unintended date. It is a quick and dirty way for me to add an interval, say, [6 years, 6 months, 6 days, 6 hours, 6 minutes, 6 seconds] to a date.