判断两个日子是否在一周内

import java.util.Calendar;
import java.util.Date;
public class Data {
    public static void main(String[] args)
    {

//      Calendar calender = Calendar.getInstance();
//      Date date = new Date();
//      
//      calender.setTime(date);
//      int year=2015;
//      int month=01;
//      int day=23;
//      calender.set(year, month-1, day);
//      int i = calender.get(Calendar.DAY_OF_WEEK);
//  System.out.println(i);
//      int j = calender.get(Calendar.WEEK_OF_MONTH);
//      
//      System.out.println(j);

        String b="20150901";
        String a="20150903";
        if((Integer.parseInt(b)-Integer.parseInt(a))<7||(Integer.parseInt(b)-Integer.parseInt(a))>-7)
        {
            System.out.println("hah");
        }
    }

}

输出结果:hah

调换a,b,输出结果:hah

<think>我们被要求在Oracle数据库中计算两个日期之间的工作日数量。工作日通常指一到五,排除末(六和日)。在Oracle中,没有内置函数直接计算工作日,但我们可以通过编写SQL语句实现。思路:1.生成两个日期之间的所有日期。2.排除末(根据具体地区,末可能是六和日,也可能有其他情况,但通常为六和日)。3.计数。方法:我们可以使用一个日期范围生成器(例如,使用CONNECTBY生成连续的数字,然后转换为日期),然后对每个日期判断是否为工作日(即不是六也不是日)。在Oracle中,我们可以使用TO_CHAR(date,'D')函数来获取星期几,其中1表示星期日,2表示星期一,...,7表示星期六(这是根据NLS设置决定的,常见的是这样,但可能因设置而异。另一种更可靠的方法是使用'DY'获取星期几的缩写,但要注意语言设置)。为了安全起见,我们通常使用'NLS_TERRITORY'设置来确保一致性,或者使用独立于语言的判断方法(比如判断星期几的数字表示,并确认在当前的NLS设置下,星期日是1,星期六是7)。因此,我们可以这样判断:-排除星期日:TO_CHAR(date,'D')='1'-排除星期六:TO_CHAR(date,'D')='7'但注意:有些地区设置可能将星期一作为一周的第一天,所以使用'D'格式可能因环境而异。为了避免这个问题,我们可以使用更通用的方法:使用'D'格式,并明确我们的环境是星期日为1,星期六为7。如果环境不同,需要调整。或者使用另一种方法:使用'DY',然后判断是否为'SAT'或'SUN'(注意大小写和语言,通常英文环境是大写)。这里我们假设环境是英文,并且一周从星期日开始。如果环境不同,可能需要调整。另一种更可靠的方法是使用函数`TRUNC(date,'IW')`,它返回日期所在的星期一(国际,ISO标准,一为一周的第一天)。然后我们可以计算该日期在一周中的位置(从一开始为0,到日为6)。这样,六和日就是5和6。但这种方法计算稍复杂。为了简单,我们采用第一种方法,并假设星期日为1,星期六为7。步骤:1.计算两个日期之间的天数差,然后生成这个天数差的连续日期序列。2.对每个日期判断是否六或日(用TO_CHAR(date,'D'),当值为1或7时排除)。3.计数。注意:我们可能还需要排除节假日,但问题只要求工作日(即排除末),所以不考虑节假日。因此,SQL语句可以这样写:```sqlSELECTCOUNT(*)ASworkdaysFROM(SELECTstart_date+LEVEL-1ASthe_dateFROMdualCONNECTBYLEVEL<=end_date-start_date+1)WHERETO_CHAR(the_date,'D','NLS_DATE_LANGUAGE=AMERICAN')NOTIN('1','7')```解释:-`start_date`和`end_date`是给定的两个日期,且`end_date`应该大于或等于`start_date`。-`CONNECTBYLEVEL<=...`生成从1到天数差的序列,然后通过`start_date+LEVEL-1`得到每一天。-使用`TO_CHAR(the_date,'D','NLS_DATE_LANGUAGE=AMERICAN')`指定语言为英文,确保星期的表示一致。在英文环境下,星期日是1,星期六是7。-然后排除这些日子。但是,注意:使用`D`格式时,它依赖于当前会话的NLS设置。为了确保结果正确,我们指定了`NLS_DATE_LANGUAGE=AMERICAN`,但`D`格式的数字表示还依赖于`NLS_TERRITORY`。因此,更通用的做法是使用`DY`(星期几的缩写)来判断:```sqlSELECTCOUNT(*)ASworkdaysFROM(SELECTstart_date+LEVEL-1ASthe_dateFROMdualCONNECTBYLEVEL<=end_date-start_date+1)WHERETO_CHAR(the_date,'DY','NLS_DATE_LANGUAGE=AMERICAN')NOTIN('SAT','SUN')```这样更直观,而且不依赖于一周的第一天是星期几。但注意:使用`DY`时,要确保使用相同的语言(这里指定了英文),因为不同语言的缩写可能不同。因此,我们选择第二种方法。但是,如果我们的日期区间很大(比如几年),使用CONNECTBYLEVEL可能会因为递归层数过多而失败(Oracle默认的递归层数由CONNECT_BY_MAX限制,但通常足够大,不过对于非常大的日期范围,可能需要考虑性能)。我们可以使用其他方法生成日期序列(比如使用一个包含大量日期的表),但这里我们使用CONNECTBY。另外,如果`start_date`和`end_date`是同一个日期,那么我们需要计算这个日期是否是工作日。例子:计算2023-10-01(星期日)到2023-10-08(星期日)之间的工作日。应该包括:2023-10-02(星期一)到2023-10-06(星期五),共5天。让我们用上面的SQL计算一下(注意:日期格式要正确,使用TO_DATE转换)。完整示例:```sqlWITHparametersAS(SELECTTO_DATE('2023-10-01','YYYY-MM-DD')ASstart_date,TO_DATE('2023-10-08','YYYY-MM-DD')ASend_dateFROMdual)SELECTCOUNT(*)ASworkdaysFROM(SELECTstart_date+LEVEL-1ASthe_dateFROMparametersCONNECTBYLEVEL<=end_date-start_date+1)WHERETO_CHAR(the_date,'DY','NLS_DATE_LANGUAGE=AMERICAN')NOTIN('SAT','SUN');```结果应该是5。但是注意:上面的SQL在CONNECTBY中使用的是dual表,而start_date和end_date来自parameters,所以CONNECTBY会正确地生成序列。然而,如果日期区间很大,我们可以考虑使用一个辅助表(如果有的话),或者使用其他更高效的方法。但这里我们使用CONNECTBY,对于一般的日期区间(几年内)是可行的。另外,我们还可以使用递归CTE(WITH子句)来生成日期序列,但CONNECTBY更简洁。因此,最终的解决方案如下:步骤:1.定义两个日期变量(或直接使用具体日期)。2.生成日期序列。3.过滤掉六和日(使用指定的语言环境)。4.计数。注意:如果日期区间很大,建议使用一个日历表(预先创建好的表)来提高性能。如果用户需要排除节假日,那么就需要一个节假日表,然后在这个基础上再排除节假日。但问题只要求排除末。因此,我们给出排除末的SQL。参考引用[1]中给出的方法也是类似的,它使用了一个数字序列生成,然后判断星期几(用'D'格式)排除1和7(即星期日和星期六)。但引用[1]没有指定语言,所以可能因环境而异。我们这里使用指定语言的方法。所以,我们采用指定语言的方法,使用'DY'格式。但是,注意:使用'DY'格式时,如果日期是星期六,那么就是'SAT',星期日就是'SUN'(在英文环境下)。所以指定语言为英文可以保证结果。如果用户的环境不是英文,那么需要调整吗?实际上,我们指定了第三个参数为'AMERICAN',所以会强制使用英文的星期缩写。因此,我们写一个函数来封装这个功能,以便重用:```sqlCREATEORREPLACEFUNCTIONget_workdays(p_start_dateINDATE,p_end_dateINDATE)RETURNNUMBERISv_workdaysNUMBER;BEGINSELECTCOUNT(*)INTOv_workdaysFROM(SELECTp_start_date+LEVEL-1ASthe_dateFROMdualCONNECTBYLEVEL<=p_end_date-p_start_date+1)WHERETO_CHAR(the_date,'DY','NLS_DATE_LANGUAGE=AMERICAN')NOTIN('SAT','SUN');RETURNv_workdays;END;/```然后就可以这样调用:```sqlSELECTget_workdays(TO_DATE('2023-10-01','YYYY-MM-DD'),TO_DATE('2023-10-08','YYYY-MM-DD'))FROMdual;```返回5。注意:这个函数没有考虑节假日。如果考虑节假日,需要连接节假日表,并在计数时排除这些日期。另外,如果p_end_date小于p_start_date,那么生成的序列会是空(因为LEVEL<=负数+1,不会生成任何行),所以函数会返回0。这是合理的,因为结束日期在开始日期之前,没有工作日。但是,我们可以调整函数,使其在p_end_date小于p_start_date时返回0,或者交换日期?但通常我们要求结束日期大于等于开始日期。如果结束日期小于开始日期,我们可以返回负值?但通常工作日数量是非负的。所以返回0是合理的。如果用户希望交换日期,可以自行处理输入参数。函数内部不做交换,因为如果结束日期小于开始日期,则差值为负,CONNECTBY不会生成任何行,所以返回0。因此,这个函数可以处理结束日期小于开始日期的情况(返回0)。但是,如果两个日期相等,那么生成一行,然后判断这一天是否是工作日。例如:2023-10-01(星期日)返回0,2023-10-02(星期一)返回1。所以,这个函数符合要求。总结:我们使用一个函数,通过生成日期序列并排除末来计算工作日数。另外,引用[3]中提到了`next_day`函数,但这里我们没有使用,因为我们需要生成整个日期序列。`next_day`可以用于找到下一个特定的星期几,但不太适合用于生成整个序列。因此,我们采用上述方法。相关问题:1.如何在Oracle中生成两个日期之间的所有日期?2.如何在Oracle中判断一个日期是星期几?3.如何排除节假日来计算工作日?4.如何优化大日期范围的工作日计算性能?</think>### Oracle计算两个日期之间的工作日数量 在Oracle中计算两个日期之间的工作日数量(排除末)可以通过以下步骤实现: #### 方法1:使用日期序列生成(推荐) ```sql SELECT COUNT(*) AS workdays FROM ( SELECT start_date + LEVEL - 1 AS the_date FROM dual CONNECT BY LEVEL <= end_date - start_date + 1 ) WHERE TO_CHAR(the_date, 'DY', 'NLS_DATE_LANGUAGE=AMERICAN') NOT IN ('SAT', 'SUN'); ``` **参数说明**: - `start_date`:开始日期(如 `TO_DATE('2023-01-01', 'YYYY-MM-DD')`) - `end_date`:结束日期(如 `TO_DATE('2023-01-31', 'YYYY-MM-DD')`) **原理**: 1. 通过 `CONNECT BY LEVEL` 生成日期序列 2. `TO_CHAR(the_date, 'DY')` 获取星期几的缩写 3. 排除六(`SAT`)、日(`SUN`) 4. 使用 `NLS_DATE_LANGUAGE=AMERICAN` 确保英文星期缩写 #### 方法2:使用数字序列(兼容旧版本) ```sql SELECT COUNT(*) AS workdays FROM ( SELECT start_date + (ROWNUM - 1) AS the_date FROM all_objects WHERE ROWNUM <= end_date - start_date + 1 ) WHERE TO_CHAR(the_date, 'D') NOT IN (1, 7); -- 1=日,7=六 ``` #### 函数封装(可复用) ```sql CREATE OR REPLACE FUNCTION get_workdays( p_start_date IN DATE, p_end_date IN DATE ) RETURN NUMBER IS v_workdays NUMBER; BEGIN SELECT COUNT(*) INTO v_workdays FROM ( SELECT p_start_date + LEVEL - 1 AS the_date FROM dual CONNECT BY LEVEL <= p_end_date - p_start_date + 1 ) WHERE TO_CHAR(the_date, 'DY', 'NLS_DATE_LANGUAGE=AMERICAN') NOT IN ('SAT', 'SUN'); RETURN v_workdays; END; / ``` **使用示例**: ```sql SELECT get_workdays( TO_DATE('2023-10-01', 'YYYY-MM-DD'), TO_DATE('2023-10-08', 'YYYY-MM-DD') ) AS workdays FROM dual; -- 返回结果:5 (排除10/1日和10/7六、10/8日) ``` #### 注意事项: 1. **日期格式**:必须使用`TO_DATE`显式转换字符串 2. **时区处理**:若日期包含时间,建议先用`TRUNC`去除时间部分 3. **节假日处理**:如需排除法定假日,需连接假日表: ```sql WHERE ... AND the_date NOT IN (SELECT holiday_date FROM holiday_table) ``` 4. **性能优化**:大日期范围建议使用物化视图或日历表 > 引用说明:日期序列生成方法参考了Oracle的`CONNECT BY`层级查询原理[^1],星期判断采用标准`TO_CHAR`日期格式化函数[^3]。 --- ### 相关问题 1. **如何排除法定节假日计算工作日?** > 需创建节假日表并通过`LEFT JOIN`排除特定日期,例如: > ```sql > SELECT COUNT(*) > FROM date_sequence ds > LEFT JOIN holiday_table h ON ds.the_date = h.holiday > WHERE h.holiday IS NULL ... > ``` 2. **Oracle中如何高效生成大时间范围的日期序列?** > 对于超过万行的日期序列,建议: > - 使用预生成的日历表 > - 启用`PARALLEL`查询 > - 使用`APPROX_COUNT_DISTINCT`加速估算 3. **如何处理不同地区的末规则?** > 通过NLS参数定制星期判断: > ```sql > WHERE TO_CHAR(the_date, 'D', 'NLS_TERRITORY=SAUDI_ARABIA') NOT IN (5,6) > -- 中东地区五、六为末 > ``` 4. **能否用`MONTHS_BETWEEN`计算工作日?** > 不能直接使用,因为`MONTHS_BETWEEN`只计算日历月差异[^2],工作日需逐日判断
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值