Parsing dates with no years that span across 12 months of data - java

I have a number of items with dates specified in MM/dd format, with no year specified.
However, the year is implied from the context of the data, since the data shows dates no older than 12 months from the "current date" (which is specified in the data)
For example, let's say the current date is January 31, 2013.
This means that there will be information from February 1 2012 to January 31, 2013, inclusive.
The problem I'm facing here is, because there are no years specified in the data, I will need to generate the years myself before I load them into my database.
From the context of the data, we know that any dates greater than the current month is from the previous year, while any dates less than or equal to the current month is the current year.
So assuming current date is Jan 2013, we have things like
01/31 - 2013
01/01 - 2013
12/31 - 2012
02/29 - 2012
Now, the problem here is the date on the last line.
2012 was a leap year, so February 29 does exist.
However, 2013 is not.
My current approach to date parsing is as follows (using SimpleDateFormat)
Grab the date: 01/31
Append the current year to it: 01/31/2016
Parse the date using date format MM/dd/yyyy
Date parsing is performed under strict mode, so something like 02/29 isn't going to be rolled over to 03/01.
However, this algorithm fails on leap years, because assuming the current year is 2013, I'm going to try to parse 02/29/2013 and it will fail.
What is an approach I can use to determine the year of the date?

Parse the day/month to a java.time.MonthDay, compare it to MonthDay.now() and call atYear with the correct year (using Year.now() for example) depending on the result.
You may also want to take time zones into account.
A simple version applied to your example, assuming that the input is well formed and ignoring time zone issues, could look like:
public static void main(String[] args) {
m("01/31", YearMonth.of(2013, 1));
m("01/01", YearMonth.of(2013, 1));
m("12/31", YearMonth.of(2013, 1));
m("02/29", YearMonth.of(2013, 1));
}
private static void m(String date, YearMonth currentMonth) {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("MM/dd");
MonthDay md = MonthDay.parse(date, fmt);
int year = currentMonth.getYear();
MonthDay cutoffDate = MonthDay.from(currentMonth.atEndOfMonth());
if (md.isAfter(cutoffDate)) year--;
LocalDate result = md.atYear(year);
System.out.println(date + " - " + year + " ==> " + result);
}
which outputs:
01/31 - 2013 ==> 2013-01-31
01/01 - 2013 ==> 2013-01-01
12/31 - 2012 ==> 2012-12-31
02/29 - 2012 ==> 2012-02-29

You cannot do it the way you want. You have to determine the year from the month and day before putting the whole thing into a Date. Here's some pseudocode assuming a date is always within the prior year, and a date equal to today is today and not 1 year ago (adjust as needed):
int cy = current year
int cm = current month
int cd = current day of month
int im = input month
int id = input day of month
int year = (im < cm || im==cm && id <= cd) ? cy : cy-1
try
Date d = new Date(year, im, id) strict
catch
invalid date

So what about doing something like:
Grab the date from your file.
Append current year.
Grab today's date.
Compare today's date with the file's date. If today's date is before, then subtract a year from the files date.
Parse the File Date.

Related

Generate a random date within some days back from today

