如何将从MongoDB查询获得的年份和周数转换为Joda datetime?

时间:2015-05-14 15:17:01

标签: java mongodb datetime

我在MongoDB聚合查询中使用$ year和$ week来按年份和年份对结果进行分组。在Java代码中,我想将返回的年份,一年中的一周转换为DateTime对象,以便更容易地呈现数据。

似乎Joda DateTime的getWeekOfWeekyear()在MongoDB中的行为与$ week不同,这会导致不同的日期结果。

  1. 场景1
  2. MongoDB查询:

    db.test.aggregate(
      {$project:
       {week: {$week: ISODate("2016-01-01T00:00:00.000Z") },
        year: {$year: ISODate("2016-01-01T00:00:00.000Z") } }
      }
    )
    

    返回:

    { "_id" : "", "week" : 0, "year" : 2016 }
    

    尝试将这些值转换为Joda DateTime对象时,会抛出异常:IllegalFieldValueException。

    (new DateTime(0, DateTimeZone.UTC)).withWeekyear(2016).withWeekOfWeekyear(0).withDayOfWeek(1).toString()
    
    1. 场景2
    2. 此外,查询2015-05-10,即星期日。

      MongoDB查询:

      db.test.aggregate(
        {$project:
         {week: {$week: ISODate("2016-01-01T00:00:00.000Z") },
          year: {$year: ISODate("2016-01-01T00:00:00.000Z") } }
        }
      )
      

      返回:

      { "_id" : "", "week" : 19, "year" : 2015 }
      

      但是当试图转换为Joda DateTime时,这导致前一周,从2015-05-04开始:

      (new DateTime(0, DateTimeZone.UTC)).withWeekyear(2015).withWeekOfWeekyear(19).withDayOfWeek(1).toString()
      

      结果:

      2015-05-04T00:00:00.000Z
      

      Mongo $ week运算符以0到53之间的数字返回一年中的一周。周从星期日开始,第1周从一年的第一个星期日开始。一年中第一个星期日之前的天数是在第0周。在Java中,WeekOfYear返回值,一年中的第一周是一年中至少4天。根据这个定义,第一周的第1天可能是前一年。星期一也开始一周。

      有没有办法解决Java代码中的这种不一致问题?

1 个答案:

答案 0 :(得分:2)

ISO 8601

Joda-Time遵循ISO 8601中的defining weeks标准。

  • 星期一是一周的第一天。
  • 周数为1至52或53.
  • 周数用大写W写成,例如W23。年份可以加上2015-W23
  • 第1周,W01,包含今年的第一个星期四。

据我所知,这个标准定义在各个国家和行业的使用中越来越普遍。

周日周

MongoDB doc将周数定义为:

  

...一年中的一周,日期为0到53之间的数字。

     

周从星期日开始,第1周从一年的第一个星期日开始。一年中第一个星期日之前的天数在第0周。此行为与strftime标准库函数的“%U”运算符相同。

据我所知,这主要是美国人的定义,在美国境外并没有太多用处。

为什么这个定义说0到53?这意味着“长达54周”。我认为这个定义在任何一年都不会产生54周,但我没想过。

为什么要混合?

你无法真正混合这两个定义。何必?如果您的目标是使用MongoDB的周定义,并按日期时间表示它们,那么请编写自己的转换器。

我自己的建议是放弃MongoDB的定义和功能,并坚持使用标准定义。

查找星期日

如果您想在MongoDB的世界中找到一周开始的星期日,请编写您自己的小功能。输入年份编号和周数,然后返回DateTime。在这种情况下,您不需要Joda-Time的周年功能。

像这样。

int yearNumber = 2015;
int weekNumber = 0;

LocalDate firstWeekSunday = null;
LocalDate firstOfYear = new LocalDate ( yearNumber, 1, 1 );
if ( firstOfYear.getDayOfWeek ( ) == DateTimeConstants.SUNDAY ) {
    firstWeekSunday = firstOfYear;
} else { // ELSE not Sunday.
    firstWeekSunday = firstOfYear.minusDays ( firstOfYear.getDayOfWeek ( ) );  // Joda-Time uses standard ISO 8601 weeks, where Monday = 1, Sunday = 7.
}
LocalDate sunday = firstWeekSunday.plusWeeks ( weekNumber );

DateTimeZone zone = DateTimeZone.forID ( "America/Montreal" );
DateTime dateTime = sunday.toDateTimeAtStartOfDay ( zone );

转储到控制台。

System.out.println ( "Sunday-based week of year:" + yearNumber + " week: " + weekNumber + " starts: " + sunday + "." );
System.out.println ( "Adjusted to time zone: " + zone + " is: " + dateTime + "." );

跑步时。

Sunday-based week of year:2015 week: 0 starts: 2014-12-28.
Adjusted to time zone: America/Montreal is: 2014-12-28T00:00:00.000-05:00.