2012-03-12 74 views
1

我试图转换JDE dates,并且已经积累了大量信息,并想通过尝试执行SQL转换函数来简化某些任务。将JDE Julian日期转换为Gregorian

这是我想到了,我只需拨打“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. 采用“朱利安”字符串的函数。
  2. 采取第一个字母,并将其添加到世纪,从19日开始。
  3. 从接下来的2个字符中增加十年和几年。
  4. 最后添加天数,这是最后3个字符,并减去1,因为它已经在第一个设置中有1天。 (如:2011-01-01)
  5. 结果前:111186 =>2011-07-05 00:00:00.000

在我看来,这是一个有点笨拙和矫枉过正,而且我希望有这样做的更好的方法。也许我做了太多的转换,或者我应该使用不同的方法?

有什么建议如何改进功能?
也许是另一种更好的方法?
不介意它是否可读性更好......

我也有一个内联版本,如果例如我只有阅读权限并且不能使用函数,凌乱,是否有可能使其更具可读性,或更好?

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) 
+0

如果日期是在20世纪,是主角字符0还是字符串5位数字? – 2012-03-12 17:10:06

+0

@AaronBertrand这将是5位数,但我所见到的表中没有一个日期低于2000年,因为我公司使用的系统是在2003年左右建立的。它不应该是一个问题,但它提出了一个问题,如果我不得不考虑这个问题,查询将会有什么不同? – ShadowScripter 2012-03-12 17:21:02

+0

你应该使用'RIGHT('0'+ column,6)'是安全的,或者添加一个约束来检查'LEN(column)= 6'和/或'LEFT(column,1)='1'' 。 – 2012-03-12 17:30:55

回答

3

我认为使用本地日期时间数学比使用各种字符串,日期和数字格式来回切换效率更高效。

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

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

结果:

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

假设这个数据不经常更改,也可能是更有效的实际存储的时间作为计算列(这就是为什么我选择的基准日0而不是某些字符串表示形式,这会导致确定性问题,阻止列被持久化并可能编入索引)。

CREATE TABLE dbo.JDEDates 
(
    JDEDate VARCHAR(6), 

    GregorianDate AS CONVERT(SMALLDATETIME, 
     DATEADD(YEAR, 
     100*CONVERT(INT, LEFT(RIGHT('0'+JDEDate,6),1)) 
     +10*CONVERT(INT, SUBSTRING(RIGHT('0'+JDEDate,6), 2,1)) 
     +CONVERT(INT, SUBSTRING(RIGHT('0'+JDEDate,6),3,1)), 
     DATEADD(DAY, CONVERT(INT, RIGHT(JDEDate, 3))-1, 
     0)) 
    ) PERSISTED 
); 

INSERT dbo.JDEDates(JDEDate) SELECT '111186'; 

SELECT JDEDate, GregorianDate FROM dbo.JDEDates; 

结果:

JDEDate GregorianDate 
======= =================== 
111186 2011-07-05 00:00:00 

即使你不索引列,它仍然隐藏丑陋的计算离你而去,而你坚持只支付,在写的时候,因为它不”牛逼导致每当列引用您在查询时进行昂贵的功能操作...

+0

不幸的是,我只有在官方工作服务器上读取权限。我有一个服务器,我是管理员,但我直接使用格雷戈里来代替。你的解决方案看起来很优雅,你能否带我走过你正在做的事情,为什么? :P – ShadowScripter 2012-03-12 17:36:17

+0

我完全按照你所描述的公式描述,但顺序相反。从右到左工作,我将基准日期(0 = 1900-01-01)加上天数(JDE的最后3位数)减1,然后加上年数(第三位数)+数十年(这是第二位数字的10倍+数百年(这是第一位数字) – 2012-03-12 17:40:33

+0

PS仅仅因为您没有在服务器上写入权限并不意味着您不能在那里实现某些功能。*你写的任何*到服务器的方式如何? – 2012-03-12 17:42:56

0
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 
1

DATE(CHAR(190万+ GLDGJ)),其中GLDGJ是Julian日期值

+1

首先,欢迎来到社区:)有一个对这个问题已经有了几个高质量的答案,其中大部分答案都是三年前发布时提出的问题。虽然尝试回答诸如此类的简单问题可能是一种有价值的练习,但为了提高编程能力,在当前状态下发布此答案并不会增加任何问题。如果您的答案中有些新颖的话,请花几个句子来解释它的不同之处,以及为什么会使它更好。另外,请解释你是如何达到这个魔术常数的。 – MTCoster 2015-12-15 15:33:47

2

接受的答案不正确。它将无法给出2016年2月29日116060的正确答案,而是返回2016年3月1日。

JDE似乎存储日期为整数,因此而不是从字符串转换我总是直接从整数

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

要从VARCHAR去(6)我使用:

DATEADD(DAY, CAST(RIGHT(@Julian,3) AS int), DATEADD(YEAR, CAST(LEFT(@Julian,LEN(@Julian)-3) AS int), '31-dec-1899')) 
+0

我也许应该说,接受答案不正确的原因是因为它将日期添加到基年,然后增加年份。因为一年的正确日期取决于哪一年(不管它是不是闰年);你必须首先添加年份,然后是日期。 – 2016-02-15 14:00:18

相关问题