【问题标题】:Why are SQL Server week numbers different to Java week numbers?为什么 SQL Server 周数与 Java 周数不同?
【发布时间】:2012-02-24 06:38:13
【问题描述】:

我正在使用 Java 6 和 SQL Server 2008 开发报告系统。对于某些查询,我想按周数查看数据。我正在使用 Java 来填补数据中的空白以制作连续的时间线,我发现

java.util.Calendar cal = new java.util.GregorianCalendar();
cal.set(2012, 0, 1);
cal.get(Calendar.WEEK_OF_YEAR);

org.joda.time.DateTime date = new org.joda.time.DateTime(2012, 01, 01, 0, 0);
date.getWeekOfWeekyear();

返回不同的周数

DATEPART(WEEK, '2012-01-01')

是否有解决这种差异的方法,或者我是否必须选择使用 SQL Server Java 周数?

TIA

【问题讨论】:

标签: java sql sql-server-2008 date week-number


【解决方案1】:

tl;博士

int iso8601WeekNumber = 
    ZonedDateTime.now( ZoneId.of ( "America/Montreal" ) )
        .get( IsoFields.WEEK_OF_WEEK_BASED_YEAR )

一周的多种定义

一周有很多定义。

对于某些人来说,一周的第一天是星期日,有些人是星期一,还有一些人是其他一天。

对于某些人来说,一年中的第一周包含 1 月 1 日,而另一些人则将第一周视为包含上述工作日开始的那一周,而其他人则将第一周视为包含特定日期-星期。

因此,您应该在没有研究文档的情况下,永远不要假设“周”或“周数”的含义。

避免使用旧的日期时间类

一般来说,您应该避免与 Java 的早期版本捆绑在一起的旧的遗留日期时间类,因为它们设计不佳、令人困惑且麻烦。

具体来说,java.util.Calendar 类的星期定义因地区而异。因此,如果您想要可靠的恒定结果,请不要使用它。

ISO 8601

ISO 8601 标准定义了日期时间值的格式和相关问题。它specifically defines一周和一年中的一周的含义:

  • 每周从周一到周日,编号为 1-7,其中周一 = 1。
  • 第 1 周包含一年中的第一个星期四,每年产生 52 或 53 周。

乔达时间

Joda-Time 库使用 ISO 8601 标准定义的一周和一年中的一周。

但是,请注意 Joda-Time 项目现在位于 maintenance mode,团队建议迁移到 java.time。

java.time

java.time 类使用 ISO 8601 定义的一周:第 1 周有第一个星期四,一周从星期一到星期日。

IsoFields 类定义了基于周的年份。我们可以要求:

首先我们得到当前的日期时间。

ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now ( zoneId );

询问该日期时间对象,询问基于周的标准年份。

int week = now.get ( IsoFields.WEEK_OF_WEEK_BASED_YEAR );
int weekYear = now.get ( IsoFields.WEEK_BASED_YEAR );

转储到控制台。

System.out.println ( "now: " + now + " is week: " + week + " of weekYear: " + weekYear );

现在:2016-01-17T20:55:27.263-05:00[America/Montreal] is week: 2 of weekYear: 2016

有关详细信息,请参阅此类似问题:How to calculate Date from ISO8601 week number in Java

WeekFields

在java.time中你也可以调用WeekFields类,比如WeekFields.ISO.weekBasedYear()。应该与 Java 8 或更高版本中的 IsoFields 具有相同的效果(Java 8 的早期版本中的一些 bugs were fixed)。


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date.Calendarjava.text.SimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到 java.time。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

    【解决方案2】:

    Java 在计算周数方面更加复杂,而 SQL-Server DATEPART(WEEK... 更加简单。我发现以下文档记录在 here

    "在设置或获取 WEEK_OF_MONTH 或 WEEK_OF_YEAR 字段时,Calendar 必须确定月份或年份的第一周作为参考点。月份或年份的第一周定义为从 getFirstDayOfWeek( ) 并至少包含 getMinimalDaysInFirstWeek() "

    我认为这默认为ISO 标准,这是一年中的第一周,其中有一个星期四(星期一至星期日,一年中至少有 4 天)。考虑使用:

    在 SQL Server 中,DATEPART(WEEK, ..) 函数要简单得多,它只是计算 1 月 1 日和输入日期之间的周边界数(由 DATEFIRST 定义),因此 1 月 1 日将 总是是第 1 周。您不妨考虑使用:

    SELECT DATEPART(ISO_WEEK, '01/01/2012')
    

    这将获取 ISO 标准定义的周数,这是一年中的第一周,其中有一个星期四(星期一至星期日,一年中至少有 4 天)。

    由于 SQL Server 的计算更加简单,因此无法对其进行配置,这意味着您需要在 Java 中配置周数。只需确保使用正确的 getFirstDayOfWeek()getMinimalDaysInFirstWeek() 值在 Java 中设置日历:

    public static Calendar getISOCalendar() {
        Calendar calendar = Calendar.getInstance();
        calendar.setMinimalDaysInFirstWeek(4);
        calendar.setFirstDayOfWeek(Calendar.MONDAY);
        return calendar;
    }
    

    然后您可以确保周数一致。

    public static void main(String args[]) {
        Calendar calendar = getISOCalendar();
        calendar.set(2012,0,1);
        System.out.println(calendar.get(Calendar.WEEK_OF_YEAR));
    }
    
    ------
    52
    

    【讨论】:

    • 使用 ISO_WEEK 似乎是答案。运行一些测试,我使用标准 Java、JODA 和 SQL 获得一系列日期的周数,表明它们保持一致。非常感谢。
    • 使用了不同的周编号系统,因此可能是导致不一致的原因。见en.wikipedia.org/wiki/Seven-day_week#Week_numbering
    • @BasilBourque 我已经对答案进行了编辑,删除了关于默认值的这句话,并建议如何使用 ISO 标准设置日历。
    【解决方案3】:
       System.out.println("get date range from week number and year in java");
       System.out.println(); // print a blank line
    
       // get the input from the user
       Scanner sc = new Scanner(System.in);
    
       System.out.print("Enter the week : ");
       int weekNumber  = sc.nextInt(); 
       System.out.print("Enter the Year: ");
       int year = sc.nextInt() ;
    
    
    
       Calendar cal = Calendar.getInstance();
       //cal.setTime(new Date());
    
       cal.set(Calendar.YEAR, year);
       cal.set(Calendar.WEEK_OF_YEAR, weekNumber);
    
       SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
    
       cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
       System.out.println(formatter.format(cal.getTime())); // start date
    
       cal.add(Calendar.DAY_OF_WEEK, 6);
       System.out.println(formatter.format(cal.getTime())); // end date
    

    【讨论】:

      【解决方案4】:

      根据他们各自的文档,java.util.calendarjodaDATEPART 都返回一个 1 到 53 之间的整数,表示星期。此值基于定义为一周的第一天和默认日历(例如公历、中文)的内容。我会检查您的默认设置或查看每个人认为一周的第一天是什么。

      【讨论】:

        【解决方案5】:

        我认为这可能与 2011 年有关,并且根据 SQLServer,它有 53 周的想法。看看Jan / New Year SQL problems!,这个解决方案可能有助于更好地处理 SQLServer 的东西:

        DATEPART(WEEK, DATEADD(WEEK, -1, '2012-01-01'))

        【讨论】:

          猜你喜欢
          • 2017-11-03
          • 1970-01-01
          • 2015-12-08
          • 2016-12-10
          • 1970-01-01
          • 1970-01-01
          • 2019-02-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多