如何在Scala中获取前一年对应的季度。

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

How to get corresponding quarter of previous year in Scala

问题

我手头有一个日期字符串,格式为 - "20202" ["yyyyQ"]。有没有办法获取上一年对应的季度?

例如,对于20202,应该是20192。

英文:

I have a date string with me in the format - "20202" ["yyyyQ"]. Is there a way to get the corresponding quarter of previous year ?

ex- for 20202 , it should be 20192

答案1

得分: 3

另一个选择是使用我的库 [Time4J][1] 及其类 [CalendarQuarter][2]示例

    String input = "20202";

    ChronoFormatter<CalendarQuarter> f =
        ChronoFormatter.ofPattern(
            "yyyyQ", 
            PatternType.CLDR, 
            Locale.ENGLISH, 
            CalendarQuarter.chronology());
    CalendarQuarter cq = f.parse(input);
    CalendarQuarter quarterInPreviousYear = cq.minus(Years.ONE);
    System.out.println(quarterInPreviousYear); // 2019-Q2

这种解决方案的两个主要优点是

- 日历季度也是时间间隔因此可以轻松按日循环遍历它们
- 支持90多种语言的国际化甚至支持基于样式的国际化

时间间隔示例

    for (PlainDate date : quarterInPreviousYear) {
        System.out.println(date);
    }

输出

    > 2019-04-01 
    > 2019-04-02 
    > 2019-04-03
    > ...

在丹麦语中的打印示例

    ChronoFormatter<CalendarQuarter> printer =
        ChronoFormatter.ofPattern(
            "QQQQ y", 
            PatternType.CLDR, 
            new Locale("da"), 
            CalendarQuarter.chronology());
    System.out.println(printer.print(quarterInPreviousYear)); // 2. kvartal 2019

  [1]: https://github.com/MenoData/Time4J
  [2]: http://time4j.net/javadoc-en/net/time4j/range/CalendarQuarter.html
英文:

An alternative to the other answers is using my lib Time4J and its class CalendarQuarter. Example:

    String input = &quot;20202&quot;;

    ChronoFormatter&lt;CalendarQuarter&gt; f =
        ChronoFormatter.ofPattern(
            &quot;yyyyQ&quot;, 
            PatternType.CLDR, 
            Locale.ENGLISH, 
            CalendarQuarter.chronology());
    CalendarQuarter cq = f.parse(input);
    CalendarQuarter quarterInPreviousYear = cq.minus(Years.ONE);
    System.out.println(quarterInPreviousYear); // 2019-Q2

Two main advantages of this solution are:

  • Calendar quarters are also intervals so it is easy to iterate over them daily.
  • Internationalization is supported for more than 90 languages, even style-based.

Interval example:

    for (PlainDate date : quarterInPreviousYear) {
        System.out.println(date);
    }

Output:

&gt; 2019-04-01 
&gt; 2019-04-02 
&gt; 2019-04-03
&gt; ...

Printing example in Danish:

    ChronoFormatter&lt;CalendarQuarter&gt; printer =
        ChronoFormatter.ofPattern(
            &quot;QQQQ y&quot;, 
            PatternType.CLDR, 
            new Locale(&quot;da&quot;), 
            CalendarQuarter.chronology());
    System.out.println(printer.print(quarterInPreviousYear)); // 2. kvartal 2019

答案2

得分: 2

这是一个简短的 Scastie,展示了一种方法。

val d = "20202";
(d.substring(0, 4).toInt - 1).toString + d.substring(4)  // 20192:String
英文:

Here is a short Scastie showing one approach.

val d = &quot;20202&quot;
(d.substring(0,4).toInt - 1).toString + d.substring(4)  // 20192: String

答案3

得分: 2

## 更新感谢 [Ole V.V.][1]

以下是纯 `DateTimeFormatter` 解决方案

```java
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.time.temporal.IsoFields;

import org.threeten.extra.YearQuarter;

public class Main {
    public static void main(String[] args) {
        // 给定字符串
        String str = "20202";

        DateTimeFormatter dtf = new DateTimeFormatterBuilder()
                                .appendValue(ChronoField.YEAR, 4)
                                .appendValue(IsoFields.QUARTER_OF_YEAR, 1)
                                .toFormatter();

        // 解析转换后的字符串
        YearQuarter yearQuarter = YearQuarter.parse(str, dtf);
        System.out.println(yearQuarter.format(dtf));

        // 一年前
        System.out.println(yearQuarter.minusYears(1).format(dtf));
    }
}

输出:

20202
20192

原始答案:

我建议您使用 ThreeTenExtra 库来实现。唯一需要处理的问题是将给定的字符串转换为 yyyy-Q 格式,您可以使用下面显示的正则表达式轻松实现:

import java.time.format.DateTimeFormatter;

import org.threeten.extra.YearQuarter;

public class Main {
    public static void main(String[] args) {
        // 给定字符串
        String str = "20202";

        // 将给定的字符串转换为 yyyy-Q 格式
        str = str.replaceAll("(\\d{4})(\\d{1,2})", "$1-$2");

        // 解析转换后的字符串
        YearQuarter yearQuarter = YearQuarter.parse(str, DateTimeFormatter.ofPattern("yyyy-Q"));

        // 定义输出格式
        DateTimeFormatter outputFormat = DateTimeFormatter.ofPattern("uuuuQ");
        System.out.println(yearQuarter.format(outputFormat));

        // 一年前
        System.out.println(yearQuarter.minusYears(1).format(outputFormat));
    }
}

输出:

20202
20192

正则表达式解释:

  1. (\\d{4})(\\d{1,2}) 指定两个组:第一个组有 4 位数字,第二个组有一到两位数字
  2. 替换字符串 $1-$2 指定为 group(1)-group(2)

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

## Update (thanks to [Ole V.V.][1])

Given below is pure `DateTimeFormatter` solution:

    import java.time.format.DateTimeFormatter;
    import java.time.format.DateTimeFormatterBuilder;
    import java.time.temporal.ChronoField;
    import java.time.temporal.IsoFields;
    
    import org.threeten.extra.YearQuarter;
    
    public class Main {
    	public static void main(String[] args) {
    		// Given string
    		String str = &quot;20202&quot;;
    
    		DateTimeFormatter dtf = new DateTimeFormatterBuilder()
    								.appendValue(ChronoField.YEAR, 4)
    								.appendValue(IsoFields.QUARTER_OF_YEAR, 1)
    								.toFormatter();
    
    		// Parse the converted string
    		YearQuarter yearQuarter = YearQuarter.parse(str, dtf);
    		System.out.println(yearQuarter.format(dtf));
    
    		// A year ago
    		System.out.println(yearQuarter.minusYears(1).format(dtf));
    	}
    }

**Output:**

    20202
    20192

## Original answer:

I recommend you do it using [ThreeTenExtra](https://www.threeten.org/threeten-extra/) library. The only problem that you will have to deal with is converting the given string into `yyyy-Q` format which you can easily do using a regex as shown below:

    import java.time.format.DateTimeFormatter;
    
    import org.threeten.extra.YearQuarter;
    
    public class Main {
    	public static void main(String[] args) {
    		// Given string
    		String str = &quot;20202&quot;;
    
    		// Convert the given string into yyyy-Q format
    		str = str.replaceAll(&quot;(\\d{4})(\\d{1,2})&quot;, &quot;$1-$2&quot;);
    
    		// Parse the converted string
    		YearQuarter yearQuarter = YearQuarter.parse(str, DateTimeFormatter.ofPattern(&quot;yyyy-Q&quot;));
    
    		// Define output format
    		DateTimeFormatter outputFormat = DateTimeFormatter.ofPattern(&quot;uuuuQ&quot;);
    		System.out.println(yearQuarter.format(outputFormat));
    
    		// A year ago
    		System.out.println(yearQuarter.minusYears(1).format(outputFormat));
    	}
    }

**Output:**

    20202
    20192

**Explanation of the regex:**

 1. `(\\d{4})(\\d{1,2})` specifies two groups: the first group having 4 digits and the second group having one or two digit(s)
 2. The replacement string `$1-$2` specifies `group(1)-group(2)`


  [1]: https://stackoverflow.com/questions/64464257/how-to-get-corresponding-quarter-of-previous-year-in-scala/64469462#comment113998667_64469462

</details>



# 答案4
**得分**: 2

> 我想到使用一些日期时间格式化程序来解决这个问题,...

当然可以!我会立即更喜欢使用Arvind Kumar Avinash提供的使用ThreeTen Extra库中的`YearQuarter`解决方案。如果你认为在项目中添加外部库过于繁琐(或者你的团队或老板这么认为),我们也可以不使用它,只是稍显不够优雅。以下是一个Java解决方案,我认为你可以手动将其翻译成Scala:

```scala
val quarterFormatter: DateTimeFormatter = new DateTimeFormatterBuilder()
  .appendValue(ChronoField.YEAR, 4)
  .appendValue(IsoFields.QUARTER_OF_YEAR, 1)
  .parseDefaulting(IsoFields.DAY_OF_QUARTER, 1)
  .toFormatter()

