“近一个月”、“近三个月”这种查询如何处理更精确?

本文分享了一种用于精确查询近一个月、近三个月等时间段数据的算法,详细介绍了如何使用C#语言处理日期和时间,确保查询结果准确无误。

 

640?wx_fmt=gif

640?wx_fmt=jpeg

作者 | 宋广泽

责编 | 胡雪蕊

出品 | 优快云(ID:优快云news)

有的人认为,全都按一个月30天算,查询“近一个月”的数据就是以30天前的0时为起点,以当前时间为终点查询,同理,查询“近三个月”的数据就是以90天前的0时为起点,以当前时间为终点进行查询。举两个例子,如果今天是1月31日,查询“近一个月”的数据就从1月1日0时开始;如果今天是2019年2月28日,查询“近一个月”的数据就从2019年1月29日0时开始。

有的人认为,查询“近三个月”的数据就是包含当前月,无论今天是当前月的几号,1号也行,31号也行,以再往前推2个月的1号的0点为起点,以当前时间为终点进行查询。举个例子,如果现在是2017年1月15日,起点就要追溯到2016年11月1日0时。

以上两种处理算法,都不是特别精确。前几天在做一个项目时遇到了这样的需求,就思考着写了写,感觉还算比较精确。在这里拿出来与大家分享一下。这个项目是用C#语言编写的。

1.首先获取到当前时间,并将其转换为字符串格式。代码如下:

 

 

DateTime nowtime = DateTime.Now;
//让DateTime以2016-05-09T13:09:55的格式显示
string now = nowtime.ToString("s");

此外,C#对于将时间转换成字符串有多种格式,有如下几种主要格式:

 

 

DateTime.Now.ToString()    2016/5/9 13:09:55   中间一个空格
DateTime.Now.ToString("d")    2016/5/9    只包含年月日
DateTime.Now.ToString("g")    2016/5/9 13:09  包含年月日时分 中间一个空格
DateTime.Now.ToString("G")     2016/5/9 13:09:55   年月日时分秒
DateTime.Now.ToString("s")    2016-05-09T13:09:55 中间一个T
DateTime.Now.ToString("u")    2016-05-09 13:09:55Z    中间一个空格 最后一个Z

2.第二步,将字符串转换成具体的年月日时分秒的整型数。

 

 

string.Substring(int start,int length)

start为截取字符串的起始下标,length为需要截取的字符数。代码如下:

 

 

 

//获取当前年
int yy = Convert.ToInt32(now.Substring(0, 4));
//获取当前月
int mm = Convert.ToInt32(now.Substring(5, 2));
//获取当前日
int dd = Convert.ToInt32(now.Substring(8, 2));
//获取当前时
int hr = Convert.ToInt32(now.Substring(11, 2));
//获取当前分
int min = Convert.ToInt32(now.Substring(14, 2));
//获取当前秒
int sec = Convert.ToInt32(now.Substring(17, 2));

3.第三步,推算查询的起始时间。

先文字描述一下算法:

“近一个月”的查询起始时间为上一个月的当前时间。举个例子,现在的时间是2019年8月22日9时45分,查询起始时间就应该为2019年7月22日9时45分,这样才算是满打满算“近一个月”。

可是又出现了新的问题,有的月(平年的2月)有28天,有的月(闰年的2月)有29天,有的月有30天(2月、4月、6月、9月、11月),有的月有31天(1月、3月、5月、7月、8月、10月、12月),因此,如果当前时间是2019年3月31日的某一时刻,按照以上算法推算,是没有2019年2月31日的。为了解决这种问题,我们可以将多于当月天数的那些天减掉,对于以上问题就将查询起始时间定为2019年2月28日的相同时刻。为了方便大家理解,再多举几个例子:

2019年5月31日-2019年4月30日

2017年3月30日-2017年2月28日

2016年3月30日-2016年2月29日(闰年)

2016年3月31日-2016年2月29日(闰年)

2000年3月31日-2000年2月29日(闰年)

完成上述推算,有两个关键点,完成的顺序不可颠倒。一是找到查询起始时间所在的年月,二是计算出查询起始时间所在的月有多少天。

先讲第一步,分为两种情况。

一种是不“跨年”的,如果当前时间是2019年8月,“近一个月”的查询起始时间就是2019年7月,即当前年/当前月-1;“近三个月”的查询起始时间就是2019年5月,即当前年/当前月-3。

