使用DateTimeFormatter& LocalDateTime以小时/分钟检索日/月/年(Java 1.8)

时间:2016-02-15 21:37:30

标签: java date

最好使用DateTimeFormatter和LocalDateTime来检索以前设置的日期吗?如果是这样,我如何将其实现到我的代码中(如下)?

到目前为止,我已经设法提出了

更新。我认为我或多或少知道日期应该如何工作,但现在我遇到了另一个问题,我不知道如何解决它所以希望在这里发布代码后,我能够得到一个答案我的问题。简而言之,我不确定我需要为我的工作分配解决方案实现的一些要求。这就是为什么我也会发布以下全部要求的原因。

要求: (在下面的评论中)

[u](测试被遗漏,因为稍后可以处理。)[/ u]

代码: 模型类[b](与作业完成的一切都由我完成,其余部分已经提供)[/ b]:

public class Model implements StudentModel {
private ArrayList<Student> students = new ArrayList<>();
private Student currentStudent = null;

private ArrayList<Module> modules = new ArrayList<>();
private Module currentModule;

private ArrayList<Enrolment> enrolments = new ArrayList<>();
private Enrolment currentEnrolment;

private ArrayList<Assignment> assignments = new ArrayList<>();
private Assignment currentAssignment = null;

/**
 * This method is called when the "Add" button in the students data panel is clicked. The
 * expectation is that the method will validate the data and if the data is valid create a
 * new Students object. The method will then store the students object for later retrieval.
 *  @param number   is the students identification number
 * @param name of the students
 * @param userId is the students userid / email
 */
@Override
public void addStudent(int number, String name, String userId) {
    Student student = Student.createStudent(number, name, userId, this);
    if (student != null && !foundStudent(student)) {
        this.students.add(student);
        setCurrentStudent(student);
    }
}

/**
 * This method is called when the "Modify" button in the student data panel is clicked. The
 * expectation is that the method will validate the data and if the data is valid, it will
 * modify the currently selected Student object. Since this is already in the collection,
 * there is no need to do anything else.
 * @param number is the student identification number
 * @param name of the student
 * @param userId is the students userid / email
 */
public void modifyStudent(int number, String name, String userId) {
    if (currentStudent == null) {
        setErrorMessage("No current student selected");
        return;
    }
    Student student = Student.createStudent(number, name, userId, this);
    if (student != null && (currentStudent.getNumber() == number || !foundStudent(student))) {
        currentStudent.setNumber(number);
        currentStudent.setName(name);
        currentStudent.setUserId(userId);
    }
}

/**
 * This method is called when the "Find" button in the student data panel is clicked. The
 * method should only use values from fields that have been entered. If an object is found
 * then it should be set to the current object.
 *
 * @param number of student to be found
 * @param name of student to be found
 * @param userId is the students userid / email
 * @return true if a student object is found
 */
public boolean findStudent(int number, String name, String userId) {
    setErrorMessage("");
    for (Student student: students) {
        if ((number == 0 || number == student.getNumber()) &&
                (name.equals("") || name.equals(student.getName()))) {
            setCurrentStudent(student);
            return true;
        }
    }
    setErrorMessage("No student object found");
    return false;
}

/**
 * Determine whether the students or the students number already exists in the collection
 *
 * @param student object to be inserted
 * @return true if duplicate students found or students number already used
 */
private boolean foundStudent(Student student) {
    boolean duplicate = false;
    for (Student student1 : students) {
        if (student.equals(student1)) {
            addErrorMessage("Student already in database");
            duplicate = true;
        } else if (student.getNumber() == student1.getNumber()) {
            addErrorMessage("Student number already in database");
            duplicate = true;
        }
    }
    return duplicate;
}

/**
 * This method is called when the user interface wants to know the size of the collection of
 * students.
 *
 * @return an integer value representing the number of students in the collection.
 */
public int getNumberOfStudents() {
    return students.size();
}

/**
 * This method is called when the user interface wants to access members of the collection of
 * Student objects. It provides an index of the item that it wants to retrieve.
 *
 * @param index of item to be retrieved
 * @return a students object
 */
public Student getStudentAt(int index) {
    return students.get(index);
}

/**
 * This method is called when the user interface needs to be able to display
 * information about the currently selected or entered students
 *
 * @return a string with the data for the currently selected students
 */
public Student getCurrentStudent() {
    return currentStudent;
}

/**
 * Retrieves the current student id
 *
 * @return the current student id
 */
public int getStudentNumber() {
    return currentStudent.getNumber();
}

/**
 * Retrieves the current student name
 *
 * @return the current student name
 */
public String getStudentName() {
    return currentStudent.getName();
}

/**
 * Retrieves the current student user id
 *
 * @return the current student user id
 */
public String getStudentUserId() {
    return currentStudent.getUserId();
}

/**
 * This method is called when the user selects a Student in the Student list.
 *
 * @param selectedStudent is reference to the currently selected students object.
 */
public void setCurrentStudent(Student selectedStudent) {
    if (selectedStudent == null) {
        addErrorMessage("This shouldn't be called with a null reference");
        return;
    }
    enrolments = selectedStudent.getEnrolments();
    currentEnrolment = null;
    currentStudent = selectedStudent;
}

/**
 * This method is called when the user clicks the "Delete" button on the Student panel. It
 * should assume that the request is to delete the currently selected student.
 */
public void deleteStudent() {
    if (currentStudent == null) {
        setErrorMessage("No student selected to delete");
        return;
    }
    currentStudent.deleteEnrolments();
    students.remove(currentStudent);
    clearStudent();
}

/**
 * This method should clear the currently selected student.
 */
public void clearStudent() {
    if (currentStudent != null && enrolments == currentStudent.getEnrolments()) {
        enrolments = new ArrayList<>();
        currentEnrolment = null;
    }
    currentStudent = null;
}

/**
 * This method is called when the "Add" button in the currentModule data panel is clicked. The
 * expectation is that the method will validate the data and if the data is valid create a
 * new Module object. The method will then store the currentModule object for later retrieval.
 *
 * @param code    of the currentModule
 * @param name    of the currentModule
 * @param credits that the currentModule is worth.
 */
@Override
public void addModule(String code, String name, int credits) {
    Module module = Module.createModule(code, name, credits, this);
    if (module != null && !moduleFound(module)) {
        modules.add(module);
        setCurrentModule(module);
    }
}

/**
 * This method is called when the "Modify" button in the module data panel is clicked. The
 * expectation is that the method will validate the data and if the data is valid modify the
 * current Module object.
 *
 * @param code    of the module
 * @param name    of the module
 * @param credits that the module is worth.
 */
public void modifyModule(String code, String name, int credits) {
    if (currentModule == null) {
        setErrorMessage("No current module selected");
        return;
    }
    Module module = Module.createModule(code, name, credits, this);
    if (module != null && (currentModule.getCode().equals(code) || !moduleFound(module))) {
        currentModule.setCode(code);
        currentModule.setName(name);
        currentModule.setCredits(credits);
    }
}

/**
 * This method is called when the "Find" button in the module data panel is clicked. The
 * method should only use values from fields that have been entered. If an object is found
 * then it should be set to the current object.
 *
 * @param code of the module
 * @param name of the module
 * @return true if a module was found and false otherwise
 */
public boolean findModule(String code, String name) {
    setErrorMessage("");
    for (Module module: modules) {
        if ((code.equals("") || code.equals(module.getCode())) &&
                (name.equals("") || name.equals(module.getName())) ||
                (code.equals("") && name.equals(module.getName()))) {
            setCurrentModule(module);
            return true;
        }
    }
    setErrorMessage("No matching module found");
    return false;
}

/**
 * Determine whether this would be a duplicate object or the module code already exists
 *
 * @param module object
 * @return true if module already exists
 */
private boolean moduleFound(Module module) {
    boolean found = false;
    for (Module module1 : modules) {
        if (module.equals(module1)) {
            addErrorMessage("Module already on database");
            found = true;
        } else if (module.getCode().equals(module1.getCode())) {
            addErrorMessage("Module code already on database");
            found = true;
        }
    }
    return found;
}

/**
 * This method is called when the interface needs to know the size of the collection of modules.
 *
 * @return an integer value representing the number of elements in the collection
 */
public int getNumberOfModules() {
    return modules.size();
}

/**
 * This method is called when the user interface wants to access members of the collection of
 * Module objects. It provides an index of the item that it wants to retrieve.
 *
 * @param index of item to be retrieved
 * @return a Module object
 */
public Module getModuleAt(int index) {
    return modules.get(index);
}

/**
 * This method is called when the user clicks the "Delete" button on the Module panel. It
 * should assume that the request is to delete the currently selected Module.
 */
public void deleteModule() {
    if (currentModule == null) {
        setErrorMessage("No module selected to delete");
        return;
    }
    currentModule.deleteEnrolments();
    modules.remove(currentModule);
    clearModule();
}

/**
 * This method should clear the currently selected module.
 */
public void clearModule() {
    if (currentModule != null && enrolments == currentModule.getEnrolments()) {
        enrolments = new ArrayList<>();
        currentEnrolment = null;
    }
    currentModule = null;
}

/**
 * This method is called when the user selects a Module in the Module list.
 *
 * @param selectedValue is a reference to the currently selected Module object.
 */
public void setCurrentModule(Module selectedValue) {
    if (selectedValue == null) {
        addErrorMessage("This shouldn't be called with a null reference");
        return;
    }
    enrolments = selectedValue.getEnrolments();
    currentEnrolment = null;
    currentModule = selectedValue;
}

/**
 * This method is called when the user interface needs to be able to display information
 * about the currently selected or entered currentModule
 *
 * @return the current module
 */
public Module getCurrentModule() {
    return currentModule;
}

/**
 * Retrieves the current module code
 *
 * @return module code for currently selected module
 */
public String getModuleCode() {
    return currentModule.getCode();
}

/**
 * Retrieves the current module name
 *
 * @return module name for currently selected module
 */
public String getModuleName() {
    return currentModule.getName();
}

/**
 * Retrieves the current module credits
 *
 * @return module credits for currently selected module
 */
public int getModuleCredits() {
    return currentModule.getCredits();
}

/**
 * This method is called when the "Add" button in the currentEnrolment data panel is clicked. The
 * expectation is that the method will validate the data and if the data is valid create a
 * new Enrolment object. The method will then store the currentEnrolment object for later retrieval.
 * <p/>
 * The students and currentModule data should be those that are currently selected.
 *
 * @param year   of currentEnrolment
 * @param status of the currentEnrolment
 * @param grade  assigned for the currentModule.
 */
@Override
public void addEnrolment(int year, String status, int grade) {
    Enrolment enrolment = Enrolment.createEnrolment(currentStudent, currentModule, year, status,
            grade, this);
    if (enrolment != null && !enrolmentFound(enrolment)) {
        currentStudent.addEnrolment(enrolment);
        currentModule.addEnrolment(enrolment);
        currentEnrolment = enrolment;
    }
}

/**
 * This method is called when the "Modify" button in the enrolment data panel is clicked. The
 * expectation is that the method will validate the data and if the data is valid modify the
 * current Enrolment object.
 * <p/>
 * The student and module data should be those that are currently selected.
 *
 * @param year   of enrolment
 * @param status of the enrolment
 * @param grade  assigned for the module.
 */
public void modifyEnrolment(int year, String status, int grade) {
    if (currentEnrolment == null) {
        setErrorMessage("No current enrolment selected");
        return;
    }
    Enrolment enrolment = Enrolment.createEnrolment(currentStudent, currentModule, year, status,
            grade, this);
    if (enrolment != null && (currentEnrolment.equals(enrolment) ||
            !enrolmentFound(enrolment))) {
        currentEnrolment.setStudent(currentStudent);
        currentEnrolment.setModule(currentModule);
        currentEnrolment.setYear(year);
        currentEnrolment.setStatus(status);
        currentEnrolment.setGrade(grade);
    }
}

/**
 * USed to find potentially duplicate modules
 *
 * @param enrolment object
 * @return true if module object with similar values found
 */
private boolean enrolmentFound(Enrolment enrolment) {
    for (Enrolment enrolment1 : enrolments) {
        if (enrolment.equals(enrolment1)) {
            addErrorMessage("Enrolment already in collection");
            return true;
        }
    }
    return false;
}

/**
 * This method is called when the interface needs to know the size of the collection of
 * enrolments.
 *
 * @return an integer value representing the number of elements in the collection
 */
public int getNumberOfEnrolments() {
    return enrolments.size();
}

/**
 * This method is called when the user interface wants to access members of the collection of
 * Enrolment objects. It provides an index of the item that it wants to retrieve.
 *
 * @param index of item to be retrieved
 * @return a Enrolment object
 */
public Enrolment getEnrolmentAt(int index) {
    return enrolments.get(index);
}

/**
 * Obtains the current enrolment
 *
 * @return the current enrolment
 */
public Enrolment getCurrentEnrolment() {
    return currentEnrolment;
}

/**
 * Retrieves the current enrolment year
 *
 * @return year for currently selected enrolment
 */
public int getEnrolmentYear() {
    return currentEnrolment.getYear();
}

/**
 * Retrieves the current enrolment status
 *
 * @return status for currently selected enrolment
 */
public String getEnrolmentStatus() {
    return currentEnrolment.getStatus();
}

/**
 * Retrieves the current enrolment grade
 *
 * @return grade for currently selected enrolment
 */
public int getEnrolmentGrade() {
    return currentEnrolment.getGrade();
}

/**
 * This method is called when the user clicks the "Delete" button on the Enrolment panel. It
 * should assume that the request is to delete the currently selected Enrolment.
 */
public void deleteEnrolment() {
    if (currentEnrolment == null) {
        setErrorMessage("No enrolment selected to delete");
        return;
    }
    currentEnrolment.delete();
    currentEnrolment = null;
}

/**
 * This method should clear the currently selected enrolment.
 */
public void clearEnrolment() {
    currentEnrolment = null;
}

/**
 * This method is called when the user selects an Enrolment in the Enrolment list.
 *
 * @param selectedValue is a reference to the currently selected Enrolment object.
 */
public void setCurrentEnrolment(Enrolment selectedValue) {
    currentEnrolment = selectedValue;
    currentStudent = currentEnrolment.getStudent();
    currentModule = currentEnrolment.getModule();
}



/**
 * This method is called when the "Add" button in the currentAssignment data panel is clicked. The
 * expectation is that the method will validate the data and if the data is valid create a
 * new Assignment object. The method will then store the currentAssignment object for later retrieval.
 *
 * @param title of the assignment
 * @param moduleCode of the assignment
 * @param dateTime is the date on which the assignment has to be handed in
 * @param assignmentPercent weight of the assignment towards the final grade
 *
 */

public void addAssignment(String title, String moduleCode, int assignmentPercent, LocalDateTime dateTime) {
    Assignment assignment = Assignment.createAssignment(title, moduleCode, assignmentPercent, dateTime, this);
    if (assignment != null && !foundAssignment(assignment)) {
        this.assignments.add(assignment);
        setCurrentAssignment(assignment);
    }
}
    /**
     * This method is called when the "Modify" button in the Assignment data panel is clicked. The
     * expectation is that the method will validate the data and if the data is valid, it will
     * modify the currently selected Assignment object. Since this is already in the collection,
     * there is no need to do anything else.
     * @param title of the assignment by which it can be found
     * @param moduleCode contains the module's code
     * @param assignmentPercent weight of the assignment towards the final grade
     */
public void modifyAssignment(String title, String moduleCode, int assignmentPercent, LocalDateTime dateTime) {
    if (currentAssignment == null) {
        setErrorMessage("No current assignment selected");
        return;
    }
    Assignment assignment = Assignment.createAssignment(title, moduleCode, assignmentPercent, dateTime, this );
    if (assignment != null && (currentAssignment.getTitle() == title || !foundAssignment(assignment))) {
        currentAssignment.setTitle(title);
        currentAssignment.setModuleCode(moduleCode);
        currentAssignment.setAssignmentPercent(assignmentPercent);
        currentAssignment.setDueDate(dateTime);
    }
}

/**
 * This method is called when the "Find" button in the Assignment data panel is clicked. The
 * method should only use values from fields that have been entered. If an object is found
 * then it should be set to the current object 'currentAssignment'.
 *
 * @param title of the assignment by which it can be found
 */
public boolean findAssignment(String title) {
    setErrorMessage("");
    for (Assignment assignment: assignments) {
        if (title.equals("") || title.equals(assignment.getTitle())){
            setCurrentAssignment(assignment);
            return true;
        }
    }
    setErrorMessage("No assignment object found");
    return false;
}

/**
 * Determine whether the assignments or the assignment already exists in the database
 *
 * @param assignment object to be inserted
 * @return true if duplicate assignments found or assignment number already used
 */
private boolean foundAssignment(Assignment assignment) {
    boolean found = false;
    for (Assignment assignment1 : assignments) {
        if (assignment.equals(assignment1)) {
            addErrorMessage("Assignment already in database");
            found = true;
        } else if (assignment.getTitle().equals(assignment1.getTitle())) {
            addErrorMessage("Assignment title already in database");
            found = true;
        }
    }
    return found;
}

/**
 * This method is called when the user selects an Assignment in the Assignment list.
 *
 * @param selectedValue is a reference to the currently selected Module object.
 */
public void setCurrentAssignment(Assignment selectedValue) {
    if (selectedValue == null) {
        addErrorMessage("This shouldn't be called with a null reference");
        return;
    }
//        assignments = selectedValue.getAssignments();
//        currentAssignments = null;
//        currentAssignment = selectedValue;
}

/**
 * This method is called when the user interface needs to be able to display information
 * about the currently selected or entered currentAssignment
 *
 * @return the current assignment
 */
public Assignment getCurrentAssignment() {
    return currentAssignment;
}

/**
 * This method should clear the currently selected assignment.
 */
public void clearAssignment() {
    currentAssignment = null;
}

/**
 * Retrieves the current assignment title
 *
 * @return assignment title for currently selected assignment
 */
public String getAssignmentTitle() {
    return currentAssignment.getTitle();
}

/**
 * Retrieves the due date for the assignment
 *
 * @return the due date of the assignment
 */
public int getAssignmentDay() {
    return dateTime.getDayOfMonth();
}

/**
 * Retrieves the month on which the assignment has to be handed in
 *
 * @return due month for currently selected assignment
 */
public String getAssignmentMonth() {
    return currentAssignment.getMonth();
}

/**
 * Retrieves the year on which the assignment has to be handed in
 *
 * @return due year for currently selected assignment
 */
public String getAssignmentYear() {
    return currentAssignment.getYear();
}

/**
 * Retrieves the time on which the assignment has to be handed in
 *
 * @return due hour for currently selected assignment
 */
public String getAssignmentHour() {
    return currentAssignment.getHour();
}

/**
 * Retrieves the time on which the assignment has to be handed in
 *
 * @return due minutes for currently selected assignment
 */
public String getAssignmentMinute() {
    return currentAssignment.getMins();
}
/**
 * Retrieves the weight of the assignment towards the overall grade
 *
 * @return percentage weight for currently selected assignment
 */
public int getAssignmentPercent() {
    return currentModule.getPercentage();
}


/*
 * This block of code is the implementation for the Error Message interface.
 */
private String errorMessage = "";

/**
 * This method simply replaces any message text currently stored with that passed in as a
 * parameter. In effect, it clears past error messages and replaces them with the new message.
 *
 * @param errorMessage is the text of the message to be displayed
 */
public void setErrorMessage(String errorMessage) {
    this.errorMessage = errorMessage;
}

/**
 * This method appends the new message to the end of any existing error messages inserting
 * the new line sequence between the existing error messages and the new message.
 *
 * @param errorMessage is the text of the message to be displayed
 */
public void addErrorMessage(String errorMessage) {
    this.errorMessage += (this.errorMessage.length() == 0 ? "" : "\n") + errorMessage;
}

/**
 * This method is called by the user interface when it wants to obtain the error messages that
 * are to be displayed.
 *
 * @return the current stored error message
 */
public String getErrorMessage() {
    return errorMessage;
}
}

2 个答案:

答案 0 :(得分:1)

我的建议是仅使用LocalDateTime来存储日期而不是每个由日期组成的单独字段(即日,月等),因为您可以随时提取和设置所有日期。

仅使用LocalDateTime,您可以同时使用:

public void setDay(int day) {
    dateTime.withDayOfMonth(day);
}

public int getDay() {
    return dateTime.getOfMonth();
}

这将允许您删除所有这些字段:

private int day;
private int month;
private int year;
private int hour;
private int minute;

并有一个更清洁的构造函数:

public Assignment(String title, String moduleCode, int assignmentPercent, LocalDateTime dateTime) {
    // ...
}

答案 1 :(得分:0)

作业?

对于家庭作业,您的教师可能会寻找您将每个组件(年,月,小时等)存储为单独的数字以用于教学目的,而不是使用日期时间类。

在现实世界中,你当然应该使用java.time框架。

时区

在现实世界的项目中,您应该知道在没有时区的情况下跟踪日期时间会产生麻烦。

从技术上讲,LocalDateTime没有时区。 LocalDateTime实际上没有任何意义,并且在您应用某个时间轴赋予其意义之前,并不表示时间轴上的时刻。您可以假设 LocalDateTime的预期时区,但这样做有风险且草率。这种假设会导致以后出现问题,尤其是如果任何此应用程序可能在以后处理来自其他时区的此类数据或从其他时区导入/导出数据的可能性。

UTC

日期时间工作的最佳做​​法是使用UTC进行业务逻辑,存储和数据交换。仅在需要时使用特定时区的时间,例如向用户显示。

要获得UTC,请按原样使用代码解析为LocalDateTime。然后应用隐式假定该值的时区。说,Québec。应用该时区ZoneId,以生成ZonedDateTime。最后,要转到UTC,请ZonedDateTime询问Instant

您可以从概念上考虑这一点:ZonedDateTime = Instant + ZoneId

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = myLocalDateTime.atZone( zoneId );

从中提取Instant,在UTC时间轴上的一个时刻。这些Instant实例是您应该用于大多数业务逻辑和数据存储的实例。

Instant instant = zdt.toInstant();

要稍后向用户演示,请应用预期/所需时区。

ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

生成格式化字符串

使用相同的格式化程序生成字符串。或者让java.time使用DateTimeFormatter为您格式化语言和语言。指定Locale确定(a)格式化字符串的文化规范,以及(b)将日期和月份的名称翻译成特定的人类语言。

DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.LONG );
formatter = formatter.withLocale( Locale.CANADA_FRENCH );
String output = zdt.format( formatter );

示例代码

把它们放在一起。

String input = "29/04/2016 at 23:59";
DateTimeFormatter formatterForParsing = DateTimeFormatter.ofPattern ( "dd/MM/yyyy 'at' HH:mm" );
LocalDateTime ldt = LocalDateTime.parse ( input , formatterForParsing );

ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime zdt = ldt.atZone ( zoneId );
Instant instant = zdt.toInstant ();

// Later, in other code, apply a time zone as required such as presentation to a user.
ZonedDateTime zdtForPresentation = ZonedDateTime.ofInstant ( instant , zoneId );

DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime ( FormatStyle.LONG );
formatter = formatter.withLocale ( Locale.CANADA_FRENCH );
String output = zdtForPresentation.format ( formatter );

System.out.println ( "input: " + input + " | ldt: " + ldt + " | zdt: " + zdt + " | instant: " + instant + " | output: " + output );
  

输入:2016年4月29日23:59 | ldt:2016-04-29T23:59 | zdt:2016-04-29T23:59-04:00 [美国/蒙特利尔] |瞬间:2016-04-30T03:59:00Z |输出:29 avril 2016 23:59:00美国东部时间

半开

另一个问题......试图在最后一刻确定截止日期是错误的做法。

在你23:59的情况下,你忽略了一个时间可以有一小部分时间。在旧的日期时间类中,这意味着最多3位小数位23:59.999。但有些数据库使用微秒,因此有6位,23:59.999999。现在在java.time和其他一些数据库中,9位数为纳秒,23:59.999999999

日期工作中更好的方法称为半开,其中开头是包含,结尾是独占。所以截止日期时间不是4月29日的最后一刻,而是4月30日的第一个时刻。必须在第二天的第一个时刻之前收到作业。 就像你在13:00上课时要求你的女儿在之前下午1点。

所以比较逻辑是<而不是<=

Boolean onTime = whenReceived.isBefore( whenDue );

Poof ,你的第二个二难困境已经消失。

要获得第二天的第一个时刻,请转到ZonedDateTime,添加一天,转换为LocalDate(仅限日期的值),在通过时atStartOfDay同一时区再次。

ZonedDateTime firstMomentOfNextDay = zdt.plusDays( 1 ).toLocalDate().atStartOfDay( zoneId );