[英]Does JodaTime or Java 8 have special support JD Edwards Date and Time?
當前的主題是一個混亂的特定於域的問題,該問題在Oracle ERP軟件JD Edwards中處理日期。 這個問題記錄了它的細節。
在編寫用於處理來自JD Edwards的日期和時間的包裝器類之前,我想知道JodaTime或Java 8是否對這種獨特的時間格式引入了任何特殊支持,或者無論我使用什么庫,是否都必須進行大量的字符串操作。
這是一個晦澀的問題,因此,只有在您對此問題有特定知識和/或JodaTime / Java 8 / JSR 310的情況下,才請進行響應。
補充:根據Basil Bourque的要求,添加了上述日期附帶的時間戳示例。 這是來自不同表的日期/時間字段的兩個示例:
JCSBMDATE:115100,JCSBMTIME:120102.0
RLUPMJ:114317,RLUPMT:141805.0
此外,日期變量被強制轉換為BigDecimal,時間為Double。 因此,我可能會保留字符串解析器,但還要編寫工廠方法,該方法也可以原生使用BigDecimal / Double值。
似乎時間字段實際上是從一天開始算起的毫秒數(不是秒),“。0”可以忽略。 因此,必須執行如下轉換和計算:
localDate.atTime(LocalTime.ofNanoOfDay(Long.parseLong(jdeTime)* 1000000))
實際上,根據Oracle.com頁面上的以下簡單描述, JD Edwards日期的細節並不是那么復雜 :
關於儒略日期格式
JD Edwards World文件中的日期字段以Julian格式存儲。 …
朱利安(* JUL)日期格式為CYYDDD,其中:
將C加到19以創建世紀,即0 + 19 = 19、1 + 19 =20。YY是世紀內的年份,DDD是年份中的一天。
條款:
C
部分稱為“世紀偏移”,即19
加幾百個世紀。 使用0
表示19xx
年,使用1
表示20xx
年。 DDD
稱為“ DayOfYear”,而“原始日期”是另一個術語。 使用“ Julian”表示一年內的天數是常見的做法,但不正確,這與Julian Day有所沖突。 java.time框架不直接支持解析或生成這種格式的字符串,我找不到。
JulianFields
有java.time.temporal.JulianFields
但這些是用於重新定義版本的儒略歷的 ,其中我們計算了從某個時期(1970-01-01(ISO),而不是歷史悠久的公元前4714年11月24日, Gregorian)),而完全忽略了歲月。 因此,這與JD Edwards定義無關,這與“問題”中鏈接的該頁面上的一些錯誤建議相反。
此JD Edwards日期是一個有序日期的版本。 有時將序數日期偶然地(並且錯誤地)稱為“朱利安”日期,這是因為它具有計算天數序列的想法。 但是,序數日期是指從年初到年底的天數,始終介於1到365/366(le年)之間,自某個時期以來就不算在內,並增長到成千上萬。
返回問題,在java.time中處理JD Edwards日期…
不,我沒有發現直接或間接支持java.time中內置的JD Edwards日期。
java.date.format包似乎沒有意識到日期的世紀,只有年份和時代。 因此,我找不到定義JD Edwards日期的C
部分的方法。
JD Edwards日期的最后一部分(一年中的天數)在日期時間類和格式類中均得到很好的處理。
LocalDate
由於JD Edwards日期顯然具有與java.time使用的ISO時序相同的邏輯,因此眼前唯一真正的問題是根據此特定格式解析和生成String對象。 可以從LocalDate
利用所有其他行為。
由於我找不到為此目的定義java.time.format.DateTimeFormatter
,因此建議編寫一個實用程序類來處理這些瑣事。
理想情況下,我們將擴展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的每日時間格式,我進行了搜索,但找不到任何文檔。 如果您知道某些內容,請編輯問題以添加鏈接。 關於JDE時間的唯一提及似乎是從午夜開始算起的幾秒鍾。
如果java.time.LocalTime
這樣(從午夜開始算),您就可以使用java.time.LocalTime
類。 可以實例化LocalTime
並將其讀取為:
withSecond
整秒( withSecond
, ofSecondOfDay
) withNano
,以納秒為單位( withNano
, ofNanoOfDay
) 納秒分辨率表示最多九位數的十進制小數。 處理您提到的六位數沒有問題。 只需做一下數學運算,即可將1_000L
乘以/除以。 請注意,這意味着可能會丟失數據,因為如果LocalTime
值來自JD Edwards數據之外, LocalTime
您可能會截斷小數點后三位數字(十進制小數點的第LocalTime
位)。 [FYI,舊的java.util.Date/.Calendar類以及Joda-Time的毫秒分辨率僅限於三位數的小數。
不推薦:您可以執行某種組合類,由LocalDate
和LocalTime
。 或使用LocalDateTime
。 關鍵問題是時區。 如果JD Edwards的日期時間始終處於某個時區(例如UTC),那么合並並使用OffsetDateTime
可能是OffsetDateTime
。 但是,如果沒有特定的時區上下文,則這些值只是日期時間的模糊概念,而不是時間軸上的特定點,請使用LocalDateTime
因為它沒有時區。 如果JDE始終處於UTC中, OffsetDateTime
設置為ZoneOffset.UTC
。 如果要指定時區(偏移量以及用於處理異常的規則,例如DST ),請使用ZonedDateTime
。
推薦:單獨使用LocalTime。 我認為您不希望在業務邏輯中使用我的JDEdwardsLocalDate類,尤其是因為它不是適合java.time框架的完整實現。 我的意圖是在遇到JDE日期時使用該類立即將其轉換為LocalDate
。 對於JDE時間,也是如此,立即轉換為LocalTime
。 如果他們的上下文始終是UTC, OffsetDateTime
使用UTC創建OffsetDateTime
,然后將其傳遞到您的業務邏輯中。 僅在必要時返回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對時區的意圖的任何信息。 因此,我無法提出更具體的建議。
否:Joda Time和Java 8都不支持JD Edwards時間表示。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.