英文:
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 = "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
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:
> 2019-04-01
> 2019-04-02
> 2019-04-03
> ...
Printing example in Danish:
ChronoFormatter<CalendarQuarter> printer =
ChronoFormatter.ofPattern(
"QQQQ y",
PatternType.CLDR,
new Locale("da"),
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 = "20202"
(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
正则表达式解释:
(\\d{4})(\\d{1,2})
指定两个组:第一个组有 4 位数字,第二个组有一到两位数字- 替换字符串
$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 = "20202";
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 = "20202";
// Convert the given string into yyyy-Q format
str = str.replaceAll("(\\d{4})(\\d{1,2})", "$1-$2");
// Parse the converted string
YearQuarter yearQuarter = YearQuarter.parse(str, DateTimeFormatter.ofPattern("yyyy-Q"));
// Define output format
DateTimeFormatter outputFormat = DateTimeFormatter.ofPattern("uuuuQ");
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
(或者long
或BigInteger
)。为什么呢?你的问题类似。
使用YearQuarter
或YearMonth
的优势包括(可能不仅限于):
- 自说明的代码:
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 = "20202";
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 = "20202"
tells nothing about the meaning of this piece of data. A variable of typeYearQuarter
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 overString
. - 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.
Link
Oracle tutorial: Date Time explaining how to use java.time.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论