Есть ли класс для кодирования местного времени недели в Java? - PullRequest
4 голосов
/ 26 апреля 2020

Я хочу создать расписание, которое будет составлять неделю за неделей.

Поскольку расписание одно и то же от одной недели к следующей, единственная информация, которую мне нужно сохранить, - это день недели. и время, когда это происходит. например. Monday 2:30pm. Фактическая дата не важна, как и часовой пояс.

До сих пор я писал свой код с разделением дня и времени, используя перечисление DayOfWeek и тип LocalTime для работы с раз. Но я начал сталкиваться с проблемами при использовании двух переменных для управления временем вместо одного удобного типа, такого как DateTime. например. если я хочу получить время через 3 часа после 23:00 во вторник, я могу использовать метод LocalTime.plus(), чтобы получить 2 часа ночи, но это не учитывает смену дня, и мне придется проверять и обновлять это отдельно.

Я также хочу убедиться, что любое решение, которое у меня есть, распространяется с конца недели до начала: например. 5 часов после воскресенья. 22:00 - понедельник, 3 часа утра. Или лучше было бы что-то решить, используя LocalDateTime, предоставляемый java, и как-то игнорировать компонент даты?

Ответы [ 3 ]

5 голосов
/ 26 апреля 2020

Нет такого встроенного класса в JDK, но создать такой класс не должно быть слишком сложно.

Я думаю, что вы на правильном пути с использованием LocalTime и DayOfWeek. Обязательно напишите класс, который обернет эти два компонента, а затем добавьте методы для добавления единиц времени к объекту-обертке.

Один из методов может выглядеть примерно так:

public WeekDayTime plusHours(int hours) {
    int plusDays = (hours / 24);
    int plusHours = hours % 24;
    if (this.time.getHours() + plusHours >= 24) {
        plusDays++;
        plusHours -= 24;
    }
    DayOfWeek newDayOfWeek = ...; // Calculate the day of week somehow
    LocalTime newTime = this.time.plusHours(plusHours);
    return new WeekDayTime(newDayOfWeek, newTime);
}

В качестве альтернативы, вы также можете обернуть LocalDateTime и просто скрыть компонент даты. Это избавит вас от выполнения этих расчетов. Но затем убедитесь, что вы правильно внедрили, например, метод equals.

2 голосов
/ 27 апреля 2020

Правильный ответ от M C Император вдохновил меня сделать следующий шаг, разработав класс DayOfWeekTime, который более гибко использует метод даты и времени, приняв более общий * 1005. * & Duration объектов класса, а не просто количество часов.

Я максимально использовал существующие java .time классы , Я следовал соглашениям об именах java .time . И я эмулировал функциональность java .time .

Для строк я обратился к стандарту ISO 8601 , используемому java .time . К сожалению, в стандарте не учитывается день недели с концепцией времени дня. Стандарт имеет представление о неделе года, отмеченной W, за которым следует число от 1 до 52 или от 1 до 53. Для определенного дня конкретной недели в стандарт добавляется дефис с номером 1-7 для понедельника-воскресенья. Таким образом, я следовал этому шаблону для моих toString и parse методов. Я начинаю с W, опускаю любой номер недели, затем дефис и номер дня недели 1-7. Я добавляю T в качестве разделителя, следуя стандарту. Затем добавьте время суток в 24-часовых часах с добавлением нуля в течение часа и минуты. Например, дата-время 2020-04-27T13:00-04:00[America/Montreal] создает W-7T19:46:40.937485.

Этот код был только протестирован. Используйте на свой страх и риск.

package work.basil.example;

import java.time.*;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAdjusters;
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;

// Revised 2020-04-27. Fixed bug where the plus & minus methods adjusted from baseline by time-of-day but not day-of-week. Fixed.

public class DayOfWeekTime
{
    // ----------|  Fields  |------------------------------------------

    private DayOfWeek dayOfWeek;
    private LocalTime localTime;

    // ----------|  Statics  |------------------------------------------

    // We do the date-time math by picking a date arbitrarily to use as a `LocalDateTime`.
    // For convenience, we might as well pick a year that starts on a Monday.
    // https://en.wikipedia.org/wiki/Common_year_starting_on_Monday
    // Let us go with 2001-01-01.
    static private LocalDateTime BASELINE = LocalDateTime.of( 2001 , 1 , 1 , 0 , 0 , 0 , 0 );

    // ----------|  Constructor  |------------------------------------------

