转换ldap日期

时间:2012-03-21 14:11:50

标签: java ldap

我正在以编程方式从ldap导出用户。因此我正在从ldap中检索用户。其中一个属性是whenCreated

我必须转换的其中一个值是:20090813145607.0Z直接将其拆分我得到以下格式:yyyyMMddHHmmss + .0Z。问题是应用程序在CET时区运行,存储的时间是UTC,可能由.0Z表示。它是14:56 UTC,本地代表是16:56。夏季时间似乎是2小时,冬季则是1小时。

我检查了SimpleDateFormat并且时区有一个占位符,但格式不同。

SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
sdf.parse("20090813145607.0Z");

将忽略日期时区,显示错误的日期。

有没有办法直接转换它?

7 个答案:

答案 0 :(得分:12)

ISO 8601

正如其他几个答案所提到的,有问题的日期时间格式由RFC 4517 Lightweight Directory Access Protocol (LDAP): Syntaxes and Matching Rules定义。请参见第3.3.13节,广义时间

该部分解释了此LDAP格式是ISO 8601定义的日期时间格式之一的受限版本。使用最少分隔符的这种样式在ISO 8601中称为“基本”。

在这些格式中,结尾的ZZulu的缩写,表示UTC(与GMT基本相同)。

末尾的小数点和数字代表一小部分秒。请注意,在RFC 4517和ISO 8601中都可以使用逗号代替点(句点)。实际上,建议使用逗号来表示ISO 8601中的点.RFC 4517规范仅允许单个数字部分(十分之一的十分之一) )或没有点/逗号&数字。请注意,相比之下:(a)ISO 8601允许任意数量的小数位数,(b)java.time对象具有纳秒分辨率,最多可达到小数秒的九位数。

java.time

java.time框架内置于Java 8及更高版本中。这些类取代了旧的麻烦日期时间类,例如java.util.Date.Calendar和& java.text.SimpleDateFormat

现在在maintenance mode中,Joda-Time项目还建议迁移到java.time。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。

大部分java.time功能都被反向移植到Java 6& ThreeTen-Backport中的7,并在ThreeTenABP中进一步适应Android。

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。

解析

定义符合RFC 4517的格式模式。研究模式编码的DateTimeFormatter类。这应该有效:uuuuMMddHHmmss[,S][.S]X。方括号表示可选。我们可以使用点或逗号。注意单位数的秒数。最后X允许Zoffset-from-UTC,例如-08或-0830或-08:30或-083015或-08:30:15。

String input = "20090813145607.0Z";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "uuuuMMddHHmmss[,S][.S]X" );
OffsetDateTime odt = OffsetDateTime.parse ( input , f );
Instant instant = odt.toInstant ();

转储到控制台。

System.out.println ( "input: " + input + " | odt: " + odt + " | instant: " + instant );
  

输入:20090813145607.0Z | odt:2009-08-13T14:56:07Z |时间:2009-08-13T14:56:07Z

当然,如果发生意外输入,您还应该为java.time.format.DateTimeParseException编码检查。

答案 1 :(得分:6)

如何使用上述分割,然后将0Z时区重新格式化为标准格式,然后使用sdf.parse(...)?也许是这样的(当然还添加了适当的错误检查):

String[] parts = inputDateTime.split("[.]");
String dateTimePart = parts[0];
String timeZonePart = "+0" + parts[1].substring(0, parts[1].length() - 1) + "00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssZ");
Date theDate = sdf.parse(dateTimePart + timeZonePart);

答案 2 :(得分:6)

检查上面提到的RFC似乎使用UTC是ldap日期的推荐默认行为。因此我直接转换了它:

public Date parseLdapDate(String ldapDate){
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
    sdf.setTimeZone(TimeZone.getTimeZone("GMT"));

    try {
        return sdf.parse(ldapDate);
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return null;
}

答案 3 :(得分:2)

目录模式中描述了该属性的语法。在转换,比较和排序从目录中检索或存储的数据时,应用程序必须使用模式。如果whenCreated属性的语法是generalizedTime,那么应用程序必须在转换时使用库作为通用时间。 generalizedTime的语法在RFC4517中描述。

答案 4 :(得分:2)

您可以使用org.apache.directory.shared.ldap.util.DateUtils的方法:

  

String ldapDate =" 20090813145607.0Z";

     

日期日期= DateUtils.parse(ldapDate);

     

String generalizedTime = DateUtils.getGeneralizedTime(date);

答案 5 :(得分:1)

这是适合我的唯一代码:

static String parseLdapDate(String ldapDate) {

            long nanoseconds = Long.parseLong(ldapDate);   // nanoseconds since target time that you want to convert to java.util.Date

            long mills = (nanoseconds / 10000000);

            long unix = (((1970 - 1601) * 365) - 3 + Math.round((1970 - 1601) / 4)) * 86400L;

            long timeStamp = mills - unix;

            Date date = new Date(timeStamp * 1000L); // *1000 is to convert seconds to milliseconds
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); // the format of your date
    //sdf.setTimeZone(TimeZone.getTimeZone("GMT")); // give a timezone reference for formating (see comment at the bottom
            String formattedDate = sdf.format(date);

            return  formattedDate;
        }

答案 6 :(得分:0)

我尝试使用带有混合结果的apache util GeneralizedTime类http://directory.apache.org/api/gen-docs/1.0.0-M11/apidocs/org/apache/directory/shared/util/GeneralizedTime.html

将当前时间转换为Active Direcotry格式:

GeneralizedTime gt = new GeneralizedTime(Calendar.getInstance());
String gtADString = gt.toGeneralizedTime(GeneralizedTime.Format.YEAR_MONTH_DAY_HOUR_MIN_SEC_FRACTION, GeneralizedTime.FractionDelimiter.DOT, 1, GeneralizedTime.TimeZoneFormat.Z).replaceFirst("Z", "\\.0Z");

唯一的问题是它不像宣传的那样有效。根据这个调用,点之后的小数部分的长度应该是“1”,但结果仍然是3.而不是“20120410011958.6Z”我得到“20120410011958.687Z”所以我仍然需要时间秒,并在Z之前插入“.0”。所以这就是你要做的事情(在我的情况下,我不关心分数,所以我把零.AD关心)

GeneralizedTime gt = new GeneralizedTime(Calendar.getInstance());
String gtADString = gt.toGeneralizedTime(GeneralizedTime.Format.YEAR_MONTH_DAY_HOUR_MIN_SEC, GeneralizedTime.FractionDelimiter.DOT, 1, GeneralizedTime.TimeZoneFormat.Z).replaceFirst("Z", "\\.0Z");

顺便说一句,此代码从AD GeneralizedTime字符串格式转换为Java Date

 GeneralizedTime gt = new GeneralizedTime(str);
 Date d = gt.getCalendar().getTime();