4

I'm trying to convert JDE dates, and have amassed a large quantity of information and figured I'd try to do an SQL conversion function to simplify some tasks.

Here's the function I came up with, which I simply call "ToGregorian"

CREATE FUNCTION [dbo].[ToGregorian](@julian varchar(6))
RETURNS datetime AS BEGIN
    DECLARE @datetime datetime

    SET @datetime = CAST(19+CAST(SUBSTRING(@julian, 1, 1) as int) as varchar(4))+SUBSTRING(@julian, 2,2)+'-01-01'
    SET @datetime = DATEADD(day, CAST(SUBSTRING(@julian, 4,3) as int)-1, @datetime)

    RETURN @datetime
END
  1. Takes a "julian" string.
  2. Takes the first letter and adds it to century, starting from 19th.
  3. Adds decade and years from the next 2 characters.
  4. Finally adds the days, which are the final 3 characters, and subtracts 1 as it already had 1 day in the first setup. (eg. 2011-01-01)
  5. Result ex: 111186 => 2011-07-05 00:00:00.000

In my opinion this is a bit clumsy and overkill, and I'm hoping there is a better way of doing this. Perhaps I'm doing too many conversions or maybe I should use a different method alltogether?

Any advice how to improve the function?
Perhaps a different, better, method?
Wouldn't mind if it could be more readable as well...

I've also got an inline version, where if for instance, I only have read privileges and can't use functions, which also looks messy, is it possible to make it more readable, or better?

CAST(REPLACE(Convert(VARCHAR, DATEADD(d,CAST(SUBSTRING(CAST([column] AS VARCHAR), 4,3) AS INT)-1, CAST(CAST(19+CAST(SUBSTRING(CAST([column] AS VARCHAR), 1,1) AS INT) AS VARCHAR)+SUBSTRING(CAST([column] AS VARCHAR), 2,2) + '-01-01' AS DATETIME)), 111), '/', '-') AS DATETIME)
ShadowScripter
  • 7,314
  • 4
  • 36
  • 54
  • If the date is in the 1900s, is the leading character 0, or is the string 5 digits? – Aaron Bertrand Mar 12 '12 at 17:10
  • @AaronBertrand It would be 5 digits, but none of the tables I've seen have had a date below the 2000s since the system my company uses was established around 2003-4. It shouldn't be a problem, but it raises the question, how would the query look differently if I'd have to take that into account? – ShadowScripter Mar 12 '12 at 17:21
  • You should use `RIGHT('0' + column, 6)` to be safe, or add a constraint to check that `LEN(column) = 6` and/or `LEFT(column,1) = '1'`. – Aaron Bertrand Mar 12 '12 at 17:30

7 Answers7

9

The accepted answer is incorrect. It will fail to give the correct answer for 116060 which should be 29th February 2016. Instead it returns 1st March 2016.

JDE seems to store dates as integers, so rather than converting from strings I always go direct from the integer:

DATEADD(DAY, @Julian % 1000, DATEADD(YEAR, @Julian / 1000, '31-dec-1899'))

To go from a varchar(6) I use:

