【问题标题】:Finding the first day of a multiple of X years查找 X 年倍数的第一天
【发布时间】:2014-08-21 01:21:38
【问题描述】:

我在创建一个方法来执行以下操作时遇到了一些真正的麻烦:传入一个 unix 时间戳和一个表示获取第一个日期的时间段的 int,该方法返回一个表示第一个日期的 unix 时间戳那一天。我现在使用的方法使用 Calendar 类,如下所示:

public static long firstTime(long in, int period){
    if(period == DAY_OF_FIVE_YEARS){
        period = Calendar.DAY_OF_YEAR;
        in -= in%Vars.FIVE_YEARS;
    }
    cal.setTimeInMillis(in*1000L);
    cal.set(period,cal.getActualMinimum(period));
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    return cal.getTimeInMillis()/1000L;
}

但有时我需要得到五年的第一天。例如,我将传入一个 1987 年的日期,我希望该方法返回 1985 年的第一天。上面的第一个 if 语句是我的尝试,但它不起作用,我想知道是否有更简单的方法。请注意,DAY_OF_FIVE_YEARS 是一些表示它的 int,而 Vars.FIVE_YEARS 是一个 long,表示五年中的秒数。

【问题讨论】:

  • 还没试过,但是如果之后你设置cal,然后year = cal.get(Calendar.YEAR); cal.roll(Calendar.YEAR, -(year%5));呢?我不是最伟大的Calendar 专家,所以如果不尝试,我不知道这是否正确。我猜您的方法不起作用的原因是闰年,因此五年内的秒数并不是真正恒定的。
  • 五是偶数年?
  • @ElliottFrisch 更像是“偶数五年”之类的——“偶数”并不总是意味着可以被 2 整除。
  • 问题未指定。您可以完成您想要的,但您需要更严格地定义每个时期的含义,包括其持续时间和终点。您不能将此基于秒数,因为闰秒会妨碍您,无法“四舍五入”到特定时期的开始,纪元(值 0 的含义)以及时区的复杂性。仔细考虑所有这些问题并重新提出问题。
  • @ajb 感谢您指出滚动方法。这正是所需要的!不敢相信我错过了。此外,我将标题更改为多个而不是偶数,也许这是一种更好的放置方式。不管怎样,感谢您的帮助。

标签: java date graph calendar


【解决方案1】:

感谢 ajb,这是一种可以找到某些年份的第一个倍数的方法:

    public static final int DAY_OF_FIVE_YEARS = 843521;  //just any unused number
    public static long firstTime(long in, int period){
        cal.setTimeInMillis(in*1000L);
        if(period == DAY_OF_FIVE_YEARS){
            int year = cal.get(Calendar.YEAR);
            cal.roll(Calendar.YEAR, -(year%5));
            period = Calendar.DAY_OF_YEAR;
        }
        cal.set(period,cal.getActualMinimum(period));
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        return cal.getTimeInMillis()/1000L;
    }

【讨论】:

【解决方案2】:

tl;博士

LocalDate                          // Represent a date-only value, without time-of-day and without time zone.
.of( 2018 , Month.JANUARY , 23 )   // Get a particular date.
.withDayOfYear( 1 )                // Get first of the year.
.minusYears( 1 )                   // Go back a year

问题的问题

您正在滥用日期时间类,显然试图在 date-with-time-of-day-and-time-zone 类中表示仅日期值。

Vars.FIVE_YEARS 是一个 long 表示五年内的秒数

您将五年表示为秒数的概念在两个方面存在缺陷:(a) 天并不总是 24 小时长,(b) 年并不总是 365 天长。

另一个问题:您应该在日期时间对象中工作,而不是仅仅传递一个毫秒数的整数。这样的数字使调试和记录变得困难,因为人类无法理解它们的含义。因此错误和错误可能会引起注意。如上所述,这些数字可能无法正确代表预期值。

而且您正在使用多年前被 java.time 类取代的糟糕的旧日期时间类。

Instant

将您的毫秒计数解析为Instant

Instant instant = Instant.ofEpochMilli( myMillis ) ;

时区对于确定日期至关重要。对于任何给定的时刻,日期在全球范围内因区域而异。例如,Paris France 中午夜过后几分钟是新的一天,而 Montréal Québec 中仍然是“昨天”。

ZoneId z = ZoneId.of( "Africa/Casablanca" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

LocalDate

LocalDate 类表示没有时间和时区的仅日期值。

从我们上面的ZonedDateTime 中提取日期。

LocalDate ld = zdt.toLocalDate() ;

如果您想要一年中的第一天,请提出。

LocalDate firstOfThisYear = ld.withDayOfYear( 1 ) ;  // Get first of the year.

想回多少年就回多少年。

LocalDate firstOfLastYear = ld.minusYears( 1 ) ;

如果您想使用十年的前半部分或后半部分(0-4 年和 5-9 年),请输入年份编号并进行计算。

int year = ld.getYear() ;  // Ex: 2018

关于java.time

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

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

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

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

从哪里获得 java.time 类?

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

【讨论】:

    猜你喜欢
    • 2021-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-31
    • 2012-06-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多