    private DayOfWeekTime ( final DayOfWeek dayOfWeek , final LocalTime localTime )
    {
        Objects.requireNonNull( dayOfWeek , "Received a null argument. Message # e3e04fde-d96a-41d8-a4e7-c2b4f2f5634b." );
        Objects.requireNonNull( localTime , "Received a null argument. Message # 97ccf27a-6c05-402a-a4aa-0a48bcff62c2." );
        this.dayOfWeek = dayOfWeek;
        this.localTime = localTime;
    }

    // ----------|  Factory  |------------------------------------------

    static public DayOfWeekTime of ( final DayOfWeek dayOfWeek , final LocalTime localTime )
    {
        Objects.requireNonNull( dayOfWeek , "Received a null argument. Message # ecfe6bf6-de34-4f63-9a3e-d04cd70e721f." );
        Objects.requireNonNull( localTime , "Received a null argument. Message # 83020094-409d-40e1-8dc3-12592eea1b81." );
        return new DayOfWeekTime( dayOfWeek , localTime );
    }

    static public DayOfWeekTime now ( ZoneId zoneId )
    {
        Objects.requireNonNull( zoneId , "Received null argument for time zone. Message # 6044dd82-3616-40a6-8ac2-52581e12e60f." );
        ZonedDateTime now = ZonedDateTime.now( zoneId );
        DayOfWeek dow = now.getDayOfWeek();
        LocalTime lt = now.toLocalTime();
        DayOfWeekTime dayOfWeekTime = DayOfWeekTime.of( dow , lt );
        return dayOfWeekTime;
    }

    // ----------|  Duration  |------------------------------------------
    public DayOfWeekTime plus ( final Duration duration )
    {
        Objects.requireNonNull( duration , "Received a null argument. Message # cf60bd16-3992-4779-a621-a0a3fdb2d750." );
        LocalDateTime ldt = DayOfWeekTime.BASELINE.with( TemporalAdjusters.nextOrSame( this.dayOfWeek ) ).with( this.localTime );
        LocalDateTime ldtSum = ldt.plus( duration );
        DayOfWeekTime dayOfWeekTime = DayOfWeekTime.of( ldtSum.getDayOfWeek() , ldtSum.toLocalTime() );
        return dayOfWeekTime;
    }

    public DayOfWeekTime minus ( final Duration duration )
    {
        Objects.requireNonNull( duration , "Received a null argument. Message # 4e7bf8c9-6e4f-4e3f-a8b1-5a42dc23cd8a." );
        LocalDateTime ldt = DayOfWeekTime.BASELINE.with( TemporalAdjusters.nextOrSame( this.dayOfWeek ) ).with( this.localTime );
        LocalDateTime ldtSum = ldt.minus( duration );
        DayOfWeekTime dayOfWeekTime = DayOfWeekTime.of( ldtSum.getDayOfWeek() , ldtSum.toLocalTime() );
        return dayOfWeekTime;
    }

    // ----------|  Period  |------------------------------------------

    public DayOfWeekTime plus ( final Period period )
    {
        Objects.requireNonNull( period , "Received a null argument. Message # 3b1f65b0-5b2c-4e86-aaa3-527992356d32." );
        LocalDateTime ldt = DayOfWeekTime.BASELINE.with( TemporalAdjusters.nextOrSame( this.dayOfWeek ) ).with( this.localTime );
        LocalDateTime ldtSum = ldt.plus( period );
        DayOfWeekTime dayOfWeekTime = DayOfWeekTime.of( ldtSum.getDayOfWeek() , ldtSum.toLocalTime() );
        return dayOfWeekTime;
    }

    public DayOfWeekTime minus ( final Period period )
    {
        Objects.requireNonNull( period , "Received a null argument. Message # 045938fc-d4b2-4bd2-8803-91db54d92564." );
        LocalDateTime ldt = DayOfWeekTime.BASELINE.with( TemporalAdjusters.nextOrSame( this.dayOfWeek ) ).with( this.localTime );
        LocalDateTime ldtSum = ldt.minus( period );
        DayOfWeekTime dayOfWeekTime = DayOfWeekTime.of( ldtSum.getDayOfWeek() , ldtSum.toLocalTime() );
        return dayOfWeekTime;
    }

    // ----------|  Parsing  |------------------------------------------

