0

I have to write a program which shows a Timeplan about when to send emails.

The User is inputing a Start date and I have to show the timeplan for one year. How do I loop the Task? In this example the mails should be sent every 8 days.

    if(recipient==0) {
        System.out.println("send mail on this day:" +calendar.getTime());   
        calendar.add((GregorianCalendar.DAY_OF_YEAR),8);
        return true;    
    }

I would like to loop the System.out.println and the calendar.add task until it is one year later.

edit: I have another case where it should send the emails every 16 days but when the day is a saturday or sunday it should send the mail on the following monday.

I did it like this but now I get more dates than I need.

if(empfaenger==1)
    {
         for (Date d=startDate; d.before(endDate); d.setTime(d.getTime() + (1000 * 60 * 60 * 24 * 8)))
         {
             if(calendar.get(calendar.DAY_OF_WEEK)==1)
            {
            calendar.add((GregorianCalendar.DAY_OF_YEAR),1);
            System.out.println("mail will be sent on this day:"+calendar.getTime());
            calendar.add((GregorianCalendar.DAY_OF_YEAR), 16);
            }
        else if(calendar.get(calendar.DAY_OF_WEEK)==7)
            {
            calendar.add((GregorianCalendar.DAY_OF_YEAR), 2);
            System.out.println("mail will be sent on this day:"+calendar.getTime());
            calendar.add((GregorianCalendar.DAY_OF_YEAR),16);
            } 
        else
        {
            System.out.println("mail will be sent on this day:"+calendar.getTime());
            calendar.add((GregorianCalendar.DAY_OF_YEAR),16);
        }
        //System.out.println(calendar.getTime;)
         }
    }