I am trying to generate a random date in yyyy-mm-dd format for the following cases.
The random date should be within 90 days range from today.
The random date should be within 90 to 180 days from today.
The random date should be within 181 to 270 days from today.
I have written a short code snippet wherein I tried generating a random date but it does not give me the date in the range expected. That is between Sep 9 and Jan 14. It gives me the dates beyond Jan 14 also.
`public static void generateRandomDate() {
Date d1=new Date(2022, 9, 9);
Date d2=new Date(2023, 1, 14);
Date randomDate = new Date(ThreadLocalRandom.current()
.nextLong(d1.getTime(), d2.getTime()));
System.out.println(randomDate);
}`
Output: Wed Jan 17 23:41:37 IST 3923
I can use switch case to generate random dates according to the cases I want. But I am not able to get the desired date with the code I am trying and also I need to date to be in yyyy-mm-dd format. It will be really helpful if I will be able to pass the d1 and d2 in that format.
First of all, you should rather use a LocalDate instead of Date.
But going back to your question.
Current date can be obtained from:
LocalDate today = LocalDate.now();
And right now, you need to add appropriate number of days.
If your case, you can create method:
private LocalDate getRandomDateInFutureDaysRange(int start, int end) {
LocalDate today = LocalDate.now();
return today.plusDays(ThreadLocalRandom.current().nextLong(start, end));
}
For your use cases you can call it as follows:
getRandomDateInFutureDaysRange(0, 90)
getRandomDateInFutureDaysRange(90, 180)
getRandomDateInFutureDaysRange(181, 270)
To format the date you can use DateTimeFormatter as follows:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String formattedDate = yourDateToFormat.format(formatter);

Creating a new date in Java using ints

I am currently using a Date Picker, to display/select a date. I am just having trouble with the format. below is how the example constructed it.
new StringBuilder().append(month + 1)
.append("-").append(day).append("-").append(year)
.append(" ").toString();
I don't want this format so I tried the following but it keeps giving the incorrect year even if the values are correct so I am not sure what I am doing wrong.
SimpleDateFormat dateFormat = new SimpleDateFormat("d MMMM yyyy");
Date date = new Date(year,month,day);
dates.setText(dateFormat.format(date));
for example if the date was 6 November 2015(current date) and I change it to 6 December 2015 it will display 6 December 3915
The following values are being returned year = 2015 month = 11 day = 6
And this Creates 6 December 3915 I don't understand why the year is not displaying properly if I choose 2016 it would be 3916
This behaviour can be expected actually. This is what the documentation says about the Date constructor you are using (emphasis added by me):
public Date(int year, int month, int day)
Deprecated. instead use the constructor Date(long date)
Constructs a Date object initialized with the given year, month, and day.
The result is undefined if a given argument is out of bounds.
Parameters:
year - the year minus 1900; must be 0 to 8099. (Note that 8099 is 9999 minus 1900.)
month - 0 to 11
day - 1 to 31
So you get 3915 because 2015 + 1900 = 3915
I recommend you don't use this constructor. First of all it is marked as deprecated. Most importantly, no person in his right mind would see an argument int year in a method and think "Of course I have to subtract 1900 from the value I pass"
The LocalDate introduced in Java 8 is recommended as a replacement. You would use it like this
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd MMMM yyyy");
LocalDate date = LocalDate.of(2015, Month.NOVEMBER, 6);
dates.setText(dateFormat.format(date));
the format you are using is wrong. try this instead:
SimpleDateFormat dateFormat = new SimpleDateFormat("dd MM YYYY");

Day and month incorrect when setting next day of date using Calendar

//fetch date and convert to date type
String DateString = Integer.toString(getDay()) + "/" + Integer.toString(getMonth()) + "/" + Integer.toString(getYear());
DateFormat parser = new SimpleDateFormat("dd/MM/yyyy"); //current format of date
Date date = (Date) parser.parse(DateString); //convert string to date
//calculate next day
Calendar cal = Calendar.getInstance();
cal.setTime(date); //set calendar time to chosen date
cal.add(Calendar.DATE, 1); //add 1 day to calendar date
//set object to next day
parser.format(cal.getTime()); //set format to dd/MM/yyyy
setDay(cal.get(cal.DAY_OF_MONTH));
setMonth(cal.get(cal.MONTH));
setYear(cal.get(cal.YEAR));
I set a date to 23 October 2002. I want to set it to the next day using the above method. It shows 24 September 2002 instead of 24 October 2002. Why is it adding 1 to the day and removing 1 from the month?
The reason is that months are zero based index ie, they start from 0 instead of 1 so January is 0, Feb is 1, march is 2 and .....Decemeber is 11
From the Oracle docs:
A month is represented by an integer from 0 to 11; 0 is January, 1 is
February, and so forth; thus 11 is December.
EDIT:-
Trying to give the reason for why months start with zero.
The tm structure which is defined in time.h has an integer field tm_mon with the range of 0-11, so I guess this has been taken from the C language. One other reason which might sound wierd but can be reason that since we have names of the month but for days(1,2,3...30,31) we dont have any names