另一种是“跨年”的,如果当前时间是2019年1月,“近一个月”的查询起始时间就是2018年12月,即当前年-1/12-(1-当前月);“近三个月”的查询起始时间就是2018年10月,即当前年-1/12-(3-当前月)。

以“近三个月”为例描述。代码如下:

 

 

if(cbxTime.Text=="近三个月")
{
    if(mm-3<1)
    {
        //年份需要前移
        ny = yy - 1;
        nm = 12 - (3 - mm);
    }
    else
    {
        //还是在本年
        ny = yy;
        nm = mm - 3;
    }
}

 

以此类推,“近一个月”的代码如下:

 

 

if (cbxTime.Text=="近一个月")
{
    if(mm-1<1)
    {
        //年份需要前移
        ny = yy - 1;
        nm = 12;
    }
    else
    {
        //年份就在本年
        ny = yy;
        nm = mm - 1;
    }
}

再讲第二步,有了查询起始时间所在的年和月,就计算出这个月有多少天就很容

易了。先将一般情况(平年)每个月有多少天进行初始化。代码如下:

640?wx_fmt=png

根据推算出的查询起始时间的年判定是不是闰年,如果是闰年就将2月的天数增加1。

ny、nm、nd为推算起始时间所在年、月、日;

yy、mm、dd为当前时间所在年、月、日。代码如下:

640?wx_fmt=png

有了以上数据,就可以套用前面讲过的将多出来的天数剪掉以确定起始时间的算

法了。代码如下:

 

 

 

if (dd<=mon[nm])
{
    nd = dd;
}
else
{
    nd = mon[nm];
}

查询“近一周”的数据以往前推7天的同一时刻为查询起始时间,需要注意的是“跨月”、“跨年”的情况。当前日-7小于1就需要跨到上一个月,既然要跨到上一个月就要考虑当前月是不是1月,如果是1月还需要跨年,通过当前月-1小于1判断需要跨年。代码如下:

 

 

else if(cbxTime.Text=="近一周")
{
    if(dd-7<1)
    {
        //月要前移 年可能会前移
        if(mm-1<1)
        {
            //年要前移
            ny = yy - 1;
            nm = 12;
        }
        else
        {
            //还在本年
            ny = yy;
            nm = mm - 1;
        }
        if (ny % 4 == 0 && ny % 100 != 0 || ny % 400 == 0)
        {
            //闰年
            mon[2]++;
        }
        nd = mon[nm] - (7 - dd);
    }
    else
    {
        //还在本月
        ny = yy;
        nm = mm;
        nd = dd - 7;
    }
}

同理,查询“近一天”的数据以往前推1天的同一时刻为起始时间。代码如下:

 

 

else
{
    //近一天
    if(dd-1<1)
    {
        if(mm-1<1)
        {
            ny = yy - 1;
            nm = 12;
        }
        else
        {
            ny = yy;
            nm = mm - 1;   
        }
        if (ny % 4 == 0 && ny % 100 != 0 || ny % 400 == 0)
        {
            //闰年
            mon[2]++;
        }
        nd = mon[nm];
    }
    else
    {
        //还在本年本月
        ny = yy;
        nm = mm;
        nd = dd - 1;
    }
}

时分秒不存在特殊情况,因此起始时间与当前时间的时分秒都是相同的。

由此生成最终的查询起始时间字符串,放到sql语句中进行比较查询,即可得到最终的查询结果。代码如下:

 

 

