我们试图在将来获取日期,以便遵循以下规则:
所以,例如:
| on | date is |
|------------------------------------------------|------------|
| 2017-06-15 | 2017-07-04 |
| 2017-06-16 14:00:00 (friday before 15:00) | 2017-07-04 |
| 2017-06-16 15:00:00 (friday after 15:00) | 2017-07-17 |
| 2017-06-16 16:00:00 | 2017-07-17 |
| 2017-06-17 | 2017-07-17 |
| 2017-06-18 | 2017-07-17 |
| 2017-06-19 | 2017-07-17 |
| 2017-06-20 | 2017-07-17 |
| 2017-06-21 | 2017-07-17 |
| 2017-06-22 | 2017-07-17 |
| 2017-06-23 (friday again, dates do not change) | 2017-07-17 |
| 2017-06-24 | 2017-07-17 |
| 2017-06-25 | 2017-07-17 |
| 2017-06-26 | 2017-07-17 |
| 2017-06-27 | 2017-07-17 |
| 2017-06-28 | 2017-07-17 |
| 2017-06-29 | 2017-07-17 |
| 2017-06-30 14:00:00 (friday before 15:00) | 2017-07-17 |
| 2017-06-30 15:00:00 (friday after 15:00) | 2017-07-31 |
| 2017-06-30 16:00:00 | 2017-07-31 |
| 2017-07-01 | 2017-07-31 |
这就是我提出的:https://jsfiddle.net/2yunw713/
它有点粗糙,我开始改变太多,所以目前它在小时尺度上甚至都不正确。
算法可以使用任何语言,我选择JavaScript只是因为它很容易测试。
答案 0 :(得分:1)
任何语言......所以这里是bash solution =)
readonly start_date='2017-06-15'
# first monday after start_date
readonly start_mon="$(date -d "$start_date -$(date -d $start_date +%u) day + 8 day")"
monday_id=2 # offset in weeks from start_mon
monday_str=$(date -d "$start_mon $monday_id week" +'%Y-%m-%d')
fri_cnt=1 # 1 if date has to be changed on first friday, else 0
printf '|%-30s|%-10s|\n' 'on' 'date is'
for d in $(seq 0 56) # 4 mondays ahead
do
printf '|%-30s|%-10s|\n' "$(date -d "$start_date $d day" +'%Y-%m-%d')" "$monday_str"
if [[ $(date -d "$start_date $d day" +'%a') == $(date -d 'fri' +'%a') ]]
then # it's friday
fri_cnt=$((fri_cnt+1))
if [[ $fri_cnt == 2 ]]
then # it's second friday, change date
fri_cnt=0
monday_id=$((monday_id+2))
monday_str=$(date -d "$start_mon $monday_id week" +'%Y-%m-%d')
printf "|%-30s|%-10s|\n" "$(date -d "$start_date 15:00 $d day" +'%Y-%m-%d %H:%M:%S')" "$monday_str"
fi
fi
done
结果:
|on |date is |
|2017-06-15 |2017-07-03|
|2017-06-16 |2017-07-03|
|2017-06-16 15:00:00 |2017-07-17|
|2017-06-17 |2017-07-17|
|2017-06-18 |2017-07-17|
|2017-06-19 |2017-07-17|
|2017-06-20 |2017-07-17|
|2017-06-21 |2017-07-17|
|2017-06-22 |2017-07-17|
|2017-06-23 |2017-07-17|
|2017-06-24 |2017-07-17|
|2017-06-25 |2017-07-17|
|2017-06-26 |2017-07-17|
|2017-06-27 |2017-07-17|
|2017-06-28 |2017-07-17|
|2017-06-29 |2017-07-17|
|2017-06-30 |2017-07-17|
|2017-06-30 15:00:00 |2017-07-31|
|2017-07-01 |2017-07-31|
|2017-07-02 |2017-07-31|
答案 1 :(得分:1)
我已经使用Java 8 new java.time API制作了这个算法,尽管它也可以使用ThreeTen Backport用于Java< = 7。
以下代码适用于两者。
唯一的区别是包名称(在Java 8中为java.time
而在ThreeTen Backport中为org.threeten.bp
),但类和方法名称是相同的。
首先,它不清楚你如何设置最初的星期五(当这一切都开始时)。所以,我考虑2017-06-16 15:00
作为第一个星期五(我的算法开始的地方;发生变化的最后一天)。
该算法基本上是:
import java.time.temporal.TemporalAdjusters;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
// starting at 2017-06-16 15:00:00 (the last date when a change occured)
LocalDateTime lastChange = LocalDateTime.of(2017, 6, 16, 15, 0);
// nextChange: 2 weeks after the last change
LocalDateTime nextChange = lastChange.plusWeeks(2);
// get the current date
LocalDateTime today = LocalDateTime.now();
// check if today is after or equals the nextChange
if (!today.isBefore(nextChange)) {
// today is equal or after the next change, adjust the last and next change dates
lastChange = nextChange;
nextChange = lastChange.plusWeeks(2);
}
LocalDate futureDate = futureDate(lastChange);
System.out.println(futureDate);
// auxiliary method: get the future date based on the last change date
// using LocalDate because time (hour/minute/second) doesn't seem to matter in output
// (but if it does, use a LocalDateTime instead - don't call toLocalDate() in the last line)
public LocalDate futureDate(LocalDateTime lastChange) {
// double checking (last change date is a Friday at 15:00) - not sure if it's really needed
if (lastChange.getDayOfWeek() != DayOfWeek.FRIDAY || (!lastChange.toLocalTime().equals(LocalTime.of(15, 0)))) {
return null; // if the change it's not Friday or it's not 15:00, it should return null? (not sure)
}
// get the next Monday and add 4 weeks
return lastChange.with(TemporalAdjusters.next(DayOfWeek.MONDAY))
.plusWeeks(4).toLocalDate();
}
输出(考虑到今天是2017-06-19
)将是:
2017年7月17日
我还在14:00(返回2017-06-30
)和15:00(返回2017-07-17
)使用2017-07-31
进行了测试。使用您的示例进行测试也给了我相同的结果。
备注:强>
LocalDate
课程。如果您还需要时间字段(小时/分钟/秒),请更改方法以返回LocalDateTime
,并删除toLocalDate()
语句中对return
的调用。2017-06-16
。2017-06-15
的结果不应该是2017-07-03
?因为2017-07-04
是星期二。如果你想要相同的输出,你可以像下面那样进行循环(使用相同的futureDate()
方法,并假设2017-06-15
的结果是2017-07-03
,如上所述)。
请注意,我使用java.time.temporal.TemporalAdjuster
来获取下一个日期 - 因为当它是星期五时,我必须检查它是否更改日期并获得结果14:00,15: 00和16:00,然后转到第二天(所以有时我需要将时间设置为14:00,或者添加1小时,或者添加1天 - 我会为每种情况选择相应的调节器。)
我还使用了java.time.format.DateTimeFormatter
来更改输出格式(当星期五更改并且它有几小时的自定义格式时):
// starting 2 weeks before 2017-06-16 15:00:00 (the last date when a change occured)
LocalDateTime lastChange = LocalDateTime.of(2017, 6, 16, 15, 0).minusWeeks(2);
// nextChange: 2 weeks after the last change
LocalDateTime nextChange = lastChange.plusWeeks(2);
// starting at 2017-06-15
LocalDateTime today = LocalDateTime.of(2017, 6, 15, 0, 0);
LocalTime twoPM = LocalTime.of(14, 0);
LocalTime threePM = LocalTime.of(15, 0);
LocalTime fourPM = LocalTime.of(16, 0);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
TemporalAdjuster adjuster; // adjuster for the next date
// adjuster for next hour
TemporalAdjuster nextHour = t -> t.plus(1, ChronoUnit.HOURS);
// adjuster for next day
TemporalAdjuster nextDay = t -> t.plus(1, ChronoUnit.DAYS);
System.out.println("| on | date is |");
System.out.println("|------------------------------------------------|------------|");
for (int i = 0; i < 40; i++) {
String sep = "";
StringBuilder sb = new StringBuilder("| ");
if (today.getDayOfWeek() == DayOfWeek.FRIDAY) {
if (ChronoUnit.DAYS.between(lastChange.toLocalDate(), today.toLocalDate()) == 7) {
sep = " (friday again, dates do not change)";
adjuster = nextDay;
sb.append(today.toLocalDate());
} else {
LocalTime time = today.toLocalTime();
if (time.equals(twoPM)) {
sep = " (friday before 15:00) ";
adjuster = nextHour;
sb.append(formatter.format(today));
} else if (time.equals(threePM)) {
sep = " (friday after 15:00) ";
adjuster = nextHour;
sb.append(formatter.format(today));
} else if (time.equals(fourPM)) {
sep = " ";
adjuster = nextDay;
sb.append(formatter.format(today));
} else {
sep = " (friday before 15:00) ";
adjuster = nextHour;
today = today.with(twoPM);
sb.append(formatter.format(today));
}
}
} else {
// get the next day at start of day
sep = " ";
adjuster = t -> LocalDate.from(t).plusDays(1).atStartOfDay();
sb.append(today.toLocalDate());
}
// check if today is after or equals the nextChange
if (!today.isBefore(nextChange)) {
// today is equal or after the next change, adjust the last and next change dates
lastChange = nextChange;
nextChange = lastChange.plusWeeks(2);
}
LocalDate futureDate = futureDate(lastChange);
sb.append(sep).append(" | ").append(futureDate).append(" |");
System.out.println(sb.toString());
// get the next date
today = today.with(adjuster);
}
输出结果为:
| on | date is |
|------------------------------------------------|------------|
| 2017-06-15 | 2017-07-03 |
| 2017-06-16 14:00:00 (friday before 15:00) | 2017-07-03 |
| 2017-06-16 15:00:00 (friday after 15:00) | 2017-07-17 |
| 2017-06-16 16:00:00 | 2017-07-17 |
| 2017-06-17 | 2017-07-17 |
| 2017-06-18 | 2017-07-17 |
| 2017-06-19 | 2017-07-17 |
| 2017-06-20 | 2017-07-17 |
| 2017-06-21 | 2017-07-17 |
| 2017-06-22 | 2017-07-17 |
| 2017-06-23 (friday again, dates do not change) | 2017-07-17 |
| 2017-06-24 | 2017-07-17 |
| 2017-06-25 | 2017-07-17 |
| 2017-06-26 | 2017-07-17 |
| 2017-06-27 | 2017-07-17 |
| 2017-06-28 | 2017-07-17 |
| 2017-06-29 | 2017-07-17 |
| 2017-06-30 14:00:00 (friday before 15:00) | 2017-07-17 |
| 2017-06-30 15:00:00 (friday after 15:00) | 2017-07-31 |
| 2017-06-30 16:00:00 | 2017-07-31 |
| 2017-07-01 | 2017-07-31 |
| 2017-07-02 | 2017-07-31 |
| 2017-07-03 | 2017-07-31 |
| 2017-07-04 | 2017-07-31 |
| 2017-07-05 | 2017-07-31 |
| 2017-07-06 | 2017-07-31 |
| 2017-07-07 (friday again, dates do not change) | 2017-07-31 |
| 2017-07-08 | 2017-07-31 |
| 2017-07-09 | 2017-07-31 |
| 2017-07-10 | 2017-07-31 |
| 2017-07-11 | 2017-07-31 |
| 2017-07-12 | 2017-07-31 |
| 2017-07-13 | 2017-07-31 |
| 2017-07-14 14:00:00 (friday before 15:00) | 2017-07-31 |
| 2017-07-14 15:00:00 (friday after 15:00) | 2017-08-14 |
| 2017-07-14 16:00:00 | 2017-08-14 |
| 2017-07-15 | 2017-08-14 |
| 2017-07-16 | 2017-08-14 |
| 2017-07-17 | 2017-08-14 |
| 2017-07-18 | 2017-08-14 |
另一种方法是创建一个地图来存储相对于发生更改的日期的所有未来日期(因此您不需要一直计算它):
// maps a change date with the respective future date
Map<LocalDateTime, LocalDate> futureDates = new HashMap<>();
// starting at 2017-06-16
LocalDateTime change = LocalDateTime.of(2017, 6, 16, 15, 0);
// storing just 30 dates, but you can change this accordingly, with as many dates as you need
for (int i = 0; i < 30; i++) {
futureDates.put(change, futureDate(change));
change = change.plusWeeks(2);
}
支票会有所改变,因为您需要准确了解与您检查日期对应的密钥:
LocalDateTime today = LocalDateTime.now();
// find the future date
for (LocalDateTime dt : futureDates.keySet()) {
long days = ChronoUnit.DAYS.between(dt, today);
if (days >= 0 && days <= 13) {
System.out.println(today + " " + futureDates.get(dt));
break;
}
}
这与上面的第一个版本的工作方式相同。
答案 2 :(得分:0)
我已经提供了一个Python解决方案,它可以作为库或下面的灵活命令行脚本。
以./script.py demo
运行,它会生成类似于您所拥有的图表(我必须手动交错切换时间):
| on | date is |
|------------------------------------------------|------------|
| 2017-06-15 00:00 | 2017-07-03 |
| 2017-06-16 00:00 | 2017-07-03 |
| 2017-06-17 00:00 | 2017-07-03 |
| 2017-06-18 00:00 | 2017-07-17 |
| 2017-06-19 00:00 | 2017-07-17 |
| 2017-06-20 00:00 | 2017-07-17 |
| 2017-06-21 00:00 | 2017-07-17 |
| 2017-06-22 00:00 | 2017-07-17 |
| 2017-06-23 00:00 | 2017-07-17 |
| 2017-06-24 00:00 | 2017-07-17 |
| 2017-06-25 00:00 | 2017-07-17 |
| 2017-06-26 00:00 | 2017-07-17 |
| 2017-06-27 00:00 | 2017-07-17 |
| 2017-06-28 00:00 | 2017-07-17 |
| 2017-06-29 00:00 | 2017-07-17 |
| 2017-06-30 00:00 | 2017-07-17 |
| 2017-07-01 00:00 | 2017-07-17 |
| 2017-07-02 00:00 | 2017-07-31 |
运行,例如./script.py 2017-06-15 2017-06-18 3
,它会在两个日期之间生成一个图表,条目间隔3小时:
| on | date is |
|------------------------------------------------|------------|
| 2017-06-15 00:00 | 2017-07-03 |
| 2017-06-15 03:00 | 2017-07-03 |
| 2017-06-15 06:00 | 2017-07-03 |
| 2017-06-15 09:00 | 2017-07-03 |
| 2017-06-15 12:00 | 2017-07-03 |
| 2017-06-15 15:00 | 2017-07-03 |
| 2017-06-15 18:00 | 2017-07-03 |
| 2017-06-15 21:00 | 2017-07-03 |
| 2017-06-16 00:00 | 2017-07-03 |
| 2017-06-16 03:00 | 2017-07-03 |
| 2017-06-16 06:00 | 2017-07-03 |
| 2017-06-16 09:00 | 2017-07-03 |
| 2017-06-16 12:00 | 2017-07-03 |
| 2017-06-16 15:00 | 2017-07-03 |
| 2017-06-16 18:00 | 2017-07-17 |
| 2017-06-16 21:00 | 2017-07-17 |
| 2017-06-17 00:00 | 2017-07-17 |
| 2017-06-17 03:00 | 2017-07-17 |
| 2017-06-17 06:00 | 2017-07-17 |
| 2017-06-17 09:00 | 2017-07-17 |
| 2017-06-17 12:00 | 2017-07-17 |
| 2017-06-17 15:00 | 2017-07-17 |
| 2017-06-17 18:00 | 2017-07-17 |
| 2017-06-17 21:00 | 2017-07-17 |
| 2017-06-18 00:00 | 2017-07-17 |
| 2017-06-18 03:00 | 2017-07-17 |
脚本如下:
#!/usr/bin/env python3
import datetime
import sys
MONDAY = 0 # 0 = Monday, 1=Tuesday, 2=Wednesday...
FRIDAY = 4
#Date of first change Friday. All future changes are calculated from here
FIRST_PREV_FRIDAY = datetime.datetime(2017,6,2,15,0,0)
#Get the next weekday following the query_date
def GetNextWeekday(query_date, wdaynum):
days_ahead = wdaynum - query_date.weekday() #0 = Monday, 1=Tuesday, 2=Wednesday, ...
if days_ahead<=0: #Day already happened this week
days_ahead += 7 #Add 7 days to get the next day
new_date = query_date + datetime.timedelta(days_ahead) #Do date math to get the day in datetime
return new_date
#Get a weekday several weeks in advance
def GetWeekdayInFuture(query_date, wdaynum, weeks_out):
wd = query_date #Starting with the current day
for i in range(weeks_out): #Loop for a given number of weeks
wd = GetNextWeekday(wd, wdaynum) #Calculating the next occurence of the day of interest
return wd #Return the day
#Get a list of (current_date,next_monday) pairs between start_date and end_date
def GetMondays(start_date, end_date, date_increment):
assert date_increment <= datetime.timedelta(days=1) #If it were larger, we might skip changes!
assert start_date >= FIRST_PREV_FRIDAY #Can't calculate into the past
#The idea is that we'll begin calculating from a point where we know things
#are correct and only provide output in the user's specified region of
#interest
prev_friday = FIRST_PREV_FRIDAY #First Friday at which a change took place
change_friday = GetWeekdayInFuture(prev_friday, FRIDAY, 2) #Next Friday at which a change will take place
this_date = prev_friday #Set the current date to one for which we know the answer
#Match hours minutes to start_date
this_date = this_date.replace(hour=start_date.hour, minute=start_date.minute)
mondays = [] #Holds the output list
while this_date<=end_date: #If current date is before the end date
if this_date>=change_friday: #Check if we've moved past a change point
prev_friday = change_friday #If so, this day becomes the point from which the future Monday is calculated
change_friday = GetWeekdayInFuture(change_friday, FRIDAY, 2) #Calculate the next change point, two weeks in the future
next_monday = GetWeekdayInFuture(prev_friday, MONDAY, 5) #The Monday of interest is 5 weeks from the previous change point
this_date += date_increment #Advance to the next day
if this_date>=start_date: #Is this a date we were interested in?
mondays.append((this_date,next_monday)) #Gather output
return mondays
#Get a particular Monday
def GetMondayForDate(query_date):
start_date = FIRST_PREV_FRIDAY
end_date = query_date
mondays = GetMondays(start_date, end_date, datetime.timedelta(days=1))
return mondays[-1][1]
#Print a nicely formatted charge of the Mondays
def PrintChart(mondays):
print("| on | date is |")
print("|------------------------------------------------|------------|")
for x in mondays:
print("| {0:47}| {1:11}|".format(x[0].strftime("%Y-%m-%d %H:%M"),x[1].strftime("%Y-%m-%d")))
#Code to run if script were executed from the command line; otherwise, it works
#as a library
def main():
if len(sys.argv)==1:
print("Syntax:")
print("\t{0:20} - This help screen".format(sys.argv[0]))
print("\t{0:20} demo - Demonstration chart".format(sys.argv[0]))
print("\t{0:20} <START DATE> <END DATE> <Hours> - Chart between two dates with increment of hours".format(sys.argv[0]))
elif sys.argv[1]=='demo':
start_date = datetime.datetime(2017,6,15,0,0,0) #Date from which to begin calculating
end_date = datetime.datetime(2017,7,1,0,0,0) #Last date for which to calculate
mondays = GetMondays(start_date, end_date, datetime.timedelta(days=1))
PrintChart(mondays)
else:
start_date = datetime.datetime.strptime(sys.argv[1], "%Y-%m-%d")
end_date = datetime.datetime.strptime(sys.argv[2], "%Y-%m-%d")
hours = int(sys.argv[3])
mondays = GetMondays(start_date, end_date, datetime.timedelta(hours=hours))
PrintChart(mondays)
if __name__ == '__main__': #Was the script executed from the command line?
main()
给出此输出图表(当我从hours=1
交错数据时):