Java 使用ZoneDateTime和Calendar API计算一年中的一周问题
我试图从ISO-8601日期格式的字符串输入计算一年中的一周。最初,我用java.time.ZonedDateTime尝试了这个方法,但它对输入日期-2049年1月2日给出了不正确的结果。然后我尝试了日历API,它也给出了2049年12月31日的错误响应 我已经附上了样本测试代码Java 使用ZoneDateTime和Calendar API计算一年中的一周问题,java,date,calendar,iso8601,zoneddatetime,Java,Date,Calendar,Iso8601,Zoneddatetime,我试图从ISO-8601日期格式的字符串输入计算一年中的一周。最初,我用java.time.ZonedDateTime尝试了这个方法,但它对输入日期-2049年1月2日给出了不正确的结果。然后我尝试了日历API,它也给出了2049年12月31日的错误响应 我已经附上了样本测试代码 public class ZonedDateTimeTest { public static void main(String[] args) throws InterruptedExcepti
public class ZonedDateTimeTest {
public static void main(String[] args) throws InterruptedException {
System.out.println("======================================");
String instantStr1 = "2049-01-02T03:48:00Z";
printYearAndWeekOfYear(instantStr1);
System.out.println("======================================");
String instantStr2 = "2049-12-31T03:48:00Z";
printYearAndWeekOfYear(instantStr2);
System.out.println("======================================");
}
public static void printYearAndWeekOfYear(String ISODate) {
System.out.println("Date provided -> " + ISODate);
ZonedDateTime utcTimestamp = parseToInstant(ISODate).atZone(ZoneOffset.UTC);
int year = utcTimestamp.getYear();
int weekOfYear = utcTimestamp.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
System.out.println("Using ZonedDateTime API:: Year " + year + " weekOfYear " + weekOfYear);
Date d1 = Date.from(parseToInstant(ISODate));
Calendar cl = Calendar.getInstance();
cl.setTime(d1);
int year1 = cl.get(Calendar.YEAR);
int weekOfYear1 = cl.get(Calendar.WEEK_OF_YEAR);
System.out.println("Using Calendar API:: Year " + year1 + " weekOfYear " + weekOfYear1);
}
public static Instant parseToInstant(String ISODate) {
return DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(ISODate, Instant::from);
}
}
上面代码的输出
======================================
Date provided 2049-01-02T03:48:00Z
Using ZonedDateTime API: Year 2049 weekOfYear 53
Using Calendar API: Year 2049 weekOfYear 1
======================================
Date provided 2049-12-31T03:48:00Z
Using ZonedDateTime API: Year 2049 weekOfYear 52
Using Calendar API: Year 2049 weekOfYear 1
======================================
首先,您的代码有四个问题:
- 当您使用
日历时,您使用的是系统默认时区,这可能会改变
的日期。如果将日历设置为使用UTC,则会使其更加一致即时
- 您使用的是
,它将给出日历年而不是周年。您需要改用Calendar.YEAR
Calendar.getWeekYear()
- 您使用的是
,它又是日历年。您应该使用ZoneDateTime.getYear()
utcTimestamp.get(IsoFields.WEEK\u-BASED\u-YEAR)
- 您正在使用的是
,它可能会给您一个非公历,或者它可能会为您要执行的计算设置了不适当的星期一Calendar.getInstance()
import java.util.*;
import java.time.*;
import java.time.format.*;
import java.time.chrono.*;
import java.time.temporal.*;
public class ZonedDateTimeTest {
public static void main(String[] args) {
printYearAndWeekOfYear("2049-01-02T03:48:00Z");
String instantStr2 = "2049-12-31T03:48:00Z";
printYearAndWeekOfYear("2049-12-31T03:48:00Z");
}
public static void printYearAndWeekOfYear(String isoDate) {
System.out.println("Date provided -> " + isoDate);
Instant instant = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(isoDate, Instant::from);
ZonedDateTime utcTimestamp = instant.atZone(ZoneOffset.UTC);
int year = utcTimestamp.get(IsoFields.WEEK_BASED_YEAR);
int weekOfYear = utcTimestamp.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
System.out.println("ZonedDateTime: Year " + year + " weekOfYear " + weekOfYear);
// Force the Gregorian calendar with ISO rules and using UTC
Calendar calendar = new GregorianCalendar();
calendar.setFirstDayOfWeek(Calendar.MONDAY);
calendar.setMinimalDaysInFirstWeek(4);
calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
calendar.setTime(Date.from(instant));
int calYear = calendar.getWeekYear();
int calWeekOfYear = calendar.get(Calendar.WEEK_OF_YEAR);
System.out.println("Calendar: Year " + calYear + " weekOfYear " + calWeekOfYear);
System.out.println();
}
}
输出:
Date provided -> 2049-01-02T03:48:00Z
ZonedDateTime: Year 2048 weekOfYear 53
Calendar: Year 2048 weekOfYear 53
Date provided -> 2049-12-31T03:48:00Z
ZonedDateTime: Year 2049 weekOfYear 52
Calendar: Year 2049 weekOfYear 52
这两个问题在我看来都很好。旧的
日历
-东西确实支持Java-7以来的解决方案,因此我将其作为Jon Skeet的Java-8相关答案的补充:
String instantStr1 = "2049-01-02T03:48:00Z";
String instantStr2 = "2049-12-31T03:48:00Z";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
Date d1 = sdf.parse(instantStr1);
Date d2 = sdf.parse(instantStr2);
GregorianCalendar gcal = new GregorianCalendar();
gcal.setFirstDayOfWeek(Calendar.MONDAY);
gcal.setMinimalDaysInFirstWeek(4);
gcal.setTime(d1);
System.out.println(
"Using Calendar API: Year " + gcal.getWeekYear() + " weekOfYear "
+ gcal.get(Calendar.WEEK_OF_YEAR)
); // Using Calendar API: Year 2048 weekOfYear 53
gcal.setTime(d2);
System.out.println(
"Using Calendar API: Year " + gcal.getWeekYear() + " weekOfYear "
+ gcal.get(Calendar.WEEK_OF_YEAR)
); // Using Calendar API: Year 2049 weekOfYear 52
对于此API为标准API的Android用户:该方法自API级别24起可用。请在问题中添加该注释-从逻辑上讲,这是问题的一部分。不幸的是,您没有在
日历
部分的任何位置指定时区,这很容易影响结果。请停止破坏格式设置。我已经两次修复了你的代码格式,两次你都撤销了它。然后我对结果进行了重新格式化,以便更容易阅读,您再次撤销了它。我很想知道weekOfYear计算。ZoneDateTime或Calendar都可能给出不正确的响应。@kanchan:不,它们没有。查看我的编辑-他们都给出了正确的ISO周-周-年结果,但是Calendar
不允许您获得周-年。类GregorianCalendar
已在Java-7中更新,以便为该问题启用等效的解决方案,请参阅我的答案。@MenoHochschild:啊,我确实没有发现getWeekYear()
在日历中
,习惯于为获取提供字段编号
…非常感谢Meno Hochschild和Jon Skeet的帮助。