//形成参照时间
pretime = new DateTime(ny, nm, nd, hr, min, sec);
canzhao = pretime.ToString("s");
canzhao.Replace('T', ' ');//进行sql查询try
{
    conn.Open();
    ds = new DataSet();
    string sql = "select recordid as '编号',name as '姓名',phonenumber as '手机号',washer as '洗发工工号',server as '服务生工号',type as '服务类型',arrivetime as '到店时间' from VipLog where arrivetime>'" + canzhao + "'";
    SqlDataAdapter da = new SqlDataAdapter(sql, conn);
    da.Fill(ds);
    dgvList.DataSource = ds.Tables[0];
}
catch (Exception ex)
{
    MessageBox.Show(ex.Message, "按时间搜索时出错", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
    conn.Close();
}

canzhao为最终的查询起始时间,作为比较时参考的字符串而存在。由于用DateTime,ToString("s")转化得到的字符串年月日与时分秒之间是用字母'T'隔开的,如2016-05-09T13:09:55,而在SqlServer中进行比较时时间的形式应该是年月日与时分秒之间用空格隔开,因此要将‘T’替换为空格后再进行比较。

以下是全部代码:

 

 

private void cbxTime_SelectedIndexChanged(object sender, EventArgs e){
    //清空其他下拉框
    txtName.Text = "";
    txtPhoneNumber.Text = "";
    cbxEmID.Text = "";
    cbxType.Text = "";

    nowtime = DateTime.Now;
    //让DateTime以2016-05-09T13:09:55的格式显示
    string now = nowtime.ToString("s");

    //获取当前年
    int yy = Convert.ToInt32(now.Substring(0, 4));
    //获取当前月
    int mm = Convert.ToInt32(now.Substring(5, 2));
    //获取当前日
    int dd = Convert.ToInt32(now.Substring(8, 2));
    //获取当前时
    int hr = Convert.ToInt32(now.Substring(11, 2));
    //获取当前分
    int min = Convert.ToInt32(now.Substring(14, 2));
    //获取当前秒
    int sec = Convert.ToInt32(now.Substring(17, 2));
    //参照比较时间字符串
    string canzhao;
    //参照比较时间
    DateTime pretime;
    //参照年
    int ny=0;
    //参照月
    int nm=0;
    //参照日
    int nd=0;
    int[] mon = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    if (cbxTime.Text=="近一个月")
    {
        if(mm-1<1)
        {
            //年份需要前移
            ny = yy - 1;
            nm = 12;
        }
        else
        {
            //年份就在本年
            ny = yy;
            nm = mm - 1;
        }
        if (ny % 4 == 0 && ny % 100 != 0 || ny % 400 == 0)
        {
            //闰年
            mon[2]++;
        }
        if (dd <= mon[nm])
        {
            nd = dd;
        }
        else
        {
            nd = mon[nm];
        }

    }
    else if(cbxTime.Text=="近三个月")
    {
        if(mm-3<1)
        {
            //年份需要前移
            ny = yy - 1;
            nm = 12 - (3 - mm);
        }
        else
        {
            //还是在本年
            ny = yy;
            nm = mm - 3;
        }
        if (ny % 4 == 0 && ny % 100 != 0 || ny % 400 == 0)
        {
            //闰年
            mon[2]++;
        }
        if (dd<=mon[nm])
        {
            nd = dd;
        }
        else
        {
            nd = mon[nm];
        }
    }
    else if(cbxTime.Text=="近一年")
    {
        ny = yy - 1;
        nm = mm;
        if (ny % 4 == 0 && ny % 100 != 0 || ny % 400 == 0)
        {
            //闰年
            mon[2]++;
        }
        if (dd<=mon[nm])
        {
            nd = dd;
        }
        else
        {
            nd = mon[nm];
        }
    }
    else if(cbxTime.Text=="近一周")
    {
        if(dd-7<1)
        {
            //月要前移 年可能会前移
            if(mm-1<1)
            {
                //年要前移
                ny = yy - 1;
                nm = 12;
            }
            else
            {
                //还在本年
                ny = yy;
                nm = mm - 1;
            }
            if (ny % 4 == 0 && ny % 100 != 0 || ny % 400 == 0)
            {
                //闰年
                mon[2]++;
            }
            nd = mon[nm] - (7 - dd);
        }
        else
        {
            //还在本月
            ny = yy;
            nm = mm;
            nd = dd - 7;
        }
    }
    else
    {
        //近一天
        if(dd-1<1)
        {
            if(mm-1<1)
            {
                ny = yy - 1;
                nm = 12;
            }
            else
            {
                ny = yy;
                nm = mm - 1;
            }
            if (ny % 4 == 0 && ny % 100 != 0 || ny % 400 == 0)
            {
                //闰年
                mon[2]++;
            }
            nd = mon[nm];
        }
        else
        {
            //还在本年本月
            ny = yy;
            nm = mm;
            nd = dd - 1;
        }
    }
    //形成参照时间
    pretime = new DateTime(ny, nm, nd, hr, min, sec);
    canzhao = pretime.ToString("s");
    canzhao.Replace('T', ' ');
    //进行sql查询
    try
    {
        conn.Open();
        ds = new DataSet();
        string sql = "select recordid as '编号',name as '姓名',phonenumber as '手机号',washer as '洗发工工号',server as '服务生工号',type as '服务类型',arrivetime as '到店时间' from VipLog where arrivetime>'" + canzhao + "'";
        SqlDataAdapter da = new SqlDataAdapter(sql, conn);
        da.Fill(ds);
        dgvList.DataSource = ds.Tables[0];
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "按时间搜索时出错", MessageBoxButtons.OK,             MessageBoxIcon.Error);
    }
    finally
    {
        conn.Close();
    }
}