Why is my printed date wrong?

I have verified that the date is read correctly from a file, but once I use SimpleDateFormat.format with the pattern "dd/MM/yy" it suddenly adds a month. This leads me to believe lenient mode is calculating the wrong value. But I have no idea what would make it add a full month.
Some example dates I read:
16/09/2013
23/09/2013
30/09/2013
07/10/2013
14/10/2013
21/10/2013
The code used to parse the date (it's a wrapper around Calendar I made):
public static SimpleDateTime parseDate(String date)
{
String[] dateParts = date.split("[-\\.:/]");
int day = Integer.parseInt(dateParts[0]);
int month = Integer.parseInt(dateParts[1]);
int year = Integer.parseInt(dateParts[2]);
return new SimpleDateTime(dag, maand, jaar);
}
The constructor used here:
public SimpleDateTime(int day, int month, int year)
{
date = Calendar.getInstance();
date.setLenient(true);
setDay(day);
setMonth(month);
setYear(year);
}
The setters for day, month and year:
public void setYear(int year)
{
date.set(Calendar.YEAR, year);
}
public void setMonth(int month)
{
date.set(Calendar.MONTH, month);
}
public void setDay(int day)
{
date.set(Calendar.DAY_OF_MONTH, day);
}
And the code used to format the date:
public String toString(String pattern)
{
String output = new SimpleDateFormat(pattern, Locale.getDefault()).format(date.getTime());
return output;
}
where the pattern passed is:
"dd/MM/yy"
Intended to print a date as:
16/09/13
23/09/13
Instead I get:
16/10/13
23/10/13
January is 0 in Java; February is 1 and so on.
See Calendar.JANUARY, Calendar.FEBRUARY.
So when you're reading 1 from the file
you think you read JAN but you read FEB.
You should do: date.set(Calendar.MONTH, month-1); to fix this.
Months are indexed from 0 not 1 so 10 is November and 11 will be December.
Calendar.MONTH
From documentation:
Field number for get and set indicating the month. This is a calendar-specific value. The first month of the year is JANUARY; the last depends on the number of months in a year.
So if you check JANUARY you see it starts in zero.
Make sure your month is in the interval 0-11. Possibly it is in 1-12.
The reason for this is that the counting starts at 0.
January == 0
February == 1
and so on. See the documentation.
THe problem is that you pass 9 to SimpleDateFormat and since month are indexed from 0 to 11 it will parse month '9' as the 10th month.
You need to subtract 1 from the month :)
Calendar class in Java holds months starting from 0, hence when you set the month as 0, it would consider it as January. SimpleDateFormat provides for a way to correctly display the value as 01.
Calendar cal = Calendar.getInstance();
cal.set(Calendar.MONTH, 0);
System.out.println(new SimpleDateFormat("dd/MM/yy").format(cal.getTime()));
Output:
29/01/14
The workaround for you to align you file that Calendar can work with (since December - or 12 would trickle over to the next year) or modify your logic to pick Constants like:
cal.set(Calendar.MONTH, Calendar.JANUARY);
The answer by peter.petrov is almost correct, except for one major problem. Like your question, it neglects to account for time zone.
For your information, this kind of work is much easier in Joda-Time (or new java.time.* classes in Java 8). Joda-Time is so much cleaner you won't even feel the need to create a wrapper class.
// Specify the time zone for which the incoming date is intended.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Brussels" );
String input = "16/09/2013";
DateTimeFormatter formatter = DateTimeFormat.forPattern("dd/MM/yyyy").withZone( timeZone );
DateTime dateTime = formatter.parseDateTime( input );
String output = formatter.print( dateTime );
Dump to console…
System.out.println( "dateTime: " + dateTime );
System.out.println( "output: " + output );
System.out.println( "millis since Unix epoch: " + dateTime.getMillis() );
When run…
dateTime: 2013-09-16T00:00:00.000+02:00
output: 16/09/2013
millis since Unix epoch: 1379282400000

