开始&月末12个月(一年)

时间:2017-08-05 06:27:57

标签: c# asp.net date datetime

我实际上正在创建一种方法来根据特定的日期时间范围获取我的asp图表的详细信息。我看过这两个链接,

First LinkSecond Link

我检索数据的方法,

//Monthly Statistics
public int janLogin(String username)
{
    int result = 0;

    StringBuilder sqlCmd = new StringBuilder();
    sqlCmd.AppendLine("SELECT COUNT(*) FROM AuditActivity WHERE Username = @getUsername AND DateTimeActivity BETWEEN @getFirstDT AND @getLastDT AND ActivityType = @getType");

    try
    {
        SqlConnection myConn = new SqlConnection(DBConnectionStr);

        myConn.Open();

        SqlCommand cmd = new SqlCommand(sqlCmd.ToString(), myConn);

        //DateTime
        DateTime currentDT = DateTime.Today;

        //Code Below gets only current Start and End of Current month
        DateTime FirstDT = currentDT.AddDays(1 - currentDT.Day);
        DateTime SecondDT = FirstDT.AddMonths(1).AddSeconds(-1);

        cmd.Parameters.AddWithValue("@getUsername", username);
        cmd.Parameters.AddWithValue("@getFirstDT", FirstDT);
        cmd.Parameters.AddWithValue("@getLastDT", SecondDT);
        cmd.Parameters.AddWithValue("@getType", "Login");

        result = Convert.ToInt16(cmd.ExecuteScalar());

        myConn.Close();

        return result;
    }
    catch (SqlException ex)
    {
        logManager log = new logManager();
        log.addLog("AuditNLoggingDAO.janLogin", sqlCmd.ToString(), ex);
        return 0;
    }
}

我正在尝试实际做的事情,你可以从方法名称janLogin看到基本上从 2017年1月1日00:00:00到2017年1月31日登录的人数。本月实际上是八月。我怎样才能真正获得2017年的所有12个月(1月至12月)的开始和结束月份(来自大胆的例子)?

如果我没错,它必须涉及for循环?最终计数是12.但我很确定如何做到这一点..可能有一个重复的问题,但我似乎找不到符合我要求的问题..

感谢任何帮助。谢谢!

4 个答案:

答案 0 :(得分:1)

整个2017日历年

整个日历年:

int year = 2017
DateTime FirstDT = new DateTime(year, 1, 1); // jan 2017
DateTime SecondDT = FirstDT.AddYear(1).AddSeconds(-1); // last second of dec 2017
  

上面的示例会将01 Jan 2017 00:00:00返回31 Dec 2017 23:59:59
  请注意 31 DEC 2017 23:59:59.999999

的答案底部的精度问题

一年中的特定月份

为了获得不同的月份,您可以这样做,而不是使用当前的月份:

int year = 2017;
int month = 12;
DateTime FirstDT = new DateTime(year, month , 1); // december 2017
DateTime SecondDT = FirstDT.AddMonths(1).AddSeconds(-1); // last second of dec 2017
  

上面的示例会将01 Dec 2017 00:00:00返回31 Dec 2017 23:59:59
  请注意 31 DEC 2017 23:59:59.999999

的答案底部的精度问题

然后你可以改变你的方法,将月份作为参数,如下所示:

public int LoginsByMonth(int year, int month, String username)
{
    if (month < 1 || month > 12)
    {
        throw new ArgumentOutOfRangeException("month must be between 1 and 12.");
    }

    ...

    DateTime FirstDT = new DateTime(year, month, 1);
    DateTime SecondDT = FirstDT.AddMonths(1).AddSeconds(-1);

    ...
}

你可以像单身一样打电话给你的方法:

 int loginsNovember2017 = LoginsByMonth(2017, 11, "userA");
 int loginsDecember2017 = LoginsByMonth(2017, 12, "userA");

精确度:使用Ticks而不是Seconds

如下面的评论中所述。您可以使用.AddTicks(-1L)代替.AddSeconds(-1)来获得更准确的过滤器。但是,根据您的要求,您可能更愿意在下一个选项中完全避免这种情况。

避免精确度

同样如下面的评论中所述。通过在SELECT语句中添加< (less than)条件而不是在使用WHERE条件时担心精度,可以完全避免这种精确的头痛问题。

更改将如下所示

BETWEEN

这意味着您可以放弃SELECT COUNT(*) FROM AuditActivity WHERE Username = @getUsername AND DateTimeActivity >= @getFirstDT AND DateTimeActivity < @getLastDT AND ActivityType = @getType AddSeconds(-1)。例如:

AddTicks(-1L)

答案 1 :(得分:1)

所以,我想这里有两个不同的问题。

第一个是&#34;如何按年份和月份获取特定月份的日期范围?&#34;