未经优化,代码略显冗余,还望见谅。

作者:宋广泽,青岛某普通一本大学计算机专业在校生,本科在读,学生开发者。喜欢用C/C++编写有意思的程序,解决实际问题。

【END】

会Python,会有什么作用?

https://edu.youkuaiyun.com/topic/python115?utm_source=csdn_bw

640?wx_fmt=jpeg

 热 文 推 荐 

马云谈 5G 危机;腾讯推出车载版微信;Ant Design 3.22.1 发布 | 极客头条

漫画:什么是旅行商问题?

程序员易踩的 9 大坑!

☞公开课 | 如何用图谱挖掘商业数据背后的宝藏?

开学了,复旦老师教你如何玩转“0”“1”浪漫!| 人物志

与旷视、商汤等上百家企业同台竞技?AI Top 30+案例评选等你来秀!

“根本就不需要 Kafka 这样的大型分布式系统!”

他是叶问制片人也是红色通缉犯, 他让泰森卷入ICO, 却最终演变成了一场狗血的罗生门……

☞如何写出让同事无法维护的代码?

640?wx_fmt=gif点击阅读原文,输入关键词,即可搜索您想要的 优快云 文章。

 

 

 

 

 

640?wx_fmt=png

你点的每个“在看”,我都认真当成了喜欢

