Зацикливание задачи на один год с помощью GregorianCalendar

Мне нужно написать программу, которая показывает временной план о том, когда отправлять электронные письма.

Пользователь вводит дату начала, и я должен показать временной план на один год. Как зациклить задачу? В этом примере письма должны отправляться каждые 8 ​​дней.

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

Я хотел бы зациклить задачу System.out.println и calendar.add, пока не пройдет год.

редактировать: у меня есть другой случай, когда он должен отправлять электронные письма каждые 16 дней, но когда день суббота или воскресенье, он должен отправлять почту в следующий понедельник.

Я сделал это так, но теперь я получаю больше дат, чем мне нужно.

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;)
         }
    }

person egaal    schedule 30.07.2017    source источник
comment
К вашему сведению, неприятные старые классы даты и времени, такие как java.util.Date , java.util.Calendar и java.text.SimpleTextFormat теперь являются устаревшими, вытесненными java.time. См. учебник Oracle.   -  person Basil Bourque    schedule 31.07.2017


Ответы (2)


Вот пример использования API java.time из java 8, его гораздо проще понять и использовать в сравнении с классами календаря или даты:

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);
    }
}

если вы хотите принять во внимание часовой пояс пользователя, вы можете использовать ZonedDateTime, установленный вне 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);
    }
}
person Hasan Sbaih    schedule 30.07.2017
comment
наш учитель сказал нам использовать класс GregorianCalendar, что я и сделал. могу ли я совместить эти два? я не хочу переписывать весь код. или, может быть, есть способ конвертировать из gregorianCalendar в java.time? - person egaal; 31.07.2017
comment
@egaal Нет, не смешивайте современные классы java.time с жалким беспорядком, который представляет собой Calendar, GregorianCalendar, Date и другие устаревшие классы. Классы java.time были добавлены в Java, чтобы заменить эти старые классы. Если вы должны использовать GregorianCalendar, чтобы удовлетворить своего учителя, пусть будет так. Но для реальной работы придерживайтесь только классов java.time. И, возможно, скажите своему учителю, что 90-е звонили, и они хотят вернуть свои уроки свиданий. ;-) - person Basil Bourque; 31.07.2017
comment
В целом хороший ответ, но неуместно использовать LocalDateTime, поскольку этот класс намеренно теряет ценную информацию о часовом поясе. Вместо этого следовало использовать ZonedDateTime. - person Basil Bourque; 31.07.2017
comment
Было бы неплохо использовать григорианский календарь, но, конечно, я могу сделать это с помощью java.time. могу ли я во время Java также получить день недели? А как же високосные годы? - person egaal; 31.07.2017
comment
Это единственная проблема, оставшаяся для меня, потому что тогда я мог бы также использовать время Java - person egaal; 31.07.2017
comment
@BasilBourque спасибо, я отредактирую свой ответ и учту часовой пояс. - person Hasan Sbaih; 01.08.2017
comment
@egaal, чтобы узнать, какой сегодня день недели, вы можете вызвать timeToSendEmail.toLocalDate().getDayOfWeek(), чтобы узнать, какой сегодня день недели - person Hasan Sbaih; 01.08.2017
comment
@egaal Классы java.time — лучшая среда для дат и времени, доступная в любом месте на любой платформе. Да, они автоматически обрабатывают високосные годы — см. Год:: isLeap в частности. Да, они работают с рабочими днями, см. LocalDate и DayOfWeek в частности такие как LocalDate.now().getDayOfWeek(). По возможности избегайте старых Date, Calendar, GregorianCalendar и SimpleDateFormat. - person Basil Bourque; 01.08.2017
comment
@HasanSbaih Я исправил ваше использование типа аргумента ZoneOffset на ZoneId. Первый — это просто смещение от UTC, а второй — истинный часовой пояс (история смещений, используемых в конкретном регионе). - person Basil Bourque; 01.08.2017
comment
К вашему сведению, 8 дней могут быть программно закодированы, переданы как объект Period: Period p = Period.ofDays( 8 ) и timeToSendEmail.plus( p ) - person Basil Bourque; 01.08.2017