这是一种方式:

var year = 2017;
for (int month = 1; month <= 12; month++)
{
    var firstDayOfMonth = new DateTime(year, month, 1);
    var lastDayOfMonth = new DateTime(year, month, DateTime.DaysInMonth(year, month));
    Console.WriteLine($"{firstDayOfMonth} - {lastDayOfMonth}");
}

请注意,没有Date - 仅DateTime的值。如果你像我一样省略构造函数中的时间部分,它将被设置为当天的午夜。使用这些值过滤某些日期时,您应首先在其上调用.Date

var datesInThisMonth = someDates.Where(x => x >= firstDayOfMonth && x.Date <= lastDayOfMonth);

...或者在下个月的第一天使用严格的下限作为上限:

var datesInThisMonth = someDates.Where(x => x >= firstDayOfMonth && x <= lastDayOfMonth.AddDays(1));

第二个是如何使用该值来构建SQL查询。

您不应在此使用BETWEEN。根据您用于列的数据类型,它难以消化或者只是完全错误。

这是您的替代选择:

  • 您的专栏属于datetime类型

然后它rounded.000.003.007秒。因此,您在BETWEEN语句中使用的上限将四舍五入到第二天的23:59:59.99700:00。您将错过一些记录,或多次包含一些记录(这将是本月和下一个记录的结果)。

  • 您的专栏属于datetime2类型

然后可以编写正确的查询,但为了做到这一点,你必须考虑在SQL中使用的精度(可以为这个特定表的特定列自定义)并且你应该从C#传递什么值才能在SQL中获得正确的值。

我不明白它是多么值得麻烦。

只需使用x.Date > firstDayOfMonth AND x.Date < firstDayOfNextMonth并忘掉它。

您还可以查看this answer

答案 2 :(得分:1)

为什么不直接使用SQL提供的功能?

@"SELECT MONTH(DateTimeActivity), COUNT(*) FROM AuditActivity 
  WHERE Username = @getUsername AND ActivityType = @getType
  GROUP BY MONTH(DateTimeActivity)
  ORDER BY MONTH(DateTimeActivity)"

这将为您提供12个记录,每个月的计数(假设您有所有年份的日期),只需一次调用数据库,而不是循环内的12个不​​同的调用。并且WHERE语句更简单,没有对DateTimeActivity字段进行计算

这种方法只有一个问题。如果WHERE语句没有找到具有给定约束的任何记录,则它不返回零值的记录。

但是在代码中相对容易处理最终缺失的月份

    cmd.Parameters.AddWithValue("@getUsername", username);
    cmd.Parameters.AddWithValue("@getType", "Login");

    SqlDataReader reader = cmd.ExecuteReader();
    int[] monthCount = new int[12];
    while(reader.Read())
        // -1 because arrays start at zero and January has value 1.
        monthCount[reader.GetInt32(0) - 1] = reader.GetInt32(1);

最后你有一个整数数组,其中每个元素代表每个月的计数,在WHERE语句中的givens约束被应用之后没有记录的数月为零。

答案 3 :(得分:1)

这应该将当前年份的登录作为代表每个月的整数数组得到:)

public int[] yearLogin(String username)
{
    string sqlCmd = @"SELECT COUNT(*)
FROM AuditActivity
WHERE Username = @getUsername AND DateTimeActivity
BETWEEN @getFirstDT AND @getLastDT AND ActivityType = @getType";

    try
    {
        Func<SqlConnection, DateTime, DateTime, int> fetch = (c, f, t) =>
        {
            using (SqlCommand cmd = new SqlCommand(sqlCmd.ToString(), c))
            {
                cmd.Parameters.AddWithValue("@getUsername", username);
                cmd.Parameters.AddWithValue("@getFirstDT", f);
                cmd.Parameters.AddWithValue("@getLastDT", t);
                cmd.Parameters.AddWithValue("@getType", "Login");

                return Convert.ToInt16(cmd.ExecuteScalar());
            }
        };
        using (SqlConnection myConn = new SqlConnection(DBConnectionStr))
        {
            myConn.Open();

            DateTime currentDT = DateTime.Today;
            DateTime FirstDT = currentDT.AddMonths(1 - currentDT.Month).AddDays(1 - currentDT.Day);

            int[] result =
                Enumerable
                    .Range(0, 12)
                    .Select(x => fetch(myConn, FirstDT.AddMonths(x), FirstDT.AddMonths(x + 1).AddTicks(-1L)))
                    .ToArray();

            return result;
        }
    }
    catch (SqlException ex)
    {
        logManager log = new logManager();
        log.addLog("AuditNLoggingDAO.janLogin", sqlCmd.ToString(), ex);
        return null;
    }
}