    // This text output invented here in this method is styled to follow the designs of ISO 8601,
    // but is most certainly *not* defined in the standard.
    static public DayOfWeekTime parse ( final String input )
    {
        Objects.requireNonNull( input , "Received a null argument. Message # 7c519b65-a1ec-486b-a9e9-ff31ee1b8057." );
        if ( input.isEmpty() || input.isBlank() )
        {
            throw new IllegalArgumentException( "Received empty/blank string as argument. Message # 59993300-bdf9-4e69-82e0-823456715c60." );
        }

        DayOfWeek dayOfWeek = null;
        LocalTime localTime = null;

        // My regex powers are weak.
        // My half-baked scheme returns tokens = [, -1, 13:00] for an input of "W-1T13:00".
        String delimiters = "[WT]+";
        String[] tokens = input.toUpperCase( Locale.US ).split( delimiters );  // ISO 8601 requires the output of uppercase letters while mandating that we accept lowercase.
        System.out.println( "DEBUG tokens = " + Arrays.toString( tokens ) );
        if ( tokens.length != 3 )
        {
            throw new IllegalArgumentException( "Received invalid string as argument. Message # e521a4e3-1ee5-46e9-b351-a5edb7206b82." );
        }

        int dowNumber = Math.abs( Integer.parseInt( tokens[ 1 ] ) );
        dayOfWeek = DayOfWeek.of( dowNumber );

        String localTimeInput = Objects.requireNonNull( tokens[ 2 ] , "The time-of-day component of the input is null. Message # 1faed491-abaa-42bd-b767-d876f8ba07d9." );
        if ( localTimeInput.isEmpty() || localTimeInput.isBlank() )
        {
            throw new IllegalArgumentException( "The time-of-day component of the input is empty/blank. Message # 98208025-d7c2-4b59-bc5f-fed7bec09741." );
        }
        try
        {
            localTime = LocalTime.parse( localTimeInput );
        }
        catch ( DateTimeParseException e )
        {
            throw new IllegalArgumentException( "The time-of-day component of the input is invalid. Message # 8de7e8d8-f4a3-478d-96d8-911454aced14." );
        }

        DayOfWeekTime dayOfWeekTime = DayOfWeekTime.of( dayOfWeek , localTime );
        return dayOfWeekTime;
    }


    // ----------|  Moment  |------------------------------------------

    public ZonedDateTime atDateInZone ( LocalDate localDate , ZoneId zoneId )
    {
        Objects.requireNonNull( zoneId , "Received null argument. Message # b8f70601-2b1d-4321-a57f-96384759a960." );
        LocalDate ld = localDate.with( TemporalAdjusters.nextOrSame( this.dayOfWeek ) ); // Move to the next date with a matching day-of-week if not a match.
        LocalDateTime ldt = LocalDateTime.of( ld , this.localTime );
        ZonedDateTime zdt = ldt.atZone( zoneId );
        return zdt;
    }


    // ----------|  Accessors  |------------------------------------------

    public DayOfWeek getDayOfWeek ( ) { return this.dayOfWeek; }

    public LocalTime getLocalTime ( ) { return this.localTime; }


    // ----------|  Object  |------------------------------------------

    @Override
    public boolean equals ( Object o )
    {
        if ( this == o ) return true;
        if ( o == null || getClass() != o.getClass() ) return false;
        DayOfWeekTime that = ( DayOfWeekTime ) o;
        return dayOfWeek == that.dayOfWeek &&
                localTime.equals( that.localTime );
    }

    @Override
    public int hashCode ( )
    {
        return Objects.hash( dayOfWeek , localTime );
    }

    // This text output invented here in this method  is styled to follow the designs of ISO 8601,
    // but is most certainly *not* defined in the standard.
    @Override
    public String toString ( )
    {
        String output = "W" + "-" + this.dayOfWeek.getValue() + "T" + this.localTime.toString();
        return output;
    }
}

Пример использования для воскресенья.

DayOfWeekTime dayOfWeekTime = DayOfWeekTime.parse( "W-7T13:00" );

DayOfWeekTime nineDaysPrior = dayOfWeekTime.minus( Period.ofDays( 9 ) );
DayOfWeekTime oneDayPrior = dayOfWeekTime.minus( Period.ofDays( 1 ) );
DayOfWeekTime oneDayLater = dayOfWeekTime.plus( Period.ofDays( 1 ) );
DayOfWeekTime twoDaysLater = dayOfWeekTime.plus( Period.ofDays( 2 ) );
DayOfWeekTime weekLater = dayOfWeekTime.plus( Period.ofWeeks( 1 ) );
DayOfWeekTime nineDaysLater = dayOfWeekTime.plus( Period.ofDays( 9 ) );

