簡體   English   中英

JodaTime或Java 8是否特別支持JD Edwards日期和時間?

[英]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))

JD Edwards日期已定義

實際上,根據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年。
  • java.time框架將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類,覆蓋其parsetoString方法。 也許是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這樣(從午夜開始算),您就可以使用java.time.LocalTime類。 可以實例化LocalTime並將其讀取為:

納秒分辨率表示最多位數的十進制小數。 處理您提到的六位數沒有問題。 只需做一下數學運算,即可將1_000L乘以/除以。 請注意,這意味着可能會丟失數據,因為如果LocalTime值來自JD Edwards數據之外, LocalTime您可能會截斷小數點后三位數字(十進制小數點的第LocalTime位)。 [FYI,舊的java.util.Date/.Calendar類以及Joda-Time的毫秒分辨率僅限於三位數的小數。

不推薦:您可以執行某種組合類,由LocalDateLocalTime 或使用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 );

時區在這里至關重要。 您必須大致了解這些概念。 請注意, LocalDateLocalTimeLocalDateTime 不在時間軸上。 在您將它們調整到一個時區(或至少從UTC偏移 )之前, 它們沒有特定的含義

如果不熟悉java.time類型, 此答案中包含的我的日期時間類型圖表可能會對您有所幫助。

並且您必須了解JDE日期和時間的含義及其在您的應用程序/數據庫中的用法。 因為我找不到有關JDE時間的任何信息,所以也無法了解有關JD Edwards對時區的意圖的任何信息。 因此,我無法提出更具體的建議。

否:Joda Time和Java 8都不支持JD Edwards時間表示。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM