Java 停车费计算
Java Parking fee calculations
看来,我找不到问题的答案,所以我在这里,首先在 Whosebug 上:)
会提到的If语句树:
buttonSzamol.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//Változók
int StartHour = 18;
int StartMin = 50;
int StopHour = 20;
int StopMin = 49;
int DayTimeIntervalStart = 6;
int DayTimeIntervalStop = 17;
int NightTimeIntervalLateStart = 18;
int NightTimeIntervalLateStop = 23;
int NightTimeIntervalEarlyStart = 0;
int NightTimeIntervalEarlyStop = 5;
int DayHoursTotal = 0;
int NightHoursTotal = 0;
int DayTimePricePerHour = Integer.parseInt(NappaliOraDij.getText());
int NightTimePricePerHour = Integer.parseInt(EjszakaiOraDij.getText());
int StartDay = Integer.parseInt((DatumStart.getText()).replace(".", ""));
int StopDay = Integer.parseInt((DatumStart.getText()).replace(".", ""));
//1 started hour
if( (StartDay == StopDay) && ( ( (StartHour == StopHour) && (StartMin < StopMin) ) || ( ((StartHour + 1) == StopHour) && (StartMin >= StopMin) ) ) ) {
if((DayTimeIntervalStart <= StartHour) && (StopHour <= DayTimeIntervalStop)) {
DayHoursTotal++;
}
if((NightTimeIntervalLateStart <= StartHour) && (StopHour <= NightTimeIntervalLateStop)) {
NightHoursTotal++;
}
} else/*More hours*/if( (StartDay == StopDay) && ( ( (StartHour < StopHour) && (StartMin <= StopMin) ) || ( (StartHour < StopHour) && (StartMin > StopMin) ) ) ) {
if( (StartHour < StopHour) && (StartMin < StopMin) ) {
if((DayTimeIntervalStart <= StartHour) && (StopHour <= DayTimeIntervalStop)) {
DayHoursTotal = DayHoursTotal + (StopHour - StartHour);
DayHoursTotal++;
}
if((NightTimeIntervalLateStart <= StartHour) && (StopHour <= NightTimeIntervalLateStop)) {
NightHoursTotal = NightHoursTotal + (StopHour - StartHour);
NightHoursTotal++;
}
}else if(( (StartHour < StopHour) && (StartMin >= StopMin) )) {
if((DayTimeIntervalStart <= StartHour) && (StopHour <= DayTimeIntervalStop)) {
DayHoursTotal = DayHoursTotal + (StopHour - StartHour);
if(StartMin != StopMin) {
DayHoursTotal--;
}
}
if((NightTimeIntervalLateStart <= StartHour) && (StopHour <= NightTimeIntervalLateStop)) {
NightHoursTotal = NightHoursTotal + (StopHour - StartHour);
if(StartMin != StopMin) {
NightHoursTotal--;
}
}
}
}
NappaliOrak.setText(Integer.toString(DayHoursTotal));
EjszakaiOrak.setText(Integer.toString(NightHoursTotal));
OrakOsszesen.setText(Integer.toString(DayHoursTotal + NightHoursTotal));
NappaliOsszeg.setText(Integer.toString(DayHoursTotal * DayTimePricePerHour));
EjszakaiOsszeg.setText(Integer.toString(NightHoursTotal * NightTimePricePerHour));
VegOsszeg.setText(Integer.toString((DayHoursTotal * DayTimePricePerHour) + (NightHoursTotal * NightTimePricePerHour)));
}
});
所以,简而言之,问题是。
我试图为我的同事创建一个停车费计算器。
主要思想是,它需要计算客户端开始了多少白天和多少夜间时间,并且需要计算这些时间的价格。我已将 StartHour/Min-StopHour/Min 字段更改为直接整数,以便更容易理解。我不知道是否有用于此的模块,但我开始使用大量 If 语句来执行此操作,我只是在其中纠结。在包含的 pastebin 中,有开始时间 18:50 和停止时间 20:49。如果我们输入此数据,则输出应为 2 天开始的小时数。现在,如果分钟相同,则不算作开始时间。但是,如果我们将输入更改为 20:51,那么它又开始了一个小时,因此 DayHoursTotal 应该等于 3。
提前感谢您的帮助。如果您对我的代码或想法有更多疑问,请提问。
首先,您需要以不同的方式解析整数。你的方法很危险,例如丢失信息。另外,您需要使代码具有故障安全性,以防有人试图输入无效的值。参考这个问题:How do I convert a String to an int in Java?
除此之外,只用几分钟和几个小时来工作总是很困难。我建议使用以毫秒为单位的绝对时间,这样可以更容易地进行计算。参考这个问题:
看来您正在尝试计算 开始 小时,不仅在 2 次 之间,而且在不同的 之间]日期.
为此,最好使用 java.time
包,更具体地说是 LocalDateTime
class。
LocalDateTime.of(startYear, startMonth, startDay, startHour, startMinute)
LocalDateTimes
与 Java 中的 between()
方法相结合 8 ChronoUnit
class 得到您所需要的。
ChronoUnit.MINUTES.between(Temporal t1, Temporal t2)
PS:你不需要那么多'interval'变量。
day (dayTimeIntervalStart
) 和 night (nightTimeIntervalLateStart
) 率的开始时间就足够了。
前后的小时费率可以从这两个时间间隔得出。
剧透!!如果您想自己进一步调查,请移开视线! ;)
这是一个可运行的代码示例,显示了超过 1 天的停车逻辑:
(我省略了用户输入 parsing/logic,因为这取决于您的实现)
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
public class ParkingFee {
private static long hoursDifference(LocalDateTime ldt1, LocalDateTime ldt2) {
long minutesDiff = ChronoUnit.MINUTES.between(ldt1, ldt2);
long hoursDiff = Math.round(Math.ceil(minutesDiff/60.0));
return hoursDiff;
}
public static long hoursDifference(
int startDay, int startMonth, int startYear, int startHour, int startMinute,
int endDay, int endMonth, int endYear, int endHour, int endMinute) {
return hoursDifference(
LocalDateTime.of(startYear, startMonth, startDay, startHour, startMinute),
LocalDateTime.of(endYear, endMonth, endDay, endHour, endMinute));
}
public static int determineDayCycle(int dayTimeIntervalStart, int nightTimeIntervalLateStart) {
return nightTimeIntervalLateStart - dayTimeIntervalStart;
}
public static void main(String[] args) {
// Hourly rates
int dayTimePricePerHour = 5;
int nightTimePricePerHour = 10;
// Rate intervals
int dayTimeIntervalStart = 6;
int nightTimeIntervalLateStart = 18;
// Counted hours per rate
int dayHoursTotal = 0;
int nightHoursTotal = 0;
// Start date and time
int startYear = 2019;
int startMonth = 1;
int startDay = 1;
int startHour = 20;
int startMinute = 50;
// End date and time
int endYear = 2019;
int endMonth = 1;
int endDay = 3;
int endHour = 2;
int endMinute = 49;
// Calculate the hours difference
long hourDiff = hoursDifference(
startDay, startMonth, startYear, startHour, startMinute,
endDay, endMonth, endYear, endHour, endMinute);
System.out.println("Hour difference found: "+ hourDiff);
// Handle parking for full days
if (hourDiff > 24) {
int dayCycle = determineDayCycle(dayTimeIntervalStart, nightTimeIntervalLateStart);
long fullDays = hourDiff / 24;
nightHoursTotal += (24-dayCycle)*fullDays;
dayHoursTotal += dayCycle*fullDays;
hourDiff = hourDiff % 24;
}
// Handle the parking for less than full day
while (hourDiff > 0) {
if (startHour < dayTimeIntervalStart) { // Before the day interval -> night
nightHoursTotal++;
} else if(startHour < nightTimeIntervalLateStart) { // Before the night interval -> day
dayHoursTotal++;
} else { // After the day interval -> night
nightHoursTotal++;
}
startHour++;
if (startHour > 23) // At midnight reset the hour to 0
startHour = 0;
hourDiff--;
}
System.out.println("Day hours: "+ dayHoursTotal);
System.out.println("Night hours: "+ nightHoursTotal);
System.out.println("Total hours: "+ (dayHoursTotal + nightHoursTotal));
System.out.println("Day rate charged at "+ dayTimePricePerHour +": "+ (dayHoursTotal * dayTimePricePerHour));
System.out.println("Night rate charged at "+ nightTimePricePerHour +": "+ (nightHoursTotal * nightTimePricePerHour));
System.out.println("Total rate charged: "+ ((dayHoursTotal * dayTimePricePerHour) + (nightHoursTotal * nightTimePricePerHour)));
}
}
这输出:
Hour difference found: 30
Day hours: 12
Night hours: 18
Total hours: 30
Day rate charged at 5: 60
Night rate charged at 10: 180
Total rate charged: 240
分而治之
将大逻辑分解成小块更容易实现
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
class Scratch {
static int StartHour = 18;
static int StartMin = 50;
static int StopHour = 20;
static int StopMin = 48;
static int DayTimeIntervalStart = 6;
static int DayTimeIntervalStop = 17;
static int NightTimeIntervalLateStart = 18;
static int NightTimeIntervalLateStop = 23;
static int NightTimeIntervalEarlyStart = 0;
static int NightTimeIntervalEarlyStop = 5;
static int DayTimePricePerHour = 10;
static int NightTimePricePerHour = 5;
static LocalTime dayStart = LocalTime.of(DayTimeIntervalStart, 0);
static LocalTime dayStop = LocalTime.of(DayTimeIntervalStop, 0);
static LocalTime nightEarlyStart = LocalTime.of(NightTimeIntervalEarlyStart, 0);
static LocalTime nightEarlyStop = LocalTime.of(NightTimeIntervalEarlyStop, 0);
static LocalTime nightLateStart = LocalTime.of(NightTimeIntervalLateStart, 0);
static LocalTime nightLateStop = LocalTime.of(NightTimeIntervalLateStop, 0);
public static void main(String[] args) {
LocalDateTime start = LocalDateTime.of(2019, 1, 1, StartHour, StartMin);
LocalDateTime stop = LocalDateTime.of(2019, 1, 1, StopHour, StopMin);
for(int i = 0; i < 3; i++){
stop = stop.plusMinutes(1L);
System.out.println(process(start, stop));
System.out.println("******");
}
stop = stop.plusDays(1L);
System.out.println(process(start, stop));
System.out.println("******");
}
public static int process(LocalDateTime start, LocalDateTime stop){
System.out.println(String.format("checking between %s and %s", start, stop));
if(stop.toLocalDate().isAfter(start.toLocalDate())){
// start and stop not on the same date
// split the computation, first currentDay then the rest
LocalDateTime endOfDay = LocalDateTime.of(start.toLocalDate(), LocalTime.MAX);
int resultForCurrentDay = process(start, endOfDay);
// not for the rest
LocalDateTime startOfNextDay = LocalDateTime.of(start.toLocalDate().plusDays(1L), LocalTime.MIN);
int resultForRest = process(startOfNextDay, stop);
return resultForCurrentDay + resultForRest;
}else{
// start and stop on the same date
return processIntraDay(start, stop);
}
}
private static int processIntraDay(LocalDateTime start, LocalDateTime stop) {
int result = 0;
LocalTime startTime = start.toLocalTime();
LocalTime stopTime = stop.toLocalTime();
// step 1: check early morning
result += checkBoundaries(startTime, stopTime, nightEarlyStart, nightEarlyStop, NightTimePricePerHour);
// step 2: check day time
result += checkBoundaries(startTime, stopTime, dayStart, dayStop, DayTimePricePerHour);
// step 3: check late night
result += checkBoundaries(startTime, stopTime, nightLateStart, nightLateStop, NightTimePricePerHour);
return result;
}
private static int checkBoundaries(LocalTime startTime, LocalTime stopTime, LocalTime lowerBoundary, LocalTime upperBoundary, int priceRatePerHour) {
// check if the period [start;stop] is crossing the period [lowerBoundary;upperBoundary]
if(stopTime.isAfter(lowerBoundary) && startTime.isBefore(upperBoundary)){
// truncate start time to not be before lowerBoundary
LocalTime actualStart = (startTime.isBefore(lowerBoundary))?lowerBoundary:startTime;
// symetrically, truncate stop to not be after upperBounday
LocalTime actualStop = (stopTime.isAfter(upperBoundary))?upperBoundary:stopTime;
// now that we have the proper start and stop of the period, let's compute the price of it
return compute(actualStart, actualStop, priceRatePerHour);
}else{
return 0;
}
}
private static int compute(LocalTime startTime, LocalTime stopTime, int pricePerHour) {
Duration duration = Duration.between(startTime, stopTime);
int hours = (int) duration.toHours();
long minutes = duration.toMinutes();
if(minutes % 60 > 0L){
// hour started, increasing the number
hours++;
}
int result = hours * pricePerHour;
System.out.println(String.format("%d hours at %d price/h => %d", hours, pricePerHour, result));
return result;
}
}
直接去计算最终价格。更新以存储白天和晚上的总小时数应该是一个很大的挑战
我的例子的结果:
checking between 2019-01-01T18:50 and 2019-01-01T20:49
2 hours at 5 price/h => 10
10
******
checking between 2019-01-01T18:50 and 2019-01-01T20:50
2 hours at 5 price/h => 10
10
******
checking between 2019-01-01T18:50 and 2019-01-01T20:51
3 hours at 5 price/h => 15
15
******
checking between 2019-01-01T18:50 and 2019-01-02T20:51
checking between 2019-01-01T18:50 and 2019-01-01T23:59:59.999999999
5 hours at 5 price/h => 25
checking between 2019-01-02T00:00 and 2019-01-02T20:51
5 hours at 5 price/h => 25
11 hours at 10 price/h => 110
3 hours at 5 price/h => 15
175
******
可能需要更多测试以确保它在所有条件下都良好,但对您来说应该是一个有用的起点
时区
您的代码和其他答案无法解决 time zone 异常。如果您跟踪的是实际时刻,即人们实际停车的时间,而不是理论上的 24 小时漫长的一天,那么您必须考虑夏令时 (DST) 等异常情况。世界各地的政治家都表现出重新定义其管辖范围内的时区的倾向。所以天数可以是任意长度,例如 25 小时、23 小时、23.5 小时、24.25 小时或其他。
时区是特定地区人民使用的 offset-from-UTC 过去、现在和未来变化的历史。
LocalDateTime
class 完全错误 class 用于此目的。 class 故意没有时区或与 UTC 的偏移量的概念。您可以将其用作代码中的构建块,但必须为其分配 ZoneId
以通过 ZonedDateTime
class.
确定实际时刻
ZoneId
指定您的时区。
如果未指定时区,JVM 将隐式应用其当前默认时区。该默认值在运行时可能 change at any moment (!),因此您的结果可能会有所不同。最好将 desired/expected 时区明确指定为参数。如果关键,请与您的用户确认区域。
以Continent/Region
的格式指定一个proper time zone name,例如America/Montreal
、Africa/Casablanca
或Pacific/Auckland
。切勿使用 2-4 个字母的缩写,例如 EST
或 IST
,因为它们 不是 真正的时区,不是标准化的,甚至不是唯一的(!)。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
如果你想使用 JVM 当前的默认时区,请求它并作为参数传递。如果省略,代码将变得难以阅读,因为我们不确定您是否打算使用默认值,或者您是否像许多程序员一样没有意识到这个问题。
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Assemble 确定时刻的日期、时间和时区
Assemble 您的日期和时间。
LocalDate startDate = LocalDate.of( 2019 , 1 , 23 ) ; // 23rd of January in 2019.
LocalTime startTime = LocalTime.of( 18 , 50 ) ; // 6:50 PM.
ZonedDateTime startMoment = ZonedDateTime.of( startDate , startTime , z ) ;
LocalDate stopDate = LocalDate.of( 2019 , 1 , 23 ) ; // 23rd of January in 2019.
LocalTime stopTime = LocalTime.of( 20 , 50 ) ; // Two hours later, exactly — maybe! Depends on any anomalies at that time in that zone.
ZonedDateTime stopMoment = ZonedDateTime.of( stopDate , stopTime , z ) ;
➥ 请注意,在这个例子中,我们 可能 的时间跨度恰好是 2 小时,但 可能不是 。可能是 3 小时或其他时间长度,具体取决于该区域当时为该日期安排的异常情况。
经过的时间
要根据天数(24 小时时间块,与日历无关)、小时、分钟和秒来计算经过的时间,请使用 Duration
。 (对于年月日,使用 Period
。)
Duration d = Duration.between( startMoment , stopMoment ) ;
以整小时为单位询问整个时间跨度。
long hours = d.toHours() ; // Entire duration as count of whole hours.
半开
In the included pastebin, there is starting time 18:50 and stop time 20:49. If we input this data, the output should be 2 started day hours. Now if the minute is the same, it does not count as a started hour. But if we change the input to 20:51, then it started an another hour, so the DayHoursTotal should be equal to 3.
这种方法被称为半开,当开始是包含,而结尾是不包含。这通常用于日期时间处理。 Duration
和 Period
class 应用了这种方法。
但要小心单独匹配分钟数。您的日期时间对象可能会保持秒数 and/or 小数秒,这会影响您的算法。作为一种习惯,如果粒度可能比您想要的更小,请显式截断日期时间对象。例如,ZonedDateTime.truncatedTo
。
费率变化
显然,利率变化使事情复杂化。其他答案似乎已经涵盖了这一点,所以我不再重复。但我可以添加一个重要提示:请参阅 ThreeTen-Extra 的 classes Interval
和 LocalDateRange
可能对您有所帮助。它们包括用于重叠、包含、邻接等的便捷比较方法。
看来,我找不到问题的答案,所以我在这里,首先在 Whosebug 上:)
会提到的If语句树:
buttonSzamol.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//Változók
int StartHour = 18;
int StartMin = 50;
int StopHour = 20;
int StopMin = 49;
int DayTimeIntervalStart = 6;
int DayTimeIntervalStop = 17;
int NightTimeIntervalLateStart = 18;
int NightTimeIntervalLateStop = 23;
int NightTimeIntervalEarlyStart = 0;
int NightTimeIntervalEarlyStop = 5;
int DayHoursTotal = 0;
int NightHoursTotal = 0;
int DayTimePricePerHour = Integer.parseInt(NappaliOraDij.getText());
int NightTimePricePerHour = Integer.parseInt(EjszakaiOraDij.getText());
int StartDay = Integer.parseInt((DatumStart.getText()).replace(".", ""));
int StopDay = Integer.parseInt((DatumStart.getText()).replace(".", ""));
//1 started hour
if( (StartDay == StopDay) && ( ( (StartHour == StopHour) && (StartMin < StopMin) ) || ( ((StartHour + 1) == StopHour) && (StartMin >= StopMin) ) ) ) {
if((DayTimeIntervalStart <= StartHour) && (StopHour <= DayTimeIntervalStop)) {
DayHoursTotal++;
}
if((NightTimeIntervalLateStart <= StartHour) && (StopHour <= NightTimeIntervalLateStop)) {
NightHoursTotal++;
}
} else/*More hours*/if( (StartDay == StopDay) && ( ( (StartHour < StopHour) && (StartMin <= StopMin) ) || ( (StartHour < StopHour) && (StartMin > StopMin) ) ) ) {
if( (StartHour < StopHour) && (StartMin < StopMin) ) {
if((DayTimeIntervalStart <= StartHour) && (StopHour <= DayTimeIntervalStop)) {
DayHoursTotal = DayHoursTotal + (StopHour - StartHour);
DayHoursTotal++;
}
if((NightTimeIntervalLateStart <= StartHour) && (StopHour <= NightTimeIntervalLateStop)) {
NightHoursTotal = NightHoursTotal + (StopHour - StartHour);
NightHoursTotal++;
}
}else if(( (StartHour < StopHour) && (StartMin >= StopMin) )) {
if((DayTimeIntervalStart <= StartHour) && (StopHour <= DayTimeIntervalStop)) {
DayHoursTotal = DayHoursTotal + (StopHour - StartHour);
if(StartMin != StopMin) {
DayHoursTotal--;
}
}
if((NightTimeIntervalLateStart <= StartHour) && (StopHour <= NightTimeIntervalLateStop)) {
NightHoursTotal = NightHoursTotal + (StopHour - StartHour);
if(StartMin != StopMin) {
NightHoursTotal--;
}
}
}
}
NappaliOrak.setText(Integer.toString(DayHoursTotal));
EjszakaiOrak.setText(Integer.toString(NightHoursTotal));
OrakOsszesen.setText(Integer.toString(DayHoursTotal + NightHoursTotal));
NappaliOsszeg.setText(Integer.toString(DayHoursTotal * DayTimePricePerHour));
EjszakaiOsszeg.setText(Integer.toString(NightHoursTotal * NightTimePricePerHour));
VegOsszeg.setText(Integer.toString((DayHoursTotal * DayTimePricePerHour) + (NightHoursTotal * NightTimePricePerHour)));
}
});
所以,简而言之,问题是。 我试图为我的同事创建一个停车费计算器。 主要思想是,它需要计算客户端开始了多少白天和多少夜间时间,并且需要计算这些时间的价格。我已将 StartHour/Min-StopHour/Min 字段更改为直接整数,以便更容易理解。我不知道是否有用于此的模块,但我开始使用大量 If 语句来执行此操作,我只是在其中纠结。在包含的 pastebin 中,有开始时间 18:50 和停止时间 20:49。如果我们输入此数据,则输出应为 2 天开始的小时数。现在,如果分钟相同,则不算作开始时间。但是,如果我们将输入更改为 20:51,那么它又开始了一个小时,因此 DayHoursTotal 应该等于 3。
提前感谢您的帮助。如果您对我的代码或想法有更多疑问,请提问。
首先,您需要以不同的方式解析整数。你的方法很危险,例如丢失信息。另外,您需要使代码具有故障安全性,以防有人试图输入无效的值。参考这个问题:How do I convert a String to an int in Java?
除此之外,只用几分钟和几个小时来工作总是很困难。我建议使用以毫秒为单位的绝对时间,这样可以更容易地进行计算。参考这个问题:
看来您正在尝试计算 开始 小时,不仅在 2 次 之间,而且在不同的 之间]日期.
为此,最好使用 java.time
包,更具体地说是 LocalDateTime
class。
LocalDateTime.of(startYear, startMonth, startDay, startHour, startMinute)
LocalDateTimes
与 Java 中的 between()
方法相结合 8 ChronoUnit
class 得到您所需要的。
ChronoUnit.MINUTES.between(Temporal t1, Temporal t2)
PS:你不需要那么多'interval'变量。
day (dayTimeIntervalStart
) 和 night (nightTimeIntervalLateStart
) 率的开始时间就足够了。
前后的小时费率可以从这两个时间间隔得出。
剧透!!如果您想自己进一步调查,请移开视线! ;)
这是一个可运行的代码示例,显示了超过 1 天的停车逻辑:
(我省略了用户输入 parsing/logic,因为这取决于您的实现)
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
public class ParkingFee {
private static long hoursDifference(LocalDateTime ldt1, LocalDateTime ldt2) {
long minutesDiff = ChronoUnit.MINUTES.between(ldt1, ldt2);
long hoursDiff = Math.round(Math.ceil(minutesDiff/60.0));
return hoursDiff;
}
public static long hoursDifference(
int startDay, int startMonth, int startYear, int startHour, int startMinute,
int endDay, int endMonth, int endYear, int endHour, int endMinute) {
return hoursDifference(
LocalDateTime.of(startYear, startMonth, startDay, startHour, startMinute),
LocalDateTime.of(endYear, endMonth, endDay, endHour, endMinute));
}
public static int determineDayCycle(int dayTimeIntervalStart, int nightTimeIntervalLateStart) {
return nightTimeIntervalLateStart - dayTimeIntervalStart;
}
public static void main(String[] args) {
// Hourly rates
int dayTimePricePerHour = 5;
int nightTimePricePerHour = 10;
// Rate intervals
int dayTimeIntervalStart = 6;
int nightTimeIntervalLateStart = 18;
// Counted hours per rate
int dayHoursTotal = 0;
int nightHoursTotal = 0;
// Start date and time
int startYear = 2019;
int startMonth = 1;
int startDay = 1;
int startHour = 20;
int startMinute = 50;
// End date and time
int endYear = 2019;
int endMonth = 1;
int endDay = 3;
int endHour = 2;
int endMinute = 49;
// Calculate the hours difference
long hourDiff = hoursDifference(
startDay, startMonth, startYear, startHour, startMinute,
endDay, endMonth, endYear, endHour, endMinute);
System.out.println("Hour difference found: "+ hourDiff);
// Handle parking for full days
if (hourDiff > 24) {
int dayCycle = determineDayCycle(dayTimeIntervalStart, nightTimeIntervalLateStart);
long fullDays = hourDiff / 24;
nightHoursTotal += (24-dayCycle)*fullDays;
dayHoursTotal += dayCycle*fullDays;
hourDiff = hourDiff % 24;
}
// Handle the parking for less than full day
while (hourDiff > 0) {
if (startHour < dayTimeIntervalStart) { // Before the day interval -> night
nightHoursTotal++;
} else if(startHour < nightTimeIntervalLateStart) { // Before the night interval -> day
dayHoursTotal++;
} else { // After the day interval -> night
nightHoursTotal++;
}
startHour++;
if (startHour > 23) // At midnight reset the hour to 0
startHour = 0;
hourDiff--;
}
System.out.println("Day hours: "+ dayHoursTotal);
System.out.println("Night hours: "+ nightHoursTotal);
System.out.println("Total hours: "+ (dayHoursTotal + nightHoursTotal));
System.out.println("Day rate charged at "+ dayTimePricePerHour +": "+ (dayHoursTotal * dayTimePricePerHour));
System.out.println("Night rate charged at "+ nightTimePricePerHour +": "+ (nightHoursTotal * nightTimePricePerHour));
System.out.println("Total rate charged: "+ ((dayHoursTotal * dayTimePricePerHour) + (nightHoursTotal * nightTimePricePerHour)));
}
}
这输出:
Hour difference found: 30
Day hours: 12
Night hours: 18
Total hours: 30
Day rate charged at 5: 60
Night rate charged at 10: 180
Total rate charged: 240
分而治之
将大逻辑分解成小块更容易实现
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
class Scratch {
static int StartHour = 18;
static int StartMin = 50;
static int StopHour = 20;
static int StopMin = 48;
static int DayTimeIntervalStart = 6;
static int DayTimeIntervalStop = 17;
static int NightTimeIntervalLateStart = 18;
static int NightTimeIntervalLateStop = 23;
static int NightTimeIntervalEarlyStart = 0;
static int NightTimeIntervalEarlyStop = 5;
static int DayTimePricePerHour = 10;
static int NightTimePricePerHour = 5;
static LocalTime dayStart = LocalTime.of(DayTimeIntervalStart, 0);
static LocalTime dayStop = LocalTime.of(DayTimeIntervalStop, 0);
static LocalTime nightEarlyStart = LocalTime.of(NightTimeIntervalEarlyStart, 0);
static LocalTime nightEarlyStop = LocalTime.of(NightTimeIntervalEarlyStop, 0);
static LocalTime nightLateStart = LocalTime.of(NightTimeIntervalLateStart, 0);
static LocalTime nightLateStop = LocalTime.of(NightTimeIntervalLateStop, 0);
public static void main(String[] args) {
LocalDateTime start = LocalDateTime.of(2019, 1, 1, StartHour, StartMin);
LocalDateTime stop = LocalDateTime.of(2019, 1, 1, StopHour, StopMin);
for(int i = 0; i < 3; i++){
stop = stop.plusMinutes(1L);
System.out.println(process(start, stop));
System.out.println("******");
}
stop = stop.plusDays(1L);
System.out.println(process(start, stop));
System.out.println("******");
}
public static int process(LocalDateTime start, LocalDateTime stop){
System.out.println(String.format("checking between %s and %s", start, stop));
if(stop.toLocalDate().isAfter(start.toLocalDate())){
// start and stop not on the same date
// split the computation, first currentDay then the rest
LocalDateTime endOfDay = LocalDateTime.of(start.toLocalDate(), LocalTime.MAX);
int resultForCurrentDay = process(start, endOfDay);
// not for the rest
LocalDateTime startOfNextDay = LocalDateTime.of(start.toLocalDate().plusDays(1L), LocalTime.MIN);
int resultForRest = process(startOfNextDay, stop);
return resultForCurrentDay + resultForRest;
}else{
// start and stop on the same date
return processIntraDay(start, stop);
}
}
private static int processIntraDay(LocalDateTime start, LocalDateTime stop) {
int result = 0;
LocalTime startTime = start.toLocalTime();
LocalTime stopTime = stop.toLocalTime();
// step 1: check early morning
result += checkBoundaries(startTime, stopTime, nightEarlyStart, nightEarlyStop, NightTimePricePerHour);
// step 2: check day time
result += checkBoundaries(startTime, stopTime, dayStart, dayStop, DayTimePricePerHour);
// step 3: check late night
result += checkBoundaries(startTime, stopTime, nightLateStart, nightLateStop, NightTimePricePerHour);
return result;
}
private static int checkBoundaries(LocalTime startTime, LocalTime stopTime, LocalTime lowerBoundary, LocalTime upperBoundary, int priceRatePerHour) {
// check if the period [start;stop] is crossing the period [lowerBoundary;upperBoundary]
if(stopTime.isAfter(lowerBoundary) && startTime.isBefore(upperBoundary)){
// truncate start time to not be before lowerBoundary
LocalTime actualStart = (startTime.isBefore(lowerBoundary))?lowerBoundary:startTime;
// symetrically, truncate stop to not be after upperBounday
LocalTime actualStop = (stopTime.isAfter(upperBoundary))?upperBoundary:stopTime;
// now that we have the proper start and stop of the period, let's compute the price of it
return compute(actualStart, actualStop, priceRatePerHour);
}else{
return 0;
}
}
private static int compute(LocalTime startTime, LocalTime stopTime, int pricePerHour) {
Duration duration = Duration.between(startTime, stopTime);
int hours = (int) duration.toHours();
long minutes = duration.toMinutes();
if(minutes % 60 > 0L){
// hour started, increasing the number
hours++;
}
int result = hours * pricePerHour;
System.out.println(String.format("%d hours at %d price/h => %d", hours, pricePerHour, result));
return result;
}
}
直接去计算最终价格。更新以存储白天和晚上的总小时数应该是一个很大的挑战
我的例子的结果:
checking between 2019-01-01T18:50 and 2019-01-01T20:49
2 hours at 5 price/h => 10
10
******
checking between 2019-01-01T18:50 and 2019-01-01T20:50
2 hours at 5 price/h => 10
10
******
checking between 2019-01-01T18:50 and 2019-01-01T20:51
3 hours at 5 price/h => 15
15
******
checking between 2019-01-01T18:50 and 2019-01-02T20:51
checking between 2019-01-01T18:50 and 2019-01-01T23:59:59.999999999
5 hours at 5 price/h => 25
checking between 2019-01-02T00:00 and 2019-01-02T20:51
5 hours at 5 price/h => 25
11 hours at 10 price/h => 110
3 hours at 5 price/h => 15
175
******
可能需要更多测试以确保它在所有条件下都良好,但对您来说应该是一个有用的起点
时区
您的代码和其他答案无法解决 time zone 异常。如果您跟踪的是实际时刻,即人们实际停车的时间,而不是理论上的 24 小时漫长的一天,那么您必须考虑夏令时 (DST) 等异常情况。世界各地的政治家都表现出重新定义其管辖范围内的时区的倾向。所以天数可以是任意长度,例如 25 小时、23 小时、23.5 小时、24.25 小时或其他。
时区是特定地区人民使用的 offset-from-UTC 过去、现在和未来变化的历史。
LocalDateTime
class 完全错误 class 用于此目的。 class 故意没有时区或与 UTC 的偏移量的概念。您可以将其用作代码中的构建块,但必须为其分配 ZoneId
以通过 ZonedDateTime
class.
ZoneId
指定您的时区。
如果未指定时区,JVM 将隐式应用其当前默认时区。该默认值在运行时可能 change at any moment (!),因此您的结果可能会有所不同。最好将 desired/expected 时区明确指定为参数。如果关键,请与您的用户确认区域。
以Continent/Region
的格式指定一个proper time zone name,例如America/Montreal
、Africa/Casablanca
或Pacific/Auckland
。切勿使用 2-4 个字母的缩写,例如 EST
或 IST
,因为它们 不是 真正的时区,不是标准化的,甚至不是唯一的(!)。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
如果你想使用 JVM 当前的默认时区,请求它并作为参数传递。如果省略,代码将变得难以阅读,因为我们不确定您是否打算使用默认值,或者您是否像许多程序员一样没有意识到这个问题。
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Assemble 确定时刻的日期、时间和时区
Assemble 您的日期和时间。
LocalDate startDate = LocalDate.of( 2019 , 1 , 23 ) ; // 23rd of January in 2019.
LocalTime startTime = LocalTime.of( 18 , 50 ) ; // 6:50 PM.
ZonedDateTime startMoment = ZonedDateTime.of( startDate , startTime , z ) ;
LocalDate stopDate = LocalDate.of( 2019 , 1 , 23 ) ; // 23rd of January in 2019.
LocalTime stopTime = LocalTime.of( 20 , 50 ) ; // Two hours later, exactly — maybe! Depends on any anomalies at that time in that zone.
ZonedDateTime stopMoment = ZonedDateTime.of( stopDate , stopTime , z ) ;
➥ 请注意,在这个例子中,我们 可能 的时间跨度恰好是 2 小时,但 可能不是 。可能是 3 小时或其他时间长度,具体取决于该区域当时为该日期安排的异常情况。
经过的时间
要根据天数(24 小时时间块,与日历无关)、小时、分钟和秒来计算经过的时间,请使用 Duration
。 (对于年月日,使用 Period
。)
Duration d = Duration.between( startMoment , stopMoment ) ;
以整小时为单位询问整个时间跨度。
long hours = d.toHours() ; // Entire duration as count of whole hours.
半开
In the included pastebin, there is starting time 18:50 and stop time 20:49. If we input this data, the output should be 2 started day hours. Now if the minute is the same, it does not count as a started hour. But if we change the input to 20:51, then it started an another hour, so the DayHoursTotal should be equal to 3.
这种方法被称为半开,当开始是包含,而结尾是不包含。这通常用于日期时间处理。 Duration
和 Period
class 应用了这种方法。
但要小心单独匹配分钟数。您的日期时间对象可能会保持秒数 and/or 小数秒,这会影响您的算法。作为一种习惯,如果粒度可能比您想要的更小,请显式截断日期时间对象。例如,ZonedDateTime.truncatedTo
。
费率变化
显然,利率变化使事情复杂化。其他答案似乎已经涵盖了这一点,所以我不再重复。但我可以添加一个重要提示:请参阅 ThreeTen-Extra 的 classes Interval
和 LocalDateRange
可能对您有所帮助。它们包括用于重叠、包含、邻接等的便捷比较方法。