2017-06-16 46 views
1

我們試圖獲得在將來的日期改變,使其遵循以下規則:日期算法來獲得4個星期一進取,上週五

  • 的日期必須在星期一
  • 日期必須改變每兩個星期
  • 日期必須提前4周(所以它始終在未來,不來太接近當前的日期)
  • 日期必須在下午3點
週五更改

因此,例如:

| 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

你可以使用https://momentjs.com/? – user3080953

+0

「用任何語言」 - 你打算如何整合? –

+3

「2017-06-15」的結果不應該是「2017-07-03」?因爲'2017-07-04'是星期二 – 2017-06-19 17:37:03

回答

1

我做了這個算法與Java 8的new java.time API,雖然它也有可能使用ThreeTen Backport for Java < = 7.

下面的代碼適用於兩者。 唯一的區別是軟件包名稱(在Java 8中是java.time,在ThreeTen Backport中是org.threeten.bp),但名稱的類和方法是相同的。


首先,你不清楚你如何設置最初的星期五(當這一切開始時)。所以,我正在考慮2017-06-16 15:00作爲第一個星期五(我的算法開始的地方;發生變化的最後一天)。

的算法基本上是:

  • 獲得最後的更改日期(星期五有些15:00),如果今天的日期是之前計算何時會發生下一變化(2周後)
  • 檢查接下來的變化提前(如果它是等於或之後,重新計算更改日期)
  • 得到下週一4周最後改變
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日

我也有2017-06-30測試時間爲14:00(返回2017-07-17)和15:00(返回2017-07-31)。用你的例子進行測試也給了我相同的結果。


注:

  • 在你的輸出例子,你剛剛印刷年/月/日,所以我用LocalDate類。如果還需要時間字段(小時/分鐘/秒),請改爲改爲返回LocalDateTime的方法,並在return語句中刪除對toLocalDate()的呼叫。
  • 因爲我不清楚這是第一個星期五(當這一切開始?),我假設它是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; 
    } 
} 

這樣的工作方式與上面的第一個版本相同。

1

任何語言......所以這裏是bash的解決方案=)

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| 
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交織數據):

相關問題