Java 转换 LDAP 日期

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9806329/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 07:51:35  来源:igfitidea点击:

Converting a ldap date

javaldap

提问by Udo Held

I'm exporting users from an ldap programmatically. Therefor I'm retrieving the users from ldap. One of the attributes is whenCreated.

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

One of the values I have to convert is: 20090813145607.0ZDirectly splitting it up I get the following format: yyyyMMddHHmmss+.0Z. The problem is that the application is running in CET timezone and the time stored is UTC which is probably indicated by the .0Z. It is 14:56 UTCand the local representation is 16:56. For summer time it seems to be 2 hours and for winter time 1 hour.

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

I checked the SimpleDateFormatand there is a placeholder for the timezone, however its a different format.

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

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

Will show the wrong date as it ignores the dates time zone.

将显示错误的日期,因为它会忽略日期时区。

Is there a way to convert it directly?

有没有办法直接转换?

采纳答案by Basil Bourque

ISO 8601

ISO 8601

As a couple of other Answers mentioned, the date-time format in question is defined by RFC 4517 Lightweight Directory Access Protocol (LDAP): Syntaxes and Matching Rules. See section 3.3.13, Generalized Time.

正如提到的其他几个答案,有问题的日期时间格式由RFC 4517轻型目录访问协议 (LDAP): Syntaxes and Matching Rules 定义。参见第 3.3.13 节,广义时间

That section explains this LDAP format is a restricted version of one of the date-time formats defined by ISO 8601. This style using a minimum of separators is known as “basic” in ISO 8601.

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

In these formats, the Zon the end is short for Zuluand means UTC(basically same as GMT).

在这些格式中,Z末尾是UTC 的缩写Zulu,表示UTC(与 GMT 基本相同)。

The decimal point and digit at the end represents a fraction of a second. Note that a comma is possible instead of the dot (period) in both RFC 4517 and ISO 8601. The comma is actually recommended over the dot in ISO 8601. The RFC 4517 spec allows for only a single digit fraction (some tenths of a fraction) or no dot/comma & digit at all. Note that in contrast: (a) ISO 8601 allows for any number of fractional digits, and (b) java.time objects have nanosecond resolution for up to nine digits of fractional second.

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

java.time

时间

The java.timeframework is built into Java 8 and later. These classes supplant the old troublesome date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.

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

Now in maintenance mode, the Joda-Timeproject also advises migration to java.time.

现在处于维护模式Joda-Time项目还建议迁移到 java.time。

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.

要了解更多信息,请参阅Oracle 教程。并在 Stack Overflow 上搜索许多示例和解释。

Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backportand further adapted to Android in ThreeTenABP.

多的java.time功能后移植到Java 6和7在ThreeTen-反向移植和在进一步适于到Android ThreeTenABP

The ThreeTen-Extraproject extends java.time with additional classes. This project is a proving ground for possible future additions to java.time.

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

Parsing

解析

Define a formatting pattern to fit RFC 4517. Study the DateTimeFormatterclass for the pattern coding. This should work: uuuuMMddHHmmss[,S][.S]X. The square brackets mean optional. We accommodate either a dot or comma. Note the singular digit for fraction of second. The Xon the end allows for either a Zor an offset-from-UTCsuch as -08 or -0830 or -08:30 or -083015 or -08:30:15.

定义格式模式以适应 RFC 4517。研究DateTimeFormatter模式编码的类。这应该工作:uuuuMMddHHmmss[,S][.S]X。方括号表示optional。我们容纳一个点或逗号。注意秒的小数部分的单数。该X对端允许为任何一个Z或一个偏移从-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 ();

Dump to console.

转储到控制台。

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

input: 20090813145607.0Z | odt: 2009-08-13T14:56:07Z | instant: 2009-08-13T14:56:07Z

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

Of course you should also be coding a check for java.time.format.DateTimeParseExceptionin case of unexpected input.

当然,您还应该编写检查代码java.time.format.DateTimeParseException,以防出现意外输入。

回答by highlycaffeinated

What about using the split you described above, then reformatting the 0Ztimezone into a standard format, then using sdf.parse(...)? Maybe something like this (with appropriate error checking added, of course):

如何使用您上面描述的拆分,然后将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);

回答by Terry Gardner

The syntax of the attribute is described in the directory schema. Applications must use the schema when converting, comparing, and ordering data that was retrieved from or stored in the directory. If the syntax of the whenCreatedattribute is generalizedTime, then applications must use libraries for generalized time when converting. The syntax for generalizedTime is described in RFC4517.

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

回答by Udo Held

Checking the RFC mentioned above it seems like using UTC is the recommended default behavior for ldap dates. Therefor I converted it directly:

检查上面提到的 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;
}

回答by pastafarian

I tried to use the apache util GeneralizedTime class http://directory.apache.org/api/gen-docs/1.0.0-M11/apidocs/org/apache/directory/shared/util/GeneralizedTime.htmlwith mixed results

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

to convert from current time to Active Direcotry format:

从当前时间转换为 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");

The only problem is that it does not work as advertised. The length of the fraction portion after the dot is supposed to be "1" according to this call but the result still comes out as 3. Instead of "20120410011958.6Z" I get "20120410011958.687Z" so I still have to get the time in seconds and insert ".0" before the Z. So here's what you have to do (in my case I don't care about the fraction so I put zero. AD cares)

唯一的问题是它不像宣传的那样工作。根据这个调用,点之后的小数部分的长度应该是“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");

Incidentally this code converts from AD GeneralizedTime string format to Java Date

顺便说一句,这段代码从 AD GeneralizedTime 字符串格式转换为 Java 日期

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

回答by iberck

You can use the methods of org.apache.directory.shared.ldap.util.DateUtils:

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

String ldapDate="20090813145607.0Z";

Date date = DateUtils.parse(ldapDate);

String generalizedTime = DateUtils.getGeneralizedTime(date);

String ldapDate="20090813145607.0Z";

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

String generalizedTime = DateUtils.getGeneralizedTime(date);

回答by kylexy1357

This is the onlypiece of code that worked for me :

这是唯一对我有用的代码:

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;
        }