I have the start date and the end date. I need to iterate through every day between these 2 dates.
What's the best way to do this?
I can suggest only something like:
Date currentDate = new Date (startDate.getTime ());
while (true) {
if (currentDate.getTime () >= endDate.getTime ())
break;
doSmth ();
currentDate = new Date (currentDate.getTime () + MILLIS_PER_DAY);
}
ready to run ;-)
public static void main(String[] args) throws ParseException {
GregorianCalendar gcal = new GregorianCalendar();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd");
Date start = sdf.parse("2010.01.01");
Date end = sdf.parse("2010.01.14");
gcal.setTime(start);
while (gcal.getTime().before(end)) {
gcal.add(Calendar.DAY_OF_YEAR, 1);
System.out.println( gcal.getTime().toString());
}
}
Ditto on those saying to use a Calendar object.
You can get into surprising trouble if you try to use a Date object and add 24 hours to it.
Here's a riddle for you: What is the longest month of the year? You might think that there is no answer to that question. Seven months have 31 days each, so they are all the same length, right? Well, in the United States that would be almost right, but in Europe it would be wrong! In Europe, October is the longest month. It has 31 days and 1 hour, because Europeans set their clocks back 1 hour for Daylight Saving Time in October, making one day in October last 25 hours. (Americans now begin DST in November, which has 30 days, so November is still shorter than October or December. Thus making that riddle not as amusing for Americans.)
I once ran into trouble by doing exactly what you're trying to do: I used a Date object and added 24 hours to it in a loop. It worked as long as I didn't cross Daylight Saving Time boundaries. But when I did, suddenly I skipped a day or hit the same day twice, because Midnight March 8, 2009 + 24 hours = 1:00 AM March 10. Drop off the time, as I was doing, and March 9 was mysteriously skipped. Likewise midnight Nov 1, 2009 + 24 hours = 11:00 PM Nov 1, and we hit Nov 1 twice.
Use a Calendar object if you want to manipulate dates.
Calendar c = Calendar.getInstance();
// ... set the calendar time ...
Date endDate = new Date();
// ... set the endDate value ...
while (c.getTime().before(endDate) {
// do something
c.add(Calendar.DAY_OF_WEEK, 1);
}
Or use Joda Time
I highly recommend using Joda time:
Java Joda Time - Implement a Date range iterator
Related
This question already has answers here:
Why when year is less than 1884, it remove few milliseconds?
(2 answers)
Closed 3 years ago.
This is not a duplicate as some people think. It is about two standard Java classes for formatting dates that produce different strings for the same value of milliseconds since the epoch.
For values of milliseconds since the epoch that occur before some point in the year 1883, SimpleDateFormat and DateTimeFormatter will produce different results. For reasons I don't understand, DateTimeFormatter will produce strings that differ from what I expect by almost four minutes.
This is important because I am changing some code to use DateTimeFormatter instead of SimpleDateFormat. Our input is always milliseconds since the epoch, and I need the values to be the same after I change the code.
The previous code would create a Date from the milliseconds, then use SimpleDateFormat to format it.
The new code creates an Instant from the milliseconds, then a ZonedDateTime from the Instant, then a DateTimeFormatter to format it.
Here's a test I wrote using JUnit4 and Hamcrest. The test finds the milliseconds since the epoch for May 13, 15:41:25, for each year starting at 2019 and working backwards one year at a time.
For each year, it formats the milliseconds using SimpleDateFormat and DateTimeFormatter then compares the results.
#Test
public void testSimpleDateFormatVersusDateTimeFormatter() throws Exception {
String formatString = "EEE MMM dd HH:mm:ss zzz yyyy";
String timeZoneCode = "America/New_York";
ZoneId zoneId = ZoneId.of(timeZoneCode);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(formatString);
simpleDateFormat.setTimeZone(TimeZone.getTimeZone(timeZoneCode));
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(formatString);
for (int year = 0; year < 200; year++) {
long millis = getMillisSinceEpoch(2019 - year, 5, 13, 15, 41, 25, timeZoneCode);
System.out.printf("%s%n", new Date(millis));
// Format using a DateTimeFormatter;
Instant instant = Instant.ofEpochMilli(millis);
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, zoneId);
String dateTimeFormatterString = dateTimeFormatter.format(zonedDateTime);
// Format using a SimpleDateFormat
Date date = new Date(millis);
String simpleDateFormatString = simpleDateFormat.format(date);
System.out.println("dateTimeFormatterString = " + dateTimeFormatterString);
System.out.println("simpleDateFormatString = " + simpleDateFormatString);
System.out.println();
assertThat(simpleDateFormatString, equalTo(dateTimeFormatterString));
}
}
private long getMillisSinceEpoch(int year, int month, int dayOfMonth, int hours, int minutes, int seconds, String timeZoneId) {
TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);
Calendar calendar = Calendar.getInstance(timeZone);
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month-1);
calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
calendar.set(Calendar.HOUR_OF_DAY, hours);
calendar.set(Calendar.MINUTE, minutes);
calendar.set(Calendar.SECOND, seconds);
return calendar.getTimeInMillis();
}
Running this you can see it passes for all years from 2019 back to 1884. So for any given year you see output like this:
Mon May 13 12:41:25 PST 1895
dateTimeFormatterString = Mon May 13 15:41:25 EST 1895
simpleDateFormatString = Mon May 13 15:41:25 EST 1895
But once it gets to 1883 it inexplicably fails:
Sun May 13 12:41:25 PST 1883
dateTimeFormatterString = Sun May 13 15:45:23 EST 1883
simpleDateFormatString = Sun May 13 15:41:25 EST 1883
java.lang.AssertionError:
Expected: "Sun May 13 15:45:23 EST 1883"
but: was "Sun May 13 15:41:25 EST 1883"```
The hours and seconds are obviously wrong.
By the way, if I change the time zone to "UTC", then the test passes.
According to https://www.timeanddate.com/time/change/usa/new-york?year=1883 (which was the first hit in a Google search for "1883 time adjustment"):
Nov 18, 1883 - Time Zone Change (LMT → EST)
When local standard time was about to reach
Sunday, November 18, 1883, 12:03:58 pm clocks were turned backward 0:03:58 hours to
Sunday, November 18, 1883, 12:00:00 noon local standard time instead.
3:58 matches the "almost four minutes" that you're seeing.
I haven't tested this, but I bet that if you iterate through months and days in addition to years, it occurs at that date.
See Also
Why when year is less than 1884, it remove few milliseconds?
Python pytz timezone conversion returns values that differ from timezone offset for different dates
Why is subtracting these two times (in 1927) giving a strange result? — a classic answered by Jon Skeet; not the same issue, but the same kind of issue
The Times Reports on "the Day of Two Noons"
I am working on an Android app that will display a list of activities. Every activity (i.e. waling, running) has a property Date (i.e. 9 March 8:58 2017). I have two buttons on the screen - Daily and Weekly and I want to switch betweren the two and change the list accordingly. Now, for the Daily list, I don't have to change anything, since a new Activity is created for every day.
However, I am not sure how to go about the Weekly list. It will essentially calculate stats (adding up the statistics for the individual weeks).
For example, I have a list of dates for the last 50 days. How to distinguish an individual list of Dates that would represent an individual week so I can construct a list of Weeks? Basically, convert those 50 dates into their week equivalent (e.g. about 7 weeks)
This is a test list of Dates that I am trying to get working first:
HashMap<Integer,Date> dateHashMap = new HashMap<>();
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
List<Date> dates = new ArrayList<>();
dates.add(sdf.parse("10/03/2017"));
dates.add(sdf.parse("9/03/2017"));
dates.add(sdf.parse("8/03/2017"));
dates.add(sdf.parse("7/03/2017"));
dates.add(sdf.parse("6/03/2017"));
dates.add(sdf.parse("23/02/2017"));
dates.add(sdf.parse("3/02/2017"));
dates.add(sdf.parse("2/02/2017"));
dates.add(sdf.parse("1/02/2017"));
for(Date d:dates){
dateHashMap.put(d.getDay(),d);
}
System.out.println(dateHashMap.toString());
An example UI design that I am trying to achieve:
As you already have Date property for each activity, then its quite simple actually
First just decide how you want your weeks
ex: I would just go from Mon to Sun as one week
So here Week nth will have dates - 1st to 5th March and Week nth+1 will have 6th to 12th March and so on..
and as far as i can understand you already have every activity (i.e. waling, running) with a property Date (i.e. 9 March 8:58 2017)
So taking an example here (let me know if this isn't how you have your data) :
waling - 1 March 2017 8:58 to 9:58, 3 March 2017 6:20 to 6:50, 8 March 2017 12:00 to 13:00
running - 2 March 2017 6:10 to 8:00, 3 2017 March 7:00 to 8:00, 9 March 2017 5:50 to 7:00
Now data for Week nth you can calculate by adding up duration for waling activity for dates 1st and 3rd March as waling was present only on these dates on Week nth of March 2017 and similarly for week nth+1 and so on
Same goes for running activity for week nth adding up for dates 2nd March, 3rd March and similarly for week nth+1 and so on..
Now you will have something like :
Week nth :
Wailing - 1 hr and 30 min
Running - 2 hrs and 50 min
Week nth+1 :
Wailing - 1 hr
Running - 1 hr and 10 min
And on clicking of each activity you can show some more details..
Hope this helps :)
Edit :
Considering this is how you have your dates list
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
List<Date> dates = new ArrayList<>();
try {
dates.add(sdf.parse("10/03/2017"));
dates.add(sdf.parse("9/03/2017"));
dates.add(sdf.parse("8/03/2017"));
dates.add(sdf.parse("7/03/2017"));
dates.add(sdf.parse("6/03/2017"));
dates.add(sdf.parse("23/02/2017"));
dates.add(sdf.parse("3/02/2017"));
dates.add(sdf.parse("2/02/2017"));
dates.add(sdf.parse("1/02/2017"));
} catch (ParseException e) {
e.printStackTrace();
}
You can create a custom List just to identify the dates that falls in the same week ex (I just used what #Jameson suggested in his answer, you can always write this a lot better):
public List<WeekDay> getListOfWeeksFromListOfDates(List<Date> listOfDates) {
List<WeekDay> listOfWeeks = new ArrayList<>();
WeekDay weekDay;
for (Date date : listOfDates) {
weekDay = new WeekDay(date, new SimpleDateFormat("w").format(date));
listOfWeeks.add(weekDay);
}
return listOfWeeks;
}
public class WeekDay {
Date date;
String weekIdentifier;
public WeekDay(Date Date, String WeekIdentifier) {
this.date = Date;
this.weekIdentifier = WeekIdentifier;
}
public Date getDate() {
return date;
}
public String getWeekIdentifier() {
return weekIdentifier;
}
}
And you can use getListOfWeeksFromListOfDates(dates); to have a list with Dates and Week number, this week number can serve as an identifier to compare the dates and then you can add the activities for dates with same Week number.. Hope you are getting what i am trying to convey here :)
/**
* Gets a list of week numbers (as strings) from a list of dates.
*
* #param listOfDates the list of dates
* #return a list of week in year (as string), corresponding
* one-to-one to the values in the input list.
*/
public List<String> getListOfWeeksFromListOfDates(List<Date> listOfDates) {
List<String> listOfWeeks = new ArrayList<>();
for (Date date : listOfDates) {
listOfWeeks.add(new SimpleDateFormat("w").format(date));
}
return listOfWeeks;
}
Week?
You have not defined what you mean by week.
Do you mean the standard ISO 8601 week where each week starts on a Monday and week # 1 contains the first Thursday, each week numbered 1-52 or 53?
Or do you mean a United States type week beginning on a Sunday with weeks numbered 1-52/53 starting with January 1, and if so are the last days of the previous week in the previous year or the new year?
Or do you mean something else?
Time zone?
What time zone do want to use as the context for determining the date? Or do you want to keep your date-times in UTC like Stack Overflow does in tracking your activity for “today” vs “yesterday”?
Avoid legacy date-time classes
The troublesome old date classes including Date and Calendar should be avoided whenever possible. They are now supplanted by the java.time classes.
Convert your given Date objects to Instant, a moment on the timeline in UTC.
Instant instant = myDate.toInstant();
ISO 8601
I suggest using the standard week whenever possible.
Adjust your Instant into the desired time zone.
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone( z );
Retrieve the standard week number.
int weekNumber = zdt.get( WeekFields.ISO.weekOfWeekBasedYear() );
Keep in mind that the year number to go with this week number is not the calendar year. We want the year of the week-based year. For example, in some years, December 30 and 31 can belong to the following year number of a week-based year.
int yearOfWeekBasedYear = zdt.get( WeekFields.ISO.weekBasedYear() );
You could track your records against a string composed of this yearOfWeekBasedYear and weekNumber. Use standard format, yyyy-Www such as 2017-W07.
ThreeTen-Extra YearWeek
Instead I suggest you use meaningful objects rather than mere strings. Add the ThreeTen-Extra library to your project to gain the YearWeek class.
This code replaces the WeekFields code we did above.
YearWeek yw = YearWeek.from( zdt );
Thanks to #shadygoneinsane and # Basil Bourque for pointing me to the right direction I solved the problem the following way:
TreeMap<Integer, List<Date>> dateHashMap = new TreeMap<>();
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
List<Date> dates = new ArrayList<>();
dates.add(sdf.parse("10/03/2017"));
dates.add(sdf.parse("9/03/2017"));
dates.add(sdf.parse("8/03/2017"));
dates.add(sdf.parse("7/03/2017"));
dates.add(sdf.parse("6/03/2017"));
dates.add(sdf.parse("23/02/2017"));
dates.add(sdf.parse("3/02/2017"));
dates.add(sdf.parse("2/02/2017"));
dates.add(sdf.parse("1/02/2017"));
for (int i = 0; i < dates.size(); i++) {
List<Date> datesList = new ArrayList<>();
Calendar calendar = Calendar.getInstance();
calendar.setTime(dates.get(i));
int weekOfMonth = calendar.get(Calendar.WEEK_OF_MONTH);
for (Date date : dates) {
Calendar c = Calendar.getInstance();
c.setTime(date);
if (weekOfMonth == c.get(Calendar.WEEK_OF_MONTH)) {
datesList.add(date);
}
}
dateHashMap.put(weekOfMonth, datesList);
}
System.out.println(dateHashMap.toString());
}
And the result:
Output:
1=[Fri Feb 03 00:00:00 GMT 2017,
Thu Feb 02 00:00:00 GMT 2017,
Wed Feb 01 00:00:00 GMT 2017],
2=[Fri Mar 10 00:00:00 GMT 2017,
Thu Mar 09 00:00:00 GMT 2017,
Wed Mar 08 00:00:00 GMT 2017,
Tue Mar 07 00:00:00 GMT 2017,
Mon Mar 06 00:00:00 GMT 2017],
4=[Thu Feb 23 00:00:00 GMT 2017]
Exactly what I needed! So now I can iterate through each week and sum up the statistics and thus formulate the "Weekly" view of the list
I am trying to write a java block to find the number of mid-nights in a particular date range.
For example:
begin date: 05/01/2014 00:00:00
end date : 05/03/2014 00:00:00
this range has 3 mid-nights in it.
or
begin date : 05/01/2014 00:00:00
end date : 05/02/2014 23:59:59
this has only one.
It basically has to tell me how many times the time "00:00:00" occurrs in the date range.
Please help me out. I tried many approaches but none work correct.
I would just count the days (the actual dates), and add one if the earliest date has a time of 00:00:00.
begin date: 05/01/2014 00:00:00 end date : 05/03/2014 00:00:00
03 - 01 = 2 days.
Since the begin date has a time of 00:00:00, then add one:
2 + 1 = 3 midnights.
or
begin date : 05/01/2014 00:00:00 end date : 05/02/2014 23:59:59
02 - 01 = 1 day.
Since the begin date has a time of 00:00:00, then add one:
1 + 1 = 2 midnights.
Also,
begin date : 5/01/2014 23:59:59 end date : 5/02/2014 00:00:01
02 - 01 = 1 day.
Since the begin date doesn't have a time of 00:00:00, then don't add one:
1 midnight.
The answer using Joda-Time is not correct. As #khriskooper has noted the count of midnights between
2014-05-01 00:00:00 and 2014-05-02 23:59:59
is not one but two midnights!
So here the correction using Joda-Time (not tested), but it could also be any other library which supports day-range calculations (not true for old Java-pre8). I leave out the timezone detail because I do not consider it as really relevant for the question. If OP wants he can replace LocalDateTime by DateTime and apply a timezone.
LocalDateTime ldt1 = new LocalDateTime(2014, 5, 1, 0, 0, 0);
LocalDateTime ldt2 = new LocalDateTime(2014, 5, 2, 23, 59, 59);
int days = Days.daysBetween(ldt1.toLocalDate(), ldt2.toLocalDate()).getDays();
if (ldt1.toLocalTime().equals(new LocalTime(0, 0))) {
days++;
}
One option is to use Joda-Time library:
DateTimeZone zone = DateTimeZone.forID("America/New_York");
int days = Days.daysBetween(new DateTime(startDate, zone), new DateTime(endDate, zone)).getDays();
I think this is the easiest way. The drawback is that you need one more library..
Hope this can help!
Using Joda's DateTime library (one I would recommmend anyway), an elegant way to do so would be:
private int getNumberOfNights(DateTime start, DateTime end) {
int nightCount = 0;
DateTime tempEnd = new DateTime(end);
while (tempEnd.withTimeAtStartOfDay().isAfter(start)) {
nightCount++;
tempEnd = tempEnd.minusDays(1);
}
return nightCount;
}
... or if you want to avoid a while loop:
private int getNumberOfNights(DateTime start, DateTime end) {
int nightCount = Days.daysBetween(start, end).getDays();
DateTime leftOver = new DateTime(end.minusDays(nightCount));
if (leftOver.withTimeAtStartOfDay().isAfter(start)) {
nightCount++;
}
return nightCount;
}
With Java 7
public long countDays(Date dtStart, Date dtEnd) {
long startDay = TimeUnit.DAYS.convert(dtStart.getTime(), TimeUnit.MILLISECONDS)
long endDay = TimeUnit.DAYS.convert(dtEnd.getTime(), TimeUnit.MILLISECONDS)
return endDay - startDay
}
I am using below code for date time, but when my startTime="12:00 Pm" my eventDate moving next date and also when system time is 12:00 Pm also my date is moving to next date.
help me whats wrong in this code.
Date appt = new Date();
Calendar apptDate = new GregorianCalendar();
apptDate.setTime(appt);
String startTime = "11:00 Pm";
String[] appointmentHourMinute = startTime.split(":");
String hour = appointmentHourMinute[0];
String[] minuteAmPm = appointmentHourMinute[1].split(" ");
String minute = minuteAmPm[0];
String amPm = minuteAmPm[1];
int hourInt = Integer.parseInt(hour);
int minInt = Integer.parseInt(minute);
Calendar eventDate = new GregorianCalendar();
eventDate.set(Calendar.MONTH, apptDate.get(Calendar.MONTH));
System.out.println("Display Month"+ apptDate.get(Calendar.MONTH));
eventDate.set(Calendar.DATE, apptDate.get(Calendar.DATE));
System.out.println("Display Month"+ apptDate.get(Calendar.DATE));
eventDate.set(Calendar.YEAR, apptDate.get(Calendar.YEAR));
System.out.println("Display Month"+ apptDate.get(Calendar.YEAR));
eventDate.set(Calendar.HOUR, hourInt);
eventDate.set(Calendar.MINUTE, minInt);
eventDate.set(Calendar.SECOND, 0);
if (amPm.equalsIgnoreCase("AM")){
if(hourInt==12)
eventDate.set(Calendar.AM_PM,Calendar.PM);
else
eventDate.set(Calendar.AM_PM,Calendar.AM);
}else{
if(hourInt==12)
eventDate.set(Calendar.AM_PM,Calendar.AM);
else
eventDate.set(Calendar.AM_PM,Calendar.PM);
}
First you tell the date that you want 12:00, which it interprets as a 24h time, i.e. noon. Next you tell it that you want to have a "P.M." time. As Inderstand it, this use of "P.M." for noon or midnight is ambiguous, and might often denote noon. But it seems that in this particular case, Java will interpret it as midnight. In general, it assumes that A.M. times are in the first half of the day, so when you switch from A.M. to P.M. it can simply add 12 to the hours field. Which dumps you at 0:00 of the next day.
If you want to avoid that, you might want to set the time first and the date afterwards. Which in your case means reading the date first and restoring it later on. As an alternative, you might do this +12h handling yourself, with a special case turning 24:00 back to 0:00.
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;
}
}