算法
您需要编写一个例程,在其中传入一个日期并返回一个代表上一季度最后一天的日期。确实,这听起来像是该方法的好名字,并且更好地总结了您的问题:endingDateOfPreviousQuarter( someDate )
.
或者,在我自己的命名约定中,我使用start
andstop
来表示包容性而不是排斥性边界,或者以其他方式命名,stopDateOfPreviousQuarter( someDate )
.
我会用两种方法做到这一点。
startDateOfQuarter( someDate )
- 通过调用monthOfYear()提取过去日期的月份。
- 确定那个月是哪个季度(测试在 1-3、4-6、7-9、9-12 之间)。
- 获取该季度的开始日期(如下面的代码所示,将 year & month & dayOfMonth 传递给构造函数)。
stopDateOfPreviousQuarter( someDate )
- 调用“startDateOfQuarter”方法,传递相关日期。
- 在返回的开始日期,调用 Joda-Time 方法minusDays(1)。
瞧,您有上一季度最后一天的日期。
这里的主要思想是,通常最好找到时间元素的开始,然后使用minus
,而不是尝试直接获取结束点。获取一个小时、一天、一周、一个月或一个季度的开始。这避免了闰日、闰秒†、夏令时(DST)、记住哪些月份有 30 天和 31 天的错误,以及分辨率不同的小数秒问题,难以确定一个小时或天。此外,至少在我的经验中,关注期初可以清晰地思考日期时间。
信息
ISO 8601不承认季度。有些人扩展了规范来这样做,使用“Q”结合一些标识符,如本 wiki中所述。
如2011 年的讨论中所述, Joda-Time 2不支持季度。
ISO 8601 准确定义了 52 或 53个带编号的周。Joda-Time 支持这一概念,由weekOfWeekYear 表示。一些企业通过 1 到 52/53 范围的子集来定义他们的季度。
或者,您可以在一年的第 3、6、9 和 12 个月末定义您的季度。Joda-Time 具有 DateTime 类的构造函数,可让您指定月份数。请注意使用withTimeAtStartOfDay()方法让 Joda-Time 完成获取一天中第一时刻的工作,因为并非所有时区的所有日子都有午夜。
org.joda.time.DateTimeZone parisDateTimeZone = org.joda.time.DateTimeZone.forID( "Europe/Paris" );
org.joda.time.DateTime q1Start = new org.joda.time.DateTime(2013, 1, 1, 0, 0, parisDateTimeZone ).withTimeAtStartOfDay();
org.joda.time.DateTime q2Start = new org.joda.time.DateTime(2013, 4, 1, 0, 0, parisDateTimeZone ).withTimeAtStartOfDay();
System.out.println( "Q1 begins in Paris FR: " + q1Start );
System.out.println( "Q2 begins in Paris FR: " + q2Start );
// When querying a database or comparing items in a collection to find Q1 data,
// Look for: (GreaterThanOrEqualTo q1Start) AND (LessThan q2Start)
如果您绝对只想要没有任何时间元素的日期,请使用 Joda-Time 的LocalDate类。该类运动一个minusDays()方法,就像DateTime类一样。
顺便说一下,考虑一下您是在处理简单的日期(没有时间)还是日期时间。例如,您可能会认为发票仅使用简单的日期,但实际上它们通常在收到时用时钟机器盖章,其中包括出于法律和审计原因可能需要记录的时间。此外,通常数据库将日期值存储为基于 UTC 的日期时间(没有时区偏移)。
† Joda-Time 忽略闰秒,但我的观点仍然成立。