【问题标题】:How to get Months from a given quarter如何从给定季度获取月份
【发布时间】:2020-03-26 05:25:12
【问题描述】:

我正在尝试从某个季度获取月份。使用下面的代码,我成功地从 LocalDate.now() 实例中获取了当前季度的月份名称。

我如何从仅一个季度字符串(例如“Q1”)中获得一个季度的月份?

int monthInt = Month.from(LocalDate.now()).firstMonthOfQuarter().getValue();
      for (int j = 1; j <= 3; j++) { //for each month in quarter
          System.out.println(Month.of(monthInt).name()); //January, February, March
          monthInt++;
      }

【问题讨论】:

  • 如果我理解正确的话,从像Q1 这样的四分之一字符串到月份名称的映射是不变的,对吧? Q1 始终表示一月至三月。
  • 我最初使用int quarter = LocalDate.now().get(IsoFields.QUARTER_OF_YEAR);,其中 IsoFields.QUARTER_OF_YEAR 是常量,并将其转换为字符串。
  • 所以你没有四分之一字符串,而是四分之一int?
  • 我有一个四分之一字符串,最初是一个 int

标签: java java-time localdate date


【解决方案1】:

我们可以通过查看getFrom的声明IsoFields.QUARTER_OF_YEAR来了解JDK是如何计算季度的:

public long getFrom(TemporalAccessor temporal) {
    if (isSupportedBy(temporal) == false) {
        throw new UnsupportedTemporalTypeException("Unsupported field: QuarterOfYear");
    }
    long moy = temporal.getLong(MONTH_OF_YEAR);
    return ((moy + 2) / 3);
}

注意它如何使用公式quarter = (moy + 2) / 3。因此,要找到一个季度的起始月份,我们只需将其重新排列为moy - moy = quarter * 3 - 2

你可以这样写一个方法:

private static List<String> monthNamesFromQuarter(int quarter) {
    // you can do the validation of quarter yourself
    int start = quarter * 3 - 2;
    return IntStream.range(start, start + 3)
            .mapToObj(Month::of)
            .map(Month::name)
            .collect(Collectors.toList());
}

【讨论】:

  • 非常感谢!我想知道是否有 API 解决方案,但这同样适用。
  • 有一个 API 可以在给定 month 的情况下找到季度的开始,但既然你说你有一个季度数,你需要在这里做一些数学运算。 @xblaz3kx
【解决方案2】:

tl;博士

使用org.threeten.extra.YearQuarter 类,以及QuarterZoneIdLocalDateMonth

YearQuarter                         // Represent an entire quarter of a specific year.
.now( ZoneId.of( "Asia/Tokyo" ) )   // Determine the current quarter as seen via the wall-clock time used by the people of a particular region (a time zone). 
.with(                              // Move to another quarter.
    Quarter.valueOf( "Q1" )         // Or, `Quarter.of( 1 )` if starting with an integer number rather than a `String` object.
)                                   // Returns another `YearQuarter` object, rather than modifying the original.
.atDay( 1 )                         // Returns a `LocalDate` object.
.getMonth()                         // Returns a `Month` enum object.
.getDisplayName(                    // Automatically localize the name of the month. 
    TextStyle.FULL ,                // How long or abbreviated do you want the translation.
    Locale.US                       // Or Locale.CANADA_FRENCH and so on. 
)                                   // Returns a `String` object.

一月

YearQuarter in ThreeTen-Extra

ThreeTen-Extra 库有一个你可能会发现对这项工作有用的类:YearQuarter

获取当前季度。我们需要一个时区来确定当前日期,从而确定当前季度。对于任何给定的时刻,日期在全球各地都因时区而异。

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
YearQuarter currentYearQuarter = YearQuarter.now( z ) ;

但是您想通过解析字符串来确定四分之一。

如果你有一个样式类似于ISO 8601 的字符串(标准实际上并没有指定四分之一)YYYY-Qq 那么YearQuarter 可以directly parse

String input = "2020-Q1" ;
YearQuarter yearQuarter = YearQuarter.parse( input ) ;

如果您只有季度部分而没有年份,请使用Quarter 枚举。如果您的输入字符串是Q1 等,请使用valueOf 检索匹配的枚举对象。

String input = "Q1" ;
Quarter quarter = Quarter.valueOf( input ) ;

如果您有一个数字而不是字符串,即1234,则使用静态方法Quarter.of。顺便说一句,在您自己的代码中,您应该传递这些 Quarter 对象而不是单纯的整数或字符串,以使您的代码更具自我记录性、提供类型安全性并确保有效值。

int input = 1 ; // Domain: 1, 2, 3, 4. 
Quarter quarter = Quarter.of( input ) ;

