JD Edwards 日期定义
实际上,根据Oracle.com 页面上的这个简单描述, JD Edwards约会的细节并没有那么血腥:
关于儒略日期格式
JD Edwards World 文件中的日期字段以 Julian 格式存储。…</p>
Julian (*JUL) 日期格式为 CYYDDD,其中:
C加19创建世纪,即0 + 19 = 19, 1 + 19 = 20。YY是世纪中的年份,DDD是年份中的日子。
条款:
- 我将这
C
部分称为“世纪偏移”,即要添加多少个世纪19
。使用0
多年19xx
,1
多年20xx
。
- java.time 框架调用
DDD
“DayOfYear”,“序数日期”是另一个术语。使用“Julian”表示一年内的天数很常见但不正确,与Julian Day冲突。
java.time 框架不包括对解析或生成这种格式的字符串的直接支持,不是我能找到的。
JulianFields
有java.time.temporal.JulianFields
但那些是针对儒略日期的重新定义版本,我们计算从一个纪元(1970-01-01(ISO)而不是历史性的公元前 4714 年 11 月 24 日(公历))开始的天数,同时忽略年份共。因此,这与 JD Edwards 的定义无关,这与问题中链接的该页面上的一些不正确建议相反。
序数日期
此 JD Edwards 日期是序号日期的一个版本。序数日期有时被随意(并且错误地)称为“朱利安”日期,只是因为它共享计算天数序列的想法。但是一个序数日期计算从年初到年底的天数,这个数字总是在 1 到 365/366(闰年)之间,从某个纪元开始计算,并增长为数千个数字。
回到问题,在 java.time 中处理 JD Edwards 日期……</p>
不,我没有发现任何直接或间接的支持 JD Edwards 日期内置于 java.time。
java.date.format包似乎不知道日期的世纪,只有年份和时代。所以我无法找到定义C
JD Edwards 日期的一部分。
JD Edwards 日期的最后一部分,即一年中的序数天数,在日期时间类和格式化类中都得到了很好的处理。
裹LocalDate
由于 JD Edwards 日期显然与 java.time 使用的 ISO 年表具有相同的逻辑,因此目前唯一真正的问题是根据这种特定格式解析和生成 String 对象。所有其他行为都可以从LocalDate
.
由于我找不到java.time.format.DateTimeFormatter
为此目的定义 a 的方法,我建议编写一个实用程序类来处理这些杂务。
理想情况下,我们会扩展LocalDate
类,覆盖它的parse
和toString
方法。也许还有一种getCenturyOffset
方法。但是LocalDate
该类已标记final
并且不能扩展。所以我会创建如下所示的此类,包装一个LocalDate
.
警告:使用风险自负。新鲜的代码,几乎没有运行,几乎没有经过测试。仅作为示例,不用于生产。根据ISC 许可条款使用。
package com.example.whatever;
import java.time.LocalDate;
import java.time.ZoneId;
/**
* Wraps a 'LocalDate' to provide parsing/generating of strings in format known
* as JD Edwards date.
*
* Format is CYYDDD where C is the number of centuries from 1900, YY is the year
* within that century, and DDD is the ordinal day within the year (1-365 or
* 1-366 in Leap Year).
*
* Immutable object. Thread-safe (hopefully! No guarantees).
*
* I would rather have done this by extending the 'java.time.LocalDate' class, but that class is marked 'final'.
*
* Examples: '000001' is January 1 of 1900. '116032' is February 1, 2016.
*
* © 2016 Basil Bourque. This source code may be used according to terms of the ISC License at https://opensource.org/licenses/ISC
*
* @author Basil Bourque
*/
public class JDEdwardsLocalDate {
private LocalDate localDate = null;
private int centuryOffset;
private int yearOfCentury;
private String formatted = null;
// Static Factory method, in lieu of public constructor.
static public JDEdwardsLocalDate from ( LocalDate localDateArg ) {
return new JDEdwardsLocalDate ( localDateArg );
}
// Static Factory method, in lieu of public constructor.
static public JDEdwardsLocalDate parse ( CharSequence charSequenceArg ) {
if ( null == charSequenceArg ) {
throw new IllegalArgumentException ( "Passed CharSequence that is null. Message # 0072f897-b05f-4a0e-88d9-57cfd63a712c." );
}
if ( charSequenceArg.length () != 6 ) {
throw new IllegalArgumentException ( "Passed CharSequence that is not six characters in length. Message # eee1e134-8ec9-4c92-aff3-9296eac1a84a." );
}
String string = charSequenceArg.toString ();
// Should have all digits. Test by converting to an int.
try {
int testAsInteger = Integer.parseInt ( string );
} catch ( NumberFormatException e ) {
throw new IllegalArgumentException ( "Passed CharSequence contains non-digits. Fails to convert to an integer value. Message # 0461f0ee-b6d6-451c-8304-6ceface05332." );
}
// Validity test passed.
// Parse.
int centuryOffset = Integer.parseInt ( string.substring ( 0 , 1 ) ); // Plus/Minus from '19' (as in '1900').
int yearOfCentury = Integer.parseInt ( string.substring ( 1 , 3 ) );
int ordinalDayOfYear = Integer.parseInt ( string.substring ( 3 ) );
int centuryStart = ( ( centuryOffset + 19 ) * 100 ); // 0 -> 1900. 1 -> 2000. 2 -> 2100.
int year = ( centuryStart + yearOfCentury );
LocalDate localDate = LocalDate.ofYearDay ( year , ordinalDayOfYear );
return new JDEdwardsLocalDate ( localDate );
}
// Constructor.
private JDEdwardsLocalDate ( LocalDate localDateArg ) {
this.localDate = localDateArg;
// Calculate century offset, how many centuries plus/minus from 1900.
int year = this.localDate.getYear ();
int century = ( year / 100 );
this.yearOfCentury = ( year - ( century * 100 ) ); // example: if 2016, return 16.
this.centuryOffset = ( century - 19 );
// Format as string.
String paddedYearOfCentury = String.format ( "%02d" , this.yearOfCentury );
String paddedDayOfYear = String.format ( "%03d" , this.localDate.getDayOfYear () );
this.formatted = ( this.centuryOffset + paddedYearOfCentury + paddedDayOfYear );
}
@Override
public String toString () {
return this.formatted;
}
public LocalDate toLocalDate () {
// Returns a java.time.LocalDate which shares the same ISO chronology as a JD Edwards Date.
return this.localDate;
}
public int getDayOfYear () {
// Returns ordinal day number within the year, 1-365 inclusive or 1-366 for Leap Year.
return this.localDate.getDayOfYear();
}
public int getYear () {
// Returns a year number such as 2016.
return this.localDate.getYear();
}
public int getYearOfCentury () {
// Returns a number within 0 and 99 inclusive.
return this.yearOfCentury;
}
public int getCenturyOffset () {
// Returns 0 for 19xx dates, 1 for 20xx dates, 2 for 21xx dates, and so on.
return this.centuryOffset;
}
public static void main ( String[] args ) {
// '000001' is January 1, 1900.
JDEdwardsLocalDate jde1 = JDEdwardsLocalDate.parse ( "000001" );
System.out.println ( "'000001' = JDEdwardsLocalDate: " + jde1 + " = LocalDate: " + jde1.toLocalDate () + " Should be: January 1, 1900. " );
// '116032' is February 1, 2016.
JDEdwardsLocalDate jde2 = JDEdwardsLocalDate.parse ( "116032" );
System.out.println ( "'116032' = JDEdwardsLocalDate: " + jde2 + " = LocalDate: " + jde2.toLocalDate () + " Should be: February 1, 2016." );
// Today
LocalDate today = LocalDate.now ( ZoneId.systemDefault () );
JDEdwardsLocalDate jdeToday = JDEdwardsLocalDate.from ( today );
System.out.println ( "LocalDate.now(): " + today + " = JDEdwardsLocalDate: " + jdeToday + " to LocalDate: " + jdeToday.toLocalDate () );
}
}
跑的时候。
'000001' = JDEdwardsLocalDate: 000001 = LocalDate: 1900-01-01 应该是:1900 年 1 月 1 日。
'116032' = JDEdwardsLocalDate:116032 = LocalDate:2016-02-01 应该是:2016 年 2 月 1 日。
LocalDate.now(): 2016-05-09 = JDEdwardsLocalDate: 116130 到 LocalDate: 2016-05-09
JD Edwards 时间
至于 JD Edwards 时间格式,我搜索并找不到任何文档。如果您知道一些,请编辑您的问题以添加链接。唯一提到 JDE 时间的似乎是从午夜开始的秒数。
如果是这种情况(从午夜开始计算),那么该java.time.LocalTime
课程已经涵盖了您。ALocalTime
可以被实例化并读取为:
纳秒分辨率意味着最多九位小数。处理你提到的六位数没有问题。只需做数学运算,乘/除以1_000L
. LocalTime
请注意,这意味着可能的数据丢失,因为如果该值来自 JD Edwards 数据之外,您可能会截断小数的最后三位(小数的第 7、8、9 位) 。[仅供参考,旧的 java.util.Date/.Calendar 类以及 Joda-Time 仅限于毫秒分辨率,用于小数的三位数字。]
不推荐:你可以做某种组合类,由 aLocalDate
和 a组成LocalTime
。或使用LocalDateTime
. 关键问题是时区。如果 JD Edwards 日期时间始终位于某个时区(例如 UTC),那么组合和使用OffsetDateTime
. 但是,如果它没有特定的时区上下文,如果这些值只是日期时间的模糊概念,而不是时间线上的特定点,那么使用LocalDateTime
它没有时区。如果 JDE 始终采用 UTC,请使用OffsetDateTime
set to ZoneOffset.UTC
。如果要指定时区(偏移量加上处理异常的规则,例如DST),请使用ZonedDateTime
.
推荐:单独使用 LocalTime。我认为您不想在业务逻辑中使用我的 JDEdwardsLocalDate 类,尤其是因为它不是适合 java.time 框架的完整实现。我的意图是使用该类LocalDate
在遇到 JDE 日期时立即转换为。JDE 时间也是如此,LocalTime
立即转换为。如果他们的上下文始终是 UTC,OffsetDateTime
请使用 UTC 创建一个,然后将其传递给您的业务逻辑。仅在必要时返回 JDE 日期和时间(保留到该 JDE 类型的数据库列,或向期望该 JDE 演示的用户报告)。
OffsetDateTime odt = OffsetDateTime.of( myLocalDate , myLocalTime , ZoneOffset.UTC );
如果 JDE 日期和时间隐含了其他一些上下文,则分配预期的时区。
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.of( myLocalDate , myLocalTime , zoneId );
时区在这里至关重要。您必须大致了解这些概念。清楚这一点LocalDate
,LocalTime
并且LocalDateTime
不是时间轴上的时刻。在您将它们调整为时区(或至少与 UTC 偏移)之前,它们没有特定含义。
如果不熟悉 java.time 类型,我在此答案中包含的日期时间类型图可能会对您有所帮助。
您必须了解 JDE 日期和时间的含义以及它们在您的应用程序/数据库中的使用。由于我找不到有关 JDE 时间的任何信息,因此我无法了解有关 JD Edwards 对时区的意图的任何信息。所以我不能提出更具体的建议。