英文:
How to convert weeks of year to LocalDate
问题
我有一个包含年-周(例如"2015-40")和年-月(例如"2015-08")格式的字符串,想要在Scala中将其转换为LocalDate。
我尝试过使用:
val date = "2015-40"
val formatter = DateTimeFormatter.ofPattern("yyyy-ww")
LocalDate.parse(date, formatter)
但是最终遇到了DateTimeParseException错误。如果能提供关于如何解决这个问题的任何帮助,将不胜感激。
提前感谢。
英文:
I have a string with both year-week e.g. "2015-40" and year-month formats e.g. "2015-08", that would like to transform into LocalDate in Scala.
I have tried using
val date = "2015-40"
val formatter = DateTimeFormatter.ofPattern("yyyy-ww")
LocalDate.parse(date, formatter)
but end up with DateTimeParseException error. Would appreciate any help on how to do this
Thanks in advance
答案1
得分: 4
import java.time.LocalDate
import java.time.format.DateTimeFormatterBuilder
import java.time.temporal.{ChronoField, DayOfWeek}
import java.util.Locale
object Main extends App {
val date = "2015-40"
val formatter = new DateTimeFormatterBuilder()
.appendPattern("YYYY-ww")
.parseDefaulting(ChronoField.DAY_OF_WEEK, DayOfWeek.MONDAY.getValue())
.toFormatter(Locale.FRANCE)
val ld = LocalDate.parse(date, formatter)
println(ld)
}
Output:
2015-09-28
在解析 2015-40
这个日期字符串到 LocalDate
时出现异常的原因是:
LocalDate
表示一个日历日期,而第40周包含7天。Java 不知道你想要这7天中的哪一天,所以拒绝为你做出选择。在我上面的代码中,我已经指定了星期一。周内的其他任何一天都应该有效。- 更微妙的是,虽然对于人类来说,2015年和第40周是明确的,但在新年附近的情况下,并不总是如此,因为第1周可能在新年之前开始,第52周或第53周可能在新年之后延续。因此,一个日历年和一个周数并不总是定义一个特定的周。相反,我们需要引入“周年”或“基于周的年”的概念。周年从包含第1周开始,无论这是否意味着它在新年前几天或之后几天开始。它持续到最后一周的最后一天,通常也是在新年前几天或之后几天。要告诉
DateTimeFormatter
我们要解析(或打印)周年,我们需要使用大写的YYYY
,而不是小写的yyyy
(或uuuu
)。
另外,如果你可以影响日期格式的话,考虑使用带有 W
的格式,例如 2015-W40
。这是 ISO 8601 格式的年和周表示方式。在 ISO 中,2015-12
表示年和月,很多人会这样理解。因此,为了消除歧义和避免误读,可以采用带有 W
的格式。
编辑:
在我的解释中,我假设了 ISO 周计数方式(星期一是一周的第一天,第1周被定义为新年后至少有4天的第一周)。你可以将不同的区域设置传递给格式化器构建器,以获得不同的周计数方式。
如果你确信你的周计数遵循 ISO 标准,评论中的 Andreas 的建议已经足够解决问题:
或者,添加 ThreeTen Extra 库,这样你可以使用
YearWeek
类,它有一个方便的atDay(DayOfWeek dayOfWeek)
方法,可以用来获取LocalDate
。
<details>
<summary>英文:</summary>
String date = "2015-40";
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("YYYY-ww")
.parseDefaulting(ChronoField.DAY_OF_WEEK, DayOfWeek.MONDAY.getValue())
.toFormatter(Locale.FRANCE);
LocalDate ld = LocalDate.parse(date, formatter);
System.out.println(ld);
Sorry that I can write only Java, I trust you to translate to Scala. Output is:
> 2015-09-28
Why were you getting an exception? There were a couple of things missing for us to parse `2015-40` into a `LocalDate`:
- A `LocalDate` is a calendar date, and week 40 consists of 7 days. Java doesn’t know which of those 7 days you want and refuses to make the choice for you. In my code above I have specified Monday. Any other day of the week should work.
- A bit subtler. While to a human year 2015 and week 40 is unambiguous, the same is not always the case around New Year where week 1 may begin before New Year or week 52 or 53 extend after New Year. So a calendar year and a week number don’t always define one specific week. Instead we need the concept of a *week year* or *week-based year*. A week year lasts from week 1 inclusive no matter if that means it begins a few days before or after New Year. And it lasts until the last day of the last week, again most often a few days before or after New Year. To tell a `DateTimeFormatter` that we want to parse (or print) the week year we need to use upper case `YYYY` instead of lower case `yyyy` (or `uuuu`).
As an aside, if you can influence the format, consider `2015-W40` with a `W`. This is ISO 8601 format for year and week. In ISO `2015-12` denotes year and month, and many will read it this way. So to disambiguate and avoid misreading.
Edit:
In my explanation I have assumed ISO week scheme (Monday is the first day of week and week 1 is defined as the first week having at least 4 days in the new year). You may pass a different locale to the formatter builder to obtain a different week scheme.
If you are sure that your weeks follow ISO, Andreas’ suggestion in the comment is good enough that we want as part of the answer:
> Alternatively, add the [ThreeTen Extra](https://www.threeten.org/threeten-extra/) library, so you can use the
> [`YearWeek`](https://www.threeten.org/threeten-extra/apidocs/org.threeten.extra/org/threeten/extra/YearWeek.html)
> class, that has a nice [`atDay(DayOfWeek
> dayOfWeek)`](https://www.threeten.org/threeten-extra/apidocs/org.threeten.extra/org/threeten/extra/YearWeek.html#atDay(java.time.DayOfWeek))
> method for getting a `LocalDate`.
**Link:** [Wikipedia article: ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)
</details>
# 答案2
**得分**: 0
`LocalDate`有三个部分:年、月和月份中的日期。因此,在`year-month`字符串的情况下,根据您的要求,您将需要获取特定日期的`LocalDate`。解析年月字符串就像演示代码中所示的那样直接。
在`year-week`字符串的情况下,您将需要获取特定星期几的`LocalDate`,例如星期一或今天的日期等。此外,与其直接解析字符串,我发现更容易获取年份和周数,然后使用`LocalDate`的方法来获取所需的`LocalDate`实例。
```java
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
public class Main {
public static void main(String[] args) {
//#################### Year-Month #######################
// 给定 year-month 字符串
var yearMonthStr = "2015-08";
// 从 yearMonthStr 解析出的 LocalDate,位于该月的第一天
LocalDate date2 = YearMonth.parse(yearMonthStr, DateTimeFormatter.ofPattern("u-M")).atDay(1);
System.out.println(date2);
// 从 yearMonthStr 解析出的 LocalDate,位于该月的最后一天
date2 = YearMonth.parse(yearMonthStr, DateTimeFormatter.ofPattern("u-M")).atEndOfMonth();
System.out.println(date2);
// 从 yearMonthStr 解析出的 LocalDate,位于该月的特定日期
date2 = YearMonth.parse(yearMonthStr, DateTimeFormatter.ofPattern("u-M")).atDay(1).withDayOfMonth(10);
System.out.println(date2);
//#################### Year-Week #######################
// 给定 year-week 字符串
var yearWeekStr = "2015-40";
// 在 '-' 上拆分字符串,并获取年份和周数值
String[] parts = yearWeekStr.split("-");
int year = Integer.parseInt(parts[0]);
int week = Integer.parseInt(parts[1]);
// 拥有年份、周数和今天的日期,例如星期五的 LocalDate
LocalDate date1 = LocalDate.now()
.withYear(year)
.with(WeekFields.ISO.weekOfYear(), week);
System.out.println(date1);
// 拥有年份、周数和下一个星期一(如果今天是星期一,则为同一天)
date1 = LocalDate.now()
.withYear(year)
.with(WeekFields.ISO.weekOfYear(), week)
.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));
System.out.println(date1);
// 拥有年份、周数和今天之前的星期一(如果今天是星期一,则为同一天)
date1 = LocalDate.now()
.withYear(year)
.with(WeekFields.ISO.weekOfYear(), week)
.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
System.out.println(date1);
}
}
输出:
2015-08-01
2015-08-31
2015-08-10
2015-10-02
2015-10-05
2015-09-28
英文:
LocalDate
has three parts: year, month and day of the month. So, in the case of year-month
string, you will have to get the LocalDate
on a specific day as per your requirement. Parsing the year-month string is straight forward as shown in the demo code.
In the case of year-week
string, you will have to get the LocalDate
on a specific day of the week e.g. Mon or today's day etc. Also, instead of parsing the string directly, I found it easier to get the year and week and then use the methods LocalDate
to get the required instance of LocalDate
.
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
public class Main {
public static void main(String[] args) {
//#################### Year-Month #######################
// Given year-month string
var yearMonthStr = "2015-08";
// LocalDate parsed from yearMonthStr and on the 1st day of the month
LocalDate date2 = YearMonth.parse(yearMonthStr, DateTimeFormatter.ofPattern("u-M")).atDay(1);
System.out.println(date2);
// LocalDate parsed from yearMonthStr and on the last day of the month
date2 = YearMonth.parse(yearMonthStr, DateTimeFormatter.ofPattern("u-M")).atEndOfMonth();
System.out.println(date2);
// LocalDate parsed from yearMonthStr and on specific day of the month
date2 = YearMonth.parse(yearMonthStr, DateTimeFormatter.ofPattern("u-M")).atDay(1).withDayOfMonth(10);
System.out.println(date2);
//#################### Year-Week #######################
// Given year-week string
var yearWeekStr = "2015-40";
// Split the string on '-' and get year and week values
String[] parts = yearWeekStr.split("-");
int year = Integer.parseInt(parts[0]);
int week = Integer.parseInt(parts[1]);
// LocalDate with year, week and today's day e.g. Fri
LocalDate date1 = LocalDate.now()
.withYear(year)
.with(WeekFields.ISO.weekOfYear(), week);
System.out.println(date1);
// LocalDate with year, week and next Mon (or same if today is Mon)
date1 = LocalDate.now()
.withYear(year)
.with(WeekFields.ISO.weekOfYear(), week)
.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));
System.out.println(date1);
// LocalDate with year, week and today's day previous Mon (or same if today is Mon)
date1 = LocalDate.now()
.withYear(year)
.with(WeekFields.ISO.weekOfYear(), week)
.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
System.out.println(date1);
}
}
Output:
2015-08-01
2015-08-31
2015-08-10
2015-10-02
2015-10-05
2015-09-28
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论