<think>嗯,用户想让我写一段JavaScript代码来判断当前时间是否超过了三个。首先,我需要明确用户的需求。这里的“超过三个”具体是指从某个特定时间点开始算起,还是相对于当前时间往前三个?比如,用户可能想检查当前时间是否已经超过了某个给定日期之后的三个,或者是否已经超过了自某个事件发生以来的三个。 假设用户是指从某个特定日期开始,判断现在是否已经过了三个。比如,用户有一个日期,比如2023年11日,现在要判断当前时间是否超过了这个日期加上三个后的时间。 那么,我需要考虑如何计算三个后的日期。这里需要注意的是,JavaScript的Date对象处理份加减时,如果当前是某个的最后一天,比如131日,加上三个可能会变成431日,而四份只有30天,这时候Date对象会自动调整为51日。这种情况下,三个后的日期可能和用户预期的不同。不过,用户的需求可能只是简单地增加三个,不管具体的日期,所以可能需要用setMonth方法直接加3。 接下来,我需要获取当前时间,然后比较当前时间是否大于目标时间。例如,假设目标时间是某个特定的开始日期加上三个后的日期,那么比较当前时间是否超过了那个日期。 可能的步骤是: 1. 获取开始日期,比如从某个变量或输入中得到。 2. 计算三个后的日期。例如,使用startDate.setMonth(startDate.getMonth() + 3)。 3. 获取当前时间,用new Date()。 4. 比较当前时间是否大于三个后的日期。 但用户的问题中没有提到具体的开始日期,所以可能需要用户提供或者假设是某个固定的日期。比如,用户可能有一个特定的日期需要检查,比如订单日期,然后判断现在是否超过了订单日期之后的三个。 因此,在代码中可能需要一个示例,假设开始日期是某个值,比如2023-01-01,然后计算三个后的日期,然后比较当前时间是否超过。 或者,用户可能希望函数接收一个开始日期作为参数,返回布尔值表示是否超过三个。所以代码可能需要封装成一个函数,比如: function isOverThreeMonths(startDate) { const currentDate = new Date(); const threeMonthsLater = new Date(startDate); threeMonthsLater.setMonth(threeMonthsLater.getMonth() + 3); return currentDate > threeMonthsLater; } 但这里需要注意,直接使用setMonth可能会遇到边缘情况,比如开始日期是某个的31日,而目标份没有31日,这时候日期会自动调整到下个的第一天。比如,131日加三个是431日,但四只有30天,所以会变成51日。这种情况下,三个后的日期可能比预期的晚一天,但这是否符合用户的需求呢? 如果用户希望三个后的日期是同一天,比如115日加三个是415日,那这种方法没问题。但如果开始日期是末,比如131日,加三个后的4没有31日,就会变成51日,这时候是否算超过三个呢?这可能需要根据用户的业务需求来决定,但通常来说,这样的处理是正确的,因为三个后的同一天可能不存在,所以自动调整到下一个月的第一天是合理的。 另外,用户的问题中是否包括三个后的当天?比如,三个后的日期是415日,那么当前时间如果是415日的午夜0点,是否算超过?这时候代码中的比较是currentDate > threeMonthsLater,所以如果当前时间等于threeMonthsLater的时间,会返回false。如果用户希望包括当天的话,可能需要将条件改为currentDate >= threeMonthsLater。但用户的问题描述是“超过”,所以可能严格大于。 现在,用户可能没有考虑到这些边缘情况,但作为回答,应该指出这些可能的问题,并提供相应的解决方案。 另外,可能还需要考虑时区的问题。JavaScript的Date对象默认使用本地时区还是UTC?例如,如果开始日期是用字符串解析的,可能存在时区差异。但假设用户提供的开始日期是一个Date对象,或者使用本地时间创建,那么处理方式是正确的。在示例代码中,可能需要明确处理时区,或者提醒用户注意这一点。 总结,代码的大致思路是: - 给定一个开始日期。 - 计算三个后的日期。 - 比较当前日期是否大于这个三个后的日期。 然后,编写一个函数,并给出使用示例,同时说明注意事项,比如末的情况和时区的问题。 另外,用户可能没有给出具体的开始日期,所以在示例中需要假设一个开始日期,或者让用户替换成自己的日期。比如: // 示例开始时间,替换为你的实际开始时间 const startDate = new Date('2023-01-01'); // 计算三个后的时间 const threeMonthsLater = new Date(startDate); threeMonthsLater.setMonth(threeMonthsLater.getMonth() + 3); const currentDate = new Date(); const isOver = currentDate > threeMonthsLater; console.log(`当前时间是否超过三个:${isOver}`); 或者封装成函数: function isOverThreeMonths(startDate) { const threeMonthsLater = new Date(startDate); threeMonthsLater.setMonth(threeMonthsLater.getMonth() + 3); return new Date() > threeMonthsLater; } // 使用示例 const startDate = new Date('2023-01-01'); console.log(isOverThreeMonths(startDate)); // true或false 同时,需要提醒用户关于末的情况,比如如果startDate是2023-01-31,那么threeMonthsLater会是2023-05-01(因为四只有30天),这时候是否正确。 另外,可能用户的需求是相对于当前时间往前三个,比如判断某个时间是否在过去的三个内。但根据问题描述,“当前时间是否超过了三个”可能是指从某个时间点开始到现在是否超过三个,所以应该是从过去的某个时间点开始计算到现在是否超过三个。 所以,正确的处理方式应该是计算开始日期加上三个后的日期,然后看当前是否在该日期之后。 因此,最终的代码应该包括这些部分,并给出注意事项。</think>以下是一段用于判断当前时间是否超过指定起始日期三个的 JavaScript 代码示例: ```javascript function isOverThreeMonths(startDate) { // 创建三个后的日期对象(避免修改原日期) const threeMonthsLater = new Date(startDate); threeMonthsLater.setMonth(threeMonthsLater.getMonth() + 3); // 获取当前时间 const currentDate = new Date(); // 比较当前时间是否超过三个后 return currentDate > threeMonthsLater; } // 使用示例 --------------- const startDate = new Date('2023-01-01'); // 替换为你的实际起始时间 console.log(isOverThreeMonths(startDate)); // 输出 true/false ``` --- ### 📝 注意事项: 1. **末日期处理** 如果起始日期是末(如 `2023-01-31`),加三个后会自动调整为有效日期(如 `2023-04-30` 不存在,则变为 `2023-05-01`)。这是 `Date` 对象的默认行为,确保计算合法。 2. **时间精度** 代码精确到毫秒,若需按“自然”忽略时间,可在比较前将时间部分归零: ```javascript threeMonthsLater.setHours(0, 0, 0, 0); currentDate.setHours(0, 0, 0, 0); ``` 3. **时区问题** `new Date()` 使用本地时区,若需 UTC 时间,改用 `new Date().toUTCString()`。 --- ### 🌰 实际场景举例: 假设用户注册时间为 `2023-06-15`,判断当前是否超过三个有效期: ```javascript const registrationDate = new Date('2023-06-15'); if (isOverThreeMonths(registrationDate)) { console.log("账号已过期"); } else { console.log("账号有效"); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

优快云资讯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值