如何将年周数转换为LocalDate

huangapple go评论88阅读模式
英文:

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

链接: Wikipedia 文章:ISO 8601


<details>
<summary>英文:</summary>

		String date = &quot;2015-40&quot;;
		DateTimeFormatter formatter = new DateTimeFormatterBuilder()
				.appendPattern(&quot;YYYY-ww&quot;)
				.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:

&gt; 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:

&gt; Alternatively, add the [ThreeTen Extra](https://www.threeten.org/threeten-extra/) library, so you can use the
&gt; [`YearWeek`](https://www.threeten.org/threeten-extra/apidocs/org.threeten.extra/org/threeten/extra/YearWeek.html)
&gt; class, that has a nice [`atDay​(DayOfWeek
&gt; dayOfWeek)`](https://www.threeten.org/threeten-extra/apidocs/org.threeten.extra/org/threeten/extra/YearWeek.html#atDay(java.time.DayOfWeek))
&gt; 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 = &quot;2015-08&quot;;

		// LocalDate parsed from yearMonthStr and on the 1st day of the month
		LocalDate date2 = YearMonth.parse(yearMonthStr, DateTimeFormatter.ofPattern(&quot;u-M&quot;)).atDay(1);
		System.out.println(date2);

		// LocalDate parsed from yearMonthStr and on the last day of the month
		date2 = YearMonth.parse(yearMonthStr, DateTimeFormatter.ofPattern(&quot;u-M&quot;)).atEndOfMonth();
		System.out.println(date2);

		// LocalDate parsed from yearMonthStr and on specific day of the month
		date2 = YearMonth.parse(yearMonthStr, DateTimeFormatter.ofPattern(&quot;u-M&quot;)).atDay(1).withDayOfMonth(10);
		System.out.println(date2);

		
		//#################### Year-Week #######################
		// Given year-week string
		var yearWeekStr = &quot;2015-40&quot;;

		// Split the string on &#39;-&#39; and get year and week values
		String[] parts = yearWeekStr.split(&quot;-&quot;);
		int year = Integer.parseInt(parts[0]);
		int week = Integer.parseInt(parts[1]);

		// LocalDate with year, week and today&#39;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&#39;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

huangapple
  • 本文由 发表于 2020年8月21日 22:22:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/63524671.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定