我在MongoDB聚合查询中使用$ year和$ week来按年份和年份对结果进行分组。在Java代码中,我想将返回的年份,一年中的一周转换为DateTime对象,以便更容易地呈现数据。
似乎Joda DateTime的getWeekOfWeekyear()在MongoDB中的行为与$ week不同,这会导致不同的日期结果。
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()
此外,查询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代码中的这种不一致问题?
答案 0 :(得分:2)
Joda-Time遵循ISO 8601中的defining weeks标准。
W
写成,例如W23
。年份可以加上2015-W23
。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.