5

当面のトピックは、JD Edwards と呼ばれる Oracle の ERP ソフトウェアで日付を操作する際の厄介なドメイン固有の問題です。詳細はこの質問に記載されています。

JD Edwards から日付と時刻を処理するためのラッパー クラスを作成する前に、JodaTime または Java 8 がこの固有の時刻形式の特別なサポートを導入したかどうか、または使用するライブラリに関係なく重要な文字列操作を行う必要があるかどうかを知りたいです。 .

これはあいまいな問題であるため、この問題や JodaTime/Java 8/JSR 310 について具体的な知識がある場合にのみ回答してください。

追加: Basil Bourque の要求に従って、上記の日付に付随するタイムスタンプの例を追加します。異なるテーブルの日付/時刻フィールドの 2 つの例を次に示します。

JCSBMDATE:115100、JCSBMTIME:120102.0

RLUPMJ:114317、RLUPMT:141805.0

また、日付変数は BigDecimal としてキャストされており、時刻は Double です。そのため、おそらく文字列パーサーは残しておきますが、BigDecimal/Double 値をネイティブに受け取るファクトリ メソッドも作成します。

時間フィールドは、実際には 1 日の始まりからのミリ秒数 (秒数ではない) のようで、「.0」は無視できます。したがって、次のように変換と計算を実行する必要があります。

localDate.atTime(LocalTime.ofNanoOfDay(Long.parseLong(jdeTime) * 1000000))

4

2 に答える 2

8

JD エドワーズ日付定義

実際、Oracle.com のページにある次の簡単な説明によると、 JD Edwardsの日付の詳細はそれほど悲惨ではありません。

ユリウス日形式について

JD Edwards World ファイルの日付フィールドは、ユリウス形式で保存されます。…</p>

ユリウス (*JUL) 日付形式は CYYDDD です。

C を 19 に追加して世紀を作成します。つまり、0 + 19 = 19、1 + 19 = 20 です。YY は世紀内の年、DDD は年内の日です。

条項:

  • 私はその部分を「世紀オフセット」と呼びます.C何世紀を追加するか19. 019xx年も何年も1使います20xx
  • java.time フレームワークはこれDDDを「DayOfYear」と呼びますが、「序日」は別の用語です。年内の日数に「ユリウス」を使用するのは一般的ですが、ユリウス日と矛盾して正しくありません。

java.time フレームワークには、この形式の文字列を解析または生成するための直接的なサポートが含まれていません。

JulianFields

ありますが、これらはユリウス日付java.time.temporal.JulianFieldsの再定義されたバージョン用であり、エポック (歴史的な紀元前 4714 年 11 月 24 日 (先発グレゴリオ暦) ではなく 1970-01-01 (ISO)) からの日数を数えますが、年は無視します。完全に。したがって、これは JD Edwards の定義とは何の関係もありません。質問にリンクされているページの誤ったアドバイスとは対照的です。

序数日

この JD Edwards の日付は、序数の日付のバージョンです。序数の日付は、何気なく (そして誤って) 「ユリウス」日付と呼ばれることがあります。これは、一連の日数を数えるという考え方を共有しているからです。しかし、序数の日付は、年の初めから年末までの日数を常に 1 ~ 365/366 (閏年) の間でカウントし、あるエポック以降はカウントせず、数千の数に成長します。

質問に戻り、java.time で JD Edwards の日付を処理します…</p>

いいえ、JD Edwards の日付が java.time に組み込まれていることを直接的または間接的にサポートしているとは思いません。

java.date.formatパッケージは、日付の世紀を認識しておらず、年と時代だけを認識しているようです。Cしたがって、JD Edwards の日付の一部を定義する方法はありません。

JD Edwards の日付の最後の部分 (1 年の序数) は、日時クラスとフォーマット クラスの両方で適切に処理されます。

包むLocalDate

JD Edwards の日付は明らかに java.time で使用される ISO 暦と同じロジックを持っているため、当面の唯一の実際の問題は、この特定の形式に従って String オブジェクトを解析および生成することです。他のすべての動作は、LocalDate.

java.time.format.DateTimeFormatterこの目的でを定義する方法が見つからないので、これらの雑用を処理するユーティリティ クラスを作成することをお勧めします。

理想的には、クラスを拡張して、そのメソッドとメソッドLocalDateをオーバーライドします。そしておそらく方法。ただし、クラスはマークされており、拡張できません。したがって、以下に示すこのクラスのようなものを作成し、.parsetoStringgetCenturyOffsetLocalDatefinalLocalDate

警告:自己責任で使用してください。新しいコード、ほとんど実行されず、ほとんどテストされていません。例としてのものであり、本番環境での使用を意図したものではありません。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 時間についての唯一の言及は、真夜中からの秒数のようでした。

その場合 (午前 0 時からのカウント)、java.time.LocalTimeクラスで対応できます。ALocalTimeはインスタンス化して、次のいずれかとして読み取ることができます。

ナノ秒の分解能とは、小数部の最大9桁を意味します。あなたが言及した6桁の処理に問題はありません。ただ計算をして、 を掛けたり割ったりして1_000Lください。LocalTime値が JD Edwards データの外部から取得された場合、小数部の最後の 3 桁 (小数部の 7 桁目、8 桁目、9 桁目) が切り捨てられる可能性があるため、データが失われる可能性があることに注意してください。[参考までに、古い java.util.Date/.Calendar クラスと Joda-Time は、小数点以下 3 桁のミリ秒単位の解像度に制限されています。]

非推奨:とで構成されたある種のコンボ クラスを実行できます。またはを使用します。重要な問題はタイムゾーンです。JD Edwards の日時が常に UTC などの特定のタイム ゾーンにある場合は、. ただし、特定のタイム ゾーン コンテキストがない場合、値がタイムライン上の特定のポイントではなく日付/時間のあいまいなアイデアである場合は、タイム ゾーンがないので使用します。JDE が常に UTC である場合は、set to を使用します。タイム ゾーン (オフセットDSTなどの異常を処理するためのルール) を指定する場合は、 を使用します。LocalDateLocalTimeLocalDateTimeOffsetDateTimeLocalDateTimeOffsetDateTimeZoneOffset.UTCZonedDateTime

推奨: LocalTime を個別に使用します。ビジネス ロジックで私の JDEdwardsLocalDate クラスを使用したいとは思わないでしょう。これは特に、java.time フレームワークに適合する完全な実装ではないためです。私の意図は、そのクラスを使用してLocalDate、JDE 日付に遭遇したときにすぐに変換することです。同じことが JDE 時刻にも当てはまり、LocalTimeすぐに変換します。コンテキストが常に UTC の場合は、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 の意図については何も知ることができませんでした。したがって、これ以上具体的に提案することはできません。

于 2016-05-10T06:46:17.330 に答える
2

いいえ: Joda Time も Java 8 も、JD Edwards 時間表現をサポートしていません。

于 2016-05-10T05:07:21.407 に答える