DATEADD(DAY, CAST(RIGHT(@Julian,3) AS int), DATEADD(YEAR, CAST(LEFT(@Julian,LEN(@Julian)-3) AS int), '31-dec-1899'))
  • I should perhaps have said that the reason the accepted answer is incorrect is because it adds the days to a base year, then adds the years. Because the correct day of the year depends on which year it is (whether or not it's a leap-year); you MUST add the years on first, then the days. – David O'Rourke Feb 15 '16 at 14:00
  • Nicely done and +1. And I wouldn't even bother with the explicit conversions to INT for the VARCHAR(6) ( or CHAR(6) ) example. I also use the underlying "Date Serial Number" for such things. With that and with the understanding that I wouldn't do it this way for anything prior to 1900, here's the code I'd use if I had to do such a conversion on a regular basis. Except for the -1 to represent '12 Dec 1899', it's identical to yours. – Jeff Moden Dec 16 '22 at 19:22
3

I think it is more efficient to use native datetime math than all this switching back and forth to various string, date, and numeric formats.

DECLARE @julian VARCHAR(6) = '111186';

SELECT DATEADD(DAY, SUBSTRING(@julian,4,3)-1,
  DATEADD(YEAR, 100 * LEFT(@julian,1) 
          + 10 * SUBSTRING(@julian,2,1)
               + SUBSTRING(@julian,3,1),0));

Result:

===================
2011-07-05 00:00:00

Assuming this data doesn't change often, it may be much more efficient to actually store the date as a computed column (which is why I chose the base date of 0 instead of some string representation, which would cause determinism issues preventing the column from being persisted and potentially indexed). Even if you don't index the column, it still hides the ugly calculation away from you, being persisted you only pay that at write time, as it doesn't cause you to perform expensive functional operations at query time whenever that column is referenced...

  • Corrected for leap year
Aaron Bertrand
  • 272,866
  • 37
  • 466
  • 490
  • Sadly, I only have read permissions on the official work server. I've got a server where I'm admin, but I straight up use gregorian instead. Your solution seems elegant, could you perhaps walk me through what you're doing and why? :P – ShadowScripter Mar 12 '12 at 17:36
  • 1
    I followed exactly as your formula described, but in the opposite order. Working from right to left, I take the base date (0 = 1900-01-01) and add the number of days (the last 3 digits of JDE) less 1, then add the number of years (the 3rd digit) + the number of decades (which is 10 times the 2nd digit + the number of centuries (which is the first digit). – Aaron Bertrand Mar 12 '12 at 17:40
  • PS just because you don't have write permissions on the server doesn't mean you can't get something implemented there. How does *anything* you write get to the server? – Aaron Bertrand Mar 12 '12 at 17:42
  • The official work server uses an application interface, whereas mine uses a web interface. Where the data is _extracted_ once every 2 weeks from the other server. It's to lighten the workload from the already very busy server. The web interface has lots of users and if I were to query the other server, it would be tremendously slow, especially during work hours. So, nothing is written to that server, only read. – ShadowScripter Mar 12 '12 at 17:52
-1
USE [master]
GO
/****** Object:  UserDefinedFunction [dbo].[ToGregorian]    Script Date: 08/18/2015 14:33:17 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[ToGregorian](@julian varchar(6),@time varchar(6))
RETURNS datetime 
AS 
BEGIN
    DECLARE @datetime datetime,@hour int, @minute int, @second int

    set @time = ltrim(rtrim(@time));
    set @julian = ltrim(rtrim(@julian));

    if(LEN(@julian) = 5)
        set @julian = '0' + @julian


    IF(LEN(@time) = 6)
        BEGIN
            SET @hour = Convert(int,LEFT(@time,2));
            SET @minute = CONVERT(int,Substring(@time,3,2));
            SET @second = CONVERT(int,Substring(@time,5,2));
        END
    else IF(LEN(@time) = 5)
        BEGIN
            SET @hour = Convert(int,LEFT(@time,1));
            SET @minute = CONVERT(int,Substring(@time,2,2));
            SET @second = CONVERT(int,Substring(@time,4,2));
        END
    else IF(LEN(@time) = 4)
        BEGIN
            SET @hour = 0;
            SET @minute = CONVERT(int,LEFT(@time,2));
            SET @second = CONVERT(int,Substring(@time,3,2));
        END
    else IF(LEN(@time) = 3)
        BEGIN
            SET @hour = 0;
            SET @minute = CONVERT(int,LEFT(@time,1));
            SET @second = CONVERT(int,Substring(@time,2,2));
        END
    else
        BEGIN
            SET @hour = 0;
            SET @minute = 0;
            SET @second = @time;
        END

    SET @datetime = DATEADD(YEAR,100*CONVERT(INT, LEFT(@julian,1))+10*CONVERT(INT, SUBSTRING(@julian, 2,1))+CONVERT(INT, SUBSTRING(@julian,3,1)),0);                     
    SET @datetime = DATEADD(DAY, CONVERT(INT,SUBSTRING(@julian, 4, 3))-1,@datetime);                   
    SET @datetime = DATEADD(hour,@hour,@datetime)
    SET @datetime = DATEADD(minute,@minute,@datetime);
    SET @datetime = DATEADD(second,@second,@datetime);

    RETURN @datetime
END
Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
-1

DATE(CHAR(1900000 + GLDGJ)) where GLDGJ is the Julian date value

  • 1
    First off, welcome to the community :) There are already several high quality answers to this question, most of which were posted three years ago when the question was asked. While it may be a worthwhile exercise to attempt to answer simple questions such as this one in order to further your programming abilities, posting this answer in its current state does not add anything to the question. If there is something novel about your answer, please take a couple sentences to explain how it's different and why that makes it better. Also, please explain how you arrived at that magic constant. – MTCoster Dec 15 '15 at 15:33
  • It also doesn't actually work. – Jeff Moden Dec 16 '22 at 19:27
-1

I don't think anyone has mentioned it, but JDE has a table just for this.

It's the F00365 data table. As far as I know, it's a translation table just for this issue.

To get a Gregorian date, you join the F00365 table using the ONDTEJ field (which is the Julian date),and you return the ONDATE value, which is Gregorian. e.g.

SELECT 
    DateReq.ONDATE 
FROM F00101 NamesData 
INNER JOIN F00365 DateReq 
    ON DateReq.ONDTEJ = NamesData.ABUPMJ

No math required. No weird issues with leap years.

  • Some research shows that this table doesn't exist for all versions/flavors of JDE, especially recent ones. Can confirm it's not present in EnterpriseOne. – Luke Feb 12 '21 at 13:38
-1

To convert Julian date to Gregorian:

DATEADD(DAY, @julian % 1000 - 1, DATEADD(YEAR, @julian / 1000, 0))

To convert Gregorian to Julian date:

(YEAR(@date) - 1900) * 1000 + DATEPART(DAYOFYEAR, @date)

Try it:

DECLARE @julian VARCHAR(6);
SET @julian = N'122129';
SELECT @julian [JulianDate],
       DATEADD(YEAR, @julian / 1000, 0) [Year],
       @julian % 1000 [DayOfYear],
       DATEADD(DAY, @julian % 1000 - 1, DATEADD(YEAR, @julian / 1000, 0)) [Date];

DECLARE @george DATETIME;
SET @george = '2022-5-9';
SELECT @george [Date],
       YEAR(@george) [Year],
       DATEPART(DAYOFYEAR, @george) [DayOfYear],
       (YEAR(@george) - 1900) * 1000 + DATEPART(DAYOFYEAR, @george) [JulianDate];

enter image description here

walterhuang
  • 574
  • 13
  • 24
-1

Here a formula that can be used I have looking all over different site and I had even though I had to twick a bit has work well for me and you do not need to create any special function stuff:

DATEADD(DAY,CONVERT(INT,SUBSTRING(CONVERT(CHAR,(JULIANDDATEFIELD + 1900000)),5,3))-1,DATEFROMPARTS(SUBSTRING(CONVERT(CHAR,(JULIANDDATEFIELD + 1900000)),1,4),01,01))

  • Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. – Simas Joneliunas Dec 20 '22 at 01:26