将该Quarter 实例应用于我们当前的YearQuarter 实例以获取另一个YearQuarter 实例。这些类使用immutable objects 模式,因此我们不是在修改现有实例,而是在生成新实例。

YearQuarter yearQuarter = currentYearQuarter.with( quarter ) ;

yearQuarter.toString(): 2019-Q1

从该年季度获取第一个日期 (LocalDate)、年月 (YearMonth) 和 Month 枚举对象。

LocalDate firstDate = yearQuarter.atDay( 1 ) ;
YearMonth yearMonth1 = YearMonth.from( firstDate ) ;
YearMonth yearMonth2 = yearMonth1.plusMonths( 1 ) ;
YearMonth yearMonth3 = yearMonth1.plusMonths( 2 ) ;

生成一个包含 automatically localized 月份名称的字符串。

Locale locale = Locale.US ;  // Or Locale.CANADA_FRENCH and so on. 
String output1 = yearMonth1.getMonth().getDisplayName( TextStyle.FULL , locale ) ;

一月


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

【解决方案3】:

我建议我们构建每个季度包含的月份的地图,并在需要某个季度字符串的月份时进行简单的地图查找。流管道和足够的DateTimeFormatter 将在短短几行内构建这样的地图:

static Map<String, List<Month>> monthsPerQuarter = Arrays.stream(Month.values())
        .collect(Collectors.groupingBy(
                DateTimeFormatter.ofPattern("QQQ", Locale.ENGLISH)::format));

IMO 最大的优势是我们不需要自己做数学运算。虽然从季度数字到月份数字的数学转换对于编写它的人来说似乎很简单,但它不仅容易出错,而且对于许多读者来说也不清楚且难以破译。

现在我们可以像这样查找示例:

    System.out.println(monthsPerQuarter.get("Q3"));

输出是:

[JULY, AUGUST, SEPTEMBER]

如果您需要单独的月份:

    monthsPerQuarter.get("Q4").forEach(System.out::println);
OCTOBER
NOVEMBER
DECEMBER

如果您的季度最初是一个介于 1 到 4 之间的数字,您可以改用此地图:

static Map<Integer, List<Month>> monthsPerQuarter = Arrays.stream(Month.values())
        .collect(Collectors.groupingBy(m -> m.get(IsoFields.QUARTER_OF_YEAR)));

我推荐 map 方法的一个原因是:从Month 转换为季度字符串很容易,但 java.time 没有提供从季度字符串转换为月份的简单方法。只是为了演示而不是我推荐的方式,可能会这样做:

    DateTimeFormatter quarterFormatter = new DateTimeFormatterBuilder()
            .appendPattern("QQQ")
            // Any year will do since all years have the same 4 quarters @ the same 3 months
            .parseDefaulting(ChronoField.YEAR, 2000)
            .parseDefaulting(IsoFields.DAY_OF_QUARTER, 1)
            .toFormatter(Locale.ENGLISH);

    String qString = "Q1";
    Month firstMonthOfQuarter = Month.from(quarterFormatter.parse(qString));
    IntStream.range(0, 3)
            .mapToObj(firstMonthOfQuarter::plus)
            .forEach(System.out::println);
JANUARY
FEBRUARY
MARCH

这是 11 行代码,而不是 4 行,我看不到任何收益。

【讨论】:

  • 这是一个非常好的和干净的解决方案。谢谢!
【解决方案4】:

对于quarter &gt; firstmonth,规则是

Q1 -> 1
Q2 -> 4
Q3 -> 7
Q4 -> 10

With math : quarterValue *3 -2

所以在 Java 中应用这个:

String quarter = "Q1";
int monthInt = Integer.parseInt(quarter.replaceAll("\\D", "")) * 3 - 2;
for (int j = 0; j < 3; j++) {
    System.out.println(Month.of(monthInt + j).name());
}

List<String> quarters = Arrays.asList("Q1", "Q2", "Q3", "Q4");
for (String quarter : quarters) {
    System.out.print(quarter);
    int monthInt = Integer.parseInt(quarter.replaceAll("\\D", "")) * 3 - 2;
    for (int j = 0; j < 3; j++) {
        System.out.print(" " + Month.of(monthInt + j).name());
    }
    System.out.println();
}

Q1 JANUARY FEBRUARY MARCH
Q2 APRIL MAY JUNE
Q3 JULY AUGUST SEPTEMBER
Q4 OCTOBER NOVEMBER DECEMBER

【讨论】:

    猜你喜欢
    • 2014-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 2020-11-16
    • 1970-01-01
    • 2020-04-28
    相关资源
    最近更新 更多