val quarterString: String = "20202"

val firstMonthOfQuarter: YearMonth = YearMonth.parse(quarterString, quarterFormatter)
val firstMonthOfQuarterLastYear: YearMonth = firstMonthOfQuarter.minusYears(1)
val quarterPreviousYearString: String = firstMonthOfQuarterLastYear.format(quarterFormatter)

println(quarterPreviousYearString)

输出结果是所期望的:

20192

正确数据类型的优势

请问在这种情况下,与简单的字符串操作和转换为整数相比,使用DateTime的优势是什么?

编辑:当你需要从输入中读取一些数字,比如一个ID号码,并在输出时再次打印它,你会将它读入一个int还是一个String?大多数情况下我们会选择int(或者longBigInteger)。为什么呢?你的问题类似。

使用YearQuarterYearMonth的优势包括(可能不仅限于):

  • 自说明的代码: val d = "20202" 对这段数据的含义一无所知。而YearQuarter类型的变量非常明确地告诉读者,这个变量保存了一年的一个季度。YearMonth是标准Java库中最好的近似类型,虽然在这里使用它的原因已经不太明确,但仍然比String要好很多。
  • 免费的验证: 通过使用格式化程序解析字符串,如果字符串不表示有效的季度,我们将通过异常确保得到通知。
  • 更廉价的进一步验证: 如果你想要进一步的验证,例如季度必须在2019年第二季度之后但不得在未来,这在java.time中非常简单明了(虽然字符串也可以做到,但除非你在几行注释中解释清楚正在进行的操作,否则代码会很难阅读)。
  • 为未来的需求做好准备: 如果有一天你需要在季度上执行更多操作,比如找到季度的第一天和最后一天,或者找到之前的季度,这将非常轻松,因为该类提供了非常丰富的操作集。

链接

Oracle教程:日期时间 解释了如何使用java.time。

英文:

> I thought of using some Date time formatter to solve this, …

Do! I would immediately prefer the solution by Arvind Kumar Avinash using YearQuarter of the ThreeTen Extra library. In case you find it overkill to add an external library to your project (or your team or boss thinks so), we can also do without it, only less elegantly. Here’s a Java solution that I think you can hand translate to Scala:

	DateTimeFormatter quarterFormatter = new DateTimeFormatterBuilder()
			.appendValue(ChronoField.YEAR, 4)
			.appendValue(IsoFields.QUARTER_OF_YEAR, 1)
			.parseDefaulting(IsoFields.DAY_OF_QUARTER, 1)
			.toFormatter();
	
	String quarterString = &quot;20202&quot;;
	
	YearMonth firstMonthOfQuarter
			= YearMonth.parse(quarterString, quarterFormatter);
	YearMonth firstMonthOfQuarterLastYear = firstMonthOfQuarter.minusYears(1);
	String quarterPreviousYearString
			= firstMonthOfQuarterLastYear.format(quarterFormatter);
	
	System.out.println(quarterPreviousYearString);

Output is the desired:

> 20192

Advantages of the right data type

> Could you please explain what advantages usage of DateTime may have in
> this case over simple string manipulation and conversion to int?

Edit: When you need to read some number, for example an ID number, from input and print it again on output, do you read it into an int or a String? Most of use int (or long or BigInteger). Why? Your question is similar.

The advantages of using YearQuarter or YearMonth include (and probably are not limited to):

  • Self-explanatory code: val d = &quot;20202&quot; tells nothing about the meaning of this piece of data. A variable of type YearQuarter very clearly tells the reader that this variable hold a quarter of year. YearMonth is the best approximation in the standard Java library and already less clear about why we are using it here, but still a sizeable advantage over String.
  • Validation for free: By parsing the string using the formatter, if the string doesn’t denote a valid quarter of year, we will be sure to be notified through an exception.
  • Further validation cheap: If you want further validation, for example the quarter must be after 2nd quarter of 2019 but not in the future, this is easy and straightforward with java.time (it can be done with the string too, but the code will be hard to read unless you explain over several lines of comment what you’ve got going).
  • Ready for future requirements: If one day you need to do something more with the quarter, like for example finding the first and the last day of the quarter or the quarter before it, this will be a walk in the park because the class offers a very rich set of operations.

Oracle tutorial: Date Time explaining how to use java.time.

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

发表评论

匿名网友

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

确定