DayOfWeekTime twoHoursLater = dayOfWeekTime.plus( Duration.ofHours( 2 ) );
DayOfWeekTime nineHoursLater = dayOfWeekTime.plus( Duration.ofHours( 9 ) );
DayOfWeekTime twentyFourHoursLater = dayOfWeekTime.plus( Duration.ofHours( 24 ) );

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = dayOfWeekTime.atDateInZone( LocalDate.now( z ) , z );

DayOfWeekTime now = DayOfWeekTime.now( z );

Дамп на консоль.

System.out.println( "After parsing: dayOfWeekTime = " + dayOfWeekTime );

System.out.println( "nineDaysPrior = " + nineDaysPrior );
System.out.println( "oneDayPrior = " + oneDayPrior );
System.out.println( "oneDayLater = " + oneDayLater );
System.out.println( "twoDaysLater = " + twoDaysLater );
System.out.println( "weekLater = " + weekLater );
System.out.println( "nineDaysLater = " + nineDaysLater );

System.out.println( "twoHoursLater = " + twoHoursLater );
System.out.println( "nineHoursLater = " + nineHoursLater );
System.out.println( "twentyFourHoursLater = " + twentyFourHoursLater );

System.out.println( "zdt = " + zdt );
System.out.println( "now = " + now );
DEBUG tokens = [, -7, 13:00]
After parsing: dayOfWeekTime = W-7T13:00
nineDaysPrior = W-5T13:00
oneDayPrior = W-6T13:00
oneDayLater = W-1T13:00
twoDaysLater = W-2T13:00
weekLater = W-7T13:00
nineDaysLater = W-2T13:00
twoHoursLater = W-7T15:00
nineHoursLater = W-7T22:00
twentyFourHoursLater = W-1T13:00
zdt = 2020-05-03T13:00-04:00[America/Montreal]
now = W-1T16:10:33.248253

И для понедельника.

DayOfWeekTime dayOfWeekTime = DayOfWeekTime.parse( "W-1T13:00" );
…
DEBUG tokens = [, -1, 13:00]
After parsing: dayOfWeekTime = W-1T13:00
nineDaysPrior = W-6T13:00
oneDayPrior = W-7T13:00
oneDayLater = W-2T13:00
twoDaysLater = W-3T13:00
weekLater = W-1T13:00
nineDaysLater = W-3T13:00
twoHoursLater = W-1T15:00
nineHoursLater = W-1T22:00
twentyFourHoursLater = W-2T13:00
zdt = 2020-04-27T13:00-04:00[America/Montreal]
now = W-1T16:16:11.543665

Похоже, что эта функциональность действительно может быть полезна людям для работы в рабочие смены или для времени открытия / закрытия магазина. Если это имеет смысл для других, возможно, кто-то захочет написать запрос на добавление функции и запрос на извлечение для проекта ThreeTen-Extra . Дальнейшая работа будет включать полную поддержку временных регуляторов, усечение и полное юнит-тестирование. И поддержка других функций java .time , о которых я могу не знать.

0 голосов
/ 26 апреля 2020

просьб см. йода документация Стандартные классы даты и времени до Java SE 8 плохие. Решая эту проблему, Joda-Time стала де-факто стандартной библиотекой даты и времени для Java до Java SE 8. Обратите внимание, что с Java SE 8 и далее пользователям предлагается перейти на java .time (JSR-310) - основная часть JDK, которая заменяет этот проект.

Конструкция позволяет использовать несколько календарных систем, но при этом предоставляет простой API. Календарь «по умолчанию» - это стандарт ISO8601, который используется многими другими стандартами. Системы григорианского, юлианского, буддистского, копти c, эфиопского c и ислами c также включены. Вспомогательные классы включают часовой пояс, длительность, формат и синтаксический анализ.

В качестве разновидности Joda-Time, вот пример кода:

public boolean isAfterPayDay(DateTime datetime) {
  if (datetime.getMonthOfYear() == 2) {   // February is month 2!!
    return datetime.getDayOfMonth() > 26;
  }
  return datetime.getDayOfMonth() > 28;
}
public Days daysToNewYear(LocalDate fromDate) {
  LocalDate newYear = fromDate.plusYears(1).withDayOfYear(1);
  return Days.daysBetween(fromDate, newYear);
}
public boolean isRentalOverdue(DateTime datetimeRented) {
  Period rentalPeriod = new Period().withDays(2).withHours(12);
  return datetimeRented.plus(rentalPeriod).isBeforeNow();
}
public String getBirthMonthText(LocalDate dateOfBirth) {
  return dateOfBirth.monthOfYear().getAsText(Locale.ENGLISH);
}
...