How to identify date of 6 complete months ago

I need to identify the date which is 6 complete months ago. For example:
Feb-27, 2012(Today) - It is Feb and we don't count incomplete month, false
Feb-01, 2012 - Still Feb so don't count too, false
Jan-01, 2011 - Completed, false
Dec-01, 2011 - Completed, false
Nov-01, 2011 - Completed, false
Oct-01, 2011 - Completed, false
Sep-01, 2011 - Completed, false
Aug-01, 2011 - Completed, false
Jul-01, 2011 - Already pass 6 complete months, true
It should work in whatever date in the future.
I thought of current date minus 30*6=180 days, but it is not accurate.
It needs to be accurate because, for example, if we identify Jul 2011 is valid then we will housekeep all the data for that month.
Thanks.
I would try this simple logic to do the job.
Calendar cal = Calendar.getInstance(); //Get current date/month i.e 27 Feb, 2012
cal.add(Calendar.MONTH, -6); //Go to date, 6 months ago 27 July, 2011
cal.set(Calendar.DAY_OF_MONTH, 1); //set date, to make it 1 July, 2011
Hope this helps.
If you could use JodaTime, here is code for ±6 months calculation:
import org.joda.time.DateTime;
import org.joda.time.Months;
....
....
DateTime now = new DateTime();
DateTime then = new DateTime().withDate(2011, 8, 1);
if(Math.abs(Months.monthsBetween(now, then).getMonths()) > 6){
System.out.println("6 mo apart!");
//your logic goes here
}
Use a library like joda-time for your time and date needs. Offhand I think you could do this with:
new LocalDate().minusMonths(7).withDayOfMonth(1)
(7 months to cover any partial months... leaves an edge case of the first of the month... but eh :) )
YearMonth
The java.time.YearMonth class built into Java makes this task simple.
Get the current year-month. This requires a time zone as at any given moment the date varies around the globe by zone. On the first/last day of the month, the month can vary around the world by zone.
YearMonth ymCurrent = YearMonth.now( ZoneId.of( "America/Montreal" ) );
We know the current month is incomplete by definition. So move to the previous month.
YearMonth ymPrevious = ymCurrent.minusMonths( 1 ) ;
Move a further six months earlier if desired.
YearMonth ymSixMonthsMore = ymPrevious.minusMonths( 6 );
That YearMonth object itself may be useful in your code. You can pas these objects of this class around your code.
If you need the first day of a month, ask.
LocalDate ld = ymSixMonthsMore.atDay( 1 );
As I understand, you need to have a utility which will determine whether a variable date is 6 months before the reference date that you will set. Take a look at my code snippet for this problem. It just gets use of java.util.Calendar, you don't have to use some other libraries.
import java.util.Calendar;
public class Test
{
public static void main(String[] args)
{
Calendar cal1 = Calendar.getInstance();
cal1.set(2012, 2, 27);
Calendar cal2 = Calendar.getInstance();
cal2.set(2011, 8, 1);
boolean valid = isSixMonthsAgo(cal1, cal2);
System.out.println(valid);
}
public static boolean isSixMonthsAgo(Calendar referenceDate, Calendar dateToBeTested)
{
int year1 = referenceDate.get(Calendar.YEAR);
int month1 = referenceDate.get(Calendar.MONTH);
int year2 = dateToBeTested.get(Calendar.YEAR);
int month2 = dateToBeTested.get(Calendar.MONTH);
if ((year1 * 12 + month1) - (year2 * 12 + month2) > 6)
return true;
return false;
}
}

Categories

Resources