Интересно, почему учителя все еще преподают старый API (Date, Calendar и SimpleDateFormat), потому что у них есть множество проблем и проблемы дизайна, и они заменяются новыми API. (Кстати, Java 8 была выпущена в 2014 году).

В любом случае, если у вас есть GregorianCalendar, вы можете преобразовать его в новые классы java.time и сделать с ними все остальное.

Во-первых, вы можете использовать календарь для создания Instant:

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

Единственная проблема заключается в том, что если вы создадите Calendar и установите день, месяц и год, оно будет иметь текущее время (часы/минуты/секунды), поэтому Instant выше будет иметь текущее время в формате UTC. Если это нормально, вы можете преобразовать этот момент в свой часовой пояс:

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

Я использовал America/Sao_Paulo, но вы можете изменить его на часовой пояс, подходящий для вашей системы. API использует названия часовых поясов IANA (всегда в формате Region/City, например America/Sao_Paulo или Europe/Berlin). Избегайте использования трехбуквенных сокращений (например, CST или PST), поскольку они двусмысленный и нестандартный.

Вы можете получить список доступных часовых поясов (и выбрать тот, который лучше всего подходит для вашей системы), позвонив по номеру ZoneId.getAvailableZoneIds(). Вы также можете использовать системное значение по умолчанию, если хотите (ZoneId.systemDefault()), но обратите внимание, что его можно изменить без предварительного уведомления, даже во время выполнения, поэтому всегда лучше указывать, какой часовой пояс вы используете. Если вы хотите работать с датами в формате UTC, вы можете использовать встроенную константу ZoneOffset.UTC.

Приведенный выше код создаст ZonedDateTime с датой и временем календаря, скорректированными в соответствии с указанным часовым поясом. Просто напоминаю, что если вы сделаете что-то вроде этого:

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

Дата будет эквивалентна 12 августаth 2017 г. (поскольку месяцы в Calendar API начинаются с нуля, поэтому 7 месяц – август), а время будет текущим. при создании календаря.

Если вы хотите указать час, у вас есть несколько вариантов его настройки:

// 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);

При этом вы можете соответствующим образом изменить поля времени (а также даты). Проверьте javadoc и учебник Oracle, чтобы увидеть все доступные варианты. Метод atStartOfDay лучше, потому что он заботится об изменении летнего времени (в зависимости от перехода на летнее время день может начинаться в 1:00, а не в полночь, и этот метод заботится обо всех деталях).

Если вы не хотите полагаться на Calendar, вы также можете создать дату напрямую:

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

Обратите внимание, что август — это 8-й месяц (одно из лучших и наиболее очевидных улучшений старого API).

Теперь, когда у вас есть начальная дата, вы можете просмотреть весь год и проверить даты в соответствии с вашими правилами. Я использую пример отправки электронной почты каждые 16 дней и приспосабливаюсь к следующему понедельнику, если это выходные:

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
}

Если вы используете Java ‹= 7, вы можете использовать ThreeTen Backport, отличный вариант для Новые классы даты/времени Java 8.

Единственное отличие от Java 8 — это имена пакетов (в Java 8 — java.time, а в ThreeTen Backport (или ThreeTenABP для Android) — org.threeten.bp), но имена классов и методов те же.


Как @BasilBourque напомнил мне в комментариях, вы также можете преобразовать GregorianCalendar в ZonedDateTime, используя метод toZonedDateTime() (при этом будет использоваться часовой пояс календаря - обычно это значение по умолчанию в системе, если вы его не установили). Вы также можете преобразовать его в Instant, используя метод toInstant(). Единственное ограничение состоит в том, что эти методы доступны только в Java 8 (поэтому, если вы используете ThreeTen Backport, просто используйте способ, описанный выше).

person Community    schedule 31.07.2017
comment
Вы можете преобразовать GregorianCalendar, вызвав < a href="https://docs.oracle.com/javase/8/docs/api/java/util/GregorianCalendar.html#toZonedDateTime--" rel="nofollow noreferrer">toZonedDateTime без прохождения Instant. - person Basil Bourque; 31.07.2017
comment
@BasilBourque Действительно, я забыл об этом. Я обновил свой ответ, большое спасибо! - person ; 31.07.2017