egaal
  • 11
  • 1
  • FYI, the troublesome old date-time classes such as [`java.util.Date`](https://docs.oracle.com/javase/8/docs/api/java/util/Date.html), [`java.util.Calendar`](https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html), and `java.text.SimpleTextFormat` are now [legacy](https://en.wikipedia.org/wiki/Legacy_system), supplanted by the [java.time](https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html) classes. See [Tutorial by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). – Basil Bourque Jul 31 '17 at 02:32

2 Answers2

1

Here is a sample using java.time api from java 8 , it's much more easier to understand and use compered to calendar or date classes :

static void sendEveryEightDays(){
    LocalDateTime timeToSendEmail= LocalDateTime.now();
    LocalDateTime afterAYear = timeToSendEmail.plusYears(1);

    while(timeToSendEmail.isBefore(afterAYear)){
        System.out.println("SendTheEmail "+timeToSendEmail.toString());
        timeToSendEmail=timeToSendEmail.plusDays(8);
    }
}

if you want to take the user's time zone into consideration you can use ZonedDateTime instated off LocalDateTime :

static void sendEveryEightDays(ZoneId userTimeZone){
    ZonedDateTime timeToSendEmail= ZonedDateTime.now(userTimeZone);
    ZonedDateTime afterAYear = timeToSendEmail.plusYears(1);

    while(timeToSendEmail.isBefore(afterAYear)){
        System.out.println("SendTheEmail "+timeToSendEmail.toString());
        timeToSendEmail=timeToSendEmail.plusDays(8);
    }
}
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • our teacher told us to use GregorianCalendar class which I did. can I combine these two? i dont want to rewrite the whole code. or maybe is there a way to convert from gregorianCalendar to java.time? – egaal Jul 30 '17 at 23:34
  • @egaal No, do not mix the modern java.time classes with the wretched mess that is `Calendar`, `GregorianCalendar`, `Date`, and the other legacy classes. The java.time classes were added to Java to replace those old classes. If you must use `GregorianCalendar` to satisfy your teacher, so be it. But for real work, stick to java.time classes only. And perhaps tell your teacher that the 90s called and they want their date-time classes back. ;-) – Basil Bourque Jul 31 '17 at 02:35
  • Good answer generally, but inappropriate to use `LocalDateTime` as that class purposely loses the valuable information of time zone. Should have used [`ZonedDateTime`](http://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html) instead. – Basil Bourque Jul 31 '17 at 02:37
  • It would be nice to use Gregorian calendar but of course I can do it with java.time. can I in Java time also get the weekday? And what about leap years? – egaal Jul 31 '17 at 06:49
  • That's the only problem left for me because then I could also use Java time – egaal Jul 31 '17 at 06:50
  • @BasilBourque thank you , I will edit my answer and i'll take time zone into consideration. – Hasan Sbaih Jul 31 '17 at 21:14
  • @egaal to get what day of the week is it , you can call timeToSendEmail.toLocalDate().getDayOfWeek() to get what day of the week is it – Hasan Sbaih Jul 31 '17 at 21:14
  • @egaal The java.time classes are the best framework for date-time available anywhere on any platform. Yes, they handle leap years automatically – see [Year::isLeap](https://docs.oracle.com/javase/8/docs/api/java/time/Year.html) in particular. Yes they handle weekdays, see `LocalDate` and [`DayOfWeek`](https://docs.oracle.com/javase/8/docs/api/java/time/DayOfWeek.html) in particular such as `LocalDate.now().getDayOfWeek()`. Avoid the old `Date`, `Calendar`, `GregorianCalendar`, and `SimpleDateFormat` wherever possible. – Basil Bourque Aug 01 '17 at 04:09
  • @HasanSbaih I corrected your use of argument type `ZoneOffset` to `ZoneId`. The first is a mere offset-from-UTC while the second is a true time zone (a history of offsets in use by a particular region). – Basil Bourque Aug 01 '17 at 04:13
  • FYI, the 8 days can be soft-coded, passed as a `Period` object: `Period p = Period.ofDays( 8 )` and `timeToSendEmail.plus( p )` – Basil Bourque Aug 01 '17 at 04:16
0

I wonder why teachers are still teaching the old API (Date, Calendar and SimpleDateFormat), because they have lots of problems and design issues, and they're being replaced by the new APIs. (Java 8 was released in 2014, btw).

Anyway, if you have a GregorianCalendar, you can convert it to the new java.time classes and do the rest with them.

First, you can use the calendar to create an Instant:

Instant instant = Instant.ofEpochMilli(calendar.getTimeInMillis());

The only problem is that, if you create a Calendar and set the day, month and year, it will have the current time (hour/minute/seconds), so the Instant above will have the current time in UTC. If that's ok, you can convert this instant to your timezone:

ZoneId zone = ZoneId.of("America/Sao_Paulo");
ZonedDateTime start = instant.atZone(zone);

I used America/Sao_Paulo, but you can change to the timezone that makes sense to your system. 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(). You can also use the system's default if you want (ZoneId.systemDefault()), but note that this can be changed without notice, even at runtime, so it's always better to specify which timezone you're using. If you want to work with dates in UTC, you can use the built-in constant ZoneOffset.UTC.

The code above will create a ZonedDateTime with the calendar's date and time adjusted to the specified timezone. Just reminding that, if you do something like this:

Calendar calendar = new GregorianCalendar();
calendar.set(2017, 7, 12);

The date will be equivalent to August 12th 2017 (because months in the Calendar API start at zero, so month 7 is August), and the time will be the current time when the calendar is created.

If you want to specify the hour, you have some options to adjust it:

// change the hour/minute/second to 10:20:45
start = start.with(LocalTime.of(10, 20, 45));

// change just the hour to 10
start = start.withHour(10);

// set to start of the day
start = start.toLocalDate().atStartOfDay(zone);

With this, you can change the time (and also date) fields accordingly. Check the javadoc and Oracle's tutorial to see all the options available. The method atStartOfDay is better because it takes care of Daylight Saving Time changes (depending on DST shift, the day can start at 1AM instead of midnight, and this method takes care of all the details).

If you don't want to rely on Calendar, you can also create the date directly:

// creating August 12th 2017, at 10:00
start = ZonedDateTime.of(2017, 8, 12, 10, 0, 0, 0, zone);

Note that August is month 8 (one of the best and most obvious improvements from the old API).

Now that you have the starting date, you can loop through a whole year and check the dates according to your rules. I'm using the example of sending the email each 16 days and adjust to next monday if it's a weekend:

ZonedDateTime d = start;
// ends in 1 year - this method already takes care of leap years
ZonedDateTime end = start.plusYears(1);
while (end.isAfter(d)) {
    d = d.plusDays(16);
    if (d.getDayOfWeek() == DayOfWeek.SUNDAY || d.getDayOfWeek() == DayOfWeek.SATURDAY) {
        // weekend, adjust to next monday
        d = d.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
    }
    // send email
}

If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes.

The only difference from Java 8 is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.


As @BasilBourque reminded me in the comments, you can also convert a GregorianCalendar to a ZonedDateTime using the toZonedDateTime() method (this will use the calendar's timezone - usually the system's default, if you don't set it). You can also convert it to an Instant using the toInstant() method. The only restriction is that those methods are only available in Java 8 (so, if you're using ThreeTen Backport, just use the way it's described above).

  • You can convert from [`GregorianCalendar`](https://docs.oracle.com/javase/8/docs/api/java/util/GregorianCalendar.html) by calling [`toZonedDateTime`](https://docs.oracle.com/javase/8/docs/api/java/util/GregorianCalendar.html#toZonedDateTime--) without going through an `Instant`. – Basil Bourque Jul 31 '17 at 16:16
  • @BasilBourque Indeed, I forgot about it. I've updated my answer, thanks a lot! –  Jul 31 '17 at 16:21