Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/377.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
比较Java日期并强制将日期设置为特定时区_Java_Date_Groovy_Datetime Parsing - Fatal编程技术网

比较Java日期并强制将日期设置为特定时区

比较Java日期并强制将日期设置为特定时区,java,date,groovy,datetime-parsing,Java,Date,Groovy,Datetime Parsing,我在进行日期比较时遇到困难 我正在使用Groovy和Spock编写针对web服务的集成测试 测试是首先使用web服务创建一个对象,然后立即进行另一次调用以通过其ID获取该对象的详细信息。然后,我想验证该对象的CreatedDate是否大于一分钟前 下面是调用的一些JSON { "Id":"696fbd5f-5a0c-4209-8b21-ea7f77e3e09d", "CreatedDate":"2017-07-11T10:53:52" } 因此,请注意日期字符串中没有时区信息;但我知道

我在进行日期比较时遇到困难

我正在使用Groovy和Spock编写针对web服务的集成测试

测试是首先使用web服务创建一个对象,然后立即进行另一次调用以通过其ID获取该对象的详细信息。然后,我想验证该对象的CreatedDate是否大于一分钟前

下面是调用的一些JSON

{
  "Id":"696fbd5f-5a0c-4209-8b21-ea7f77e3e09d",
  "CreatedDate":"2017-07-11T10:53:52"
}
因此,请注意日期字符串中没有时区信息;但我知道是UTC

我刚从.NET开始接触Java,对不同的日期类型有些迷惑

这是我的Groovy类模型,我使用Gson进行反序列化:

class Thing {
    public UUID Id
    public Date CreatedDate
}
反序列化工作得很好。但在非UTC时区运行的代码认为日期实际上在本地时区

我可以使用Instant类创建一个表示1分钟前的变量:

这就是我试图进行比较的方式:

rule.CreatedDate.toInstant().compareTo(aMinuteAgo) < 0
问题是,运行时认为日期是本地时间。似乎没有过载,我无法强制将.toInstant强制转换为UTC

我已经尝试使用我理解为更现代的类,例如模型中的LocalDateTime和ZonedDateTime,而不是Date,但是Gson在反序列化方面做得不好。

输入字符串只有日期和时间,没有时区信息,因此您可以将其解析为LocalDateTime,然后将其转换为UTC:

// parse date and time
LocalDateTime d = LocalDateTime.parse("2017-07-11T10:53:52");
// convert to UTC
ZonedDateTime z = d.atZone(ZoneOffset.UTC);
// or
OffsetDateTime odt = d.atOffset(ZoneOffset.UTC);
// convert to Instant
Instant instant = z.toInstant();
您可以使用ZonedDateTime、OffsetDateTime或Instant,因为它们都包含UTC中的等效日期和时间。要获取它们,可以使用反序列化程序

要检查此日期与当前日期之间的分钟数,可以使用java.time.temporal.ChronoUnit:

这将返回即时和当前日期/时间之间的分钟数。您还可以将其与ZonedDateTime或OffsetDateTime一起使用:

但是,当你使用UTC时,瞬间更好,因为它的定义是UTC——实际上,瞬间代表一个时间点,自1970-01-01T00:00Z纪元以来的纳秒数,没有时区/偏移,所以你也可以认为它总是在UTC

如果您确定日期始终以UTC为单位,也可以使用OffsetDateTime。ZonedDateTime也可以工作,但是如果您不需要时区规则来跟踪DST规则等等,那么OffsetDateTime是一个更好的选择

另一个不同之处是,从1970-01-01T00:00Z时代开始,瞬间只有纳秒数。如果需要日、月、年、小时、分钟、秒等字段,最好使用ZonedDateTime或OffsetDateTime

您还可以查看,它有一个。

输入字符串只有日期和时间,没有时区信息,因此您可以将其解析为LocalDateTime,然后将其转换为UTC:

// parse date and time
LocalDateTime d = LocalDateTime.parse("2017-07-11T10:53:52");
// convert to UTC
ZonedDateTime z = d.atZone(ZoneOffset.UTC);
// or
OffsetDateTime odt = d.atOffset(ZoneOffset.UTC);
// convert to Instant
Instant instant = z.toInstant();
您可以使用ZonedDateTime、OffsetDateTime或Instant,因为它们都包含UTC中的等效日期和时间。要获取它们,可以使用反序列化程序

要检查此日期与当前日期之间的分钟数,可以使用java.time.temporal.ChronoUnit:

这将返回即时和当前日期/时间之间的分钟数。您还可以将其与ZonedDateTime或OffsetDateTime一起使用:

但是,当你使用UTC时,瞬间更好,因为它的定义是UTC——实际上,瞬间代表一个时间点,自1970-01-01T00:00Z纪元以来的纳秒数,没有时区/偏移,所以你也可以认为它总是在UTC

如果您确定日期始终以UTC为单位,也可以使用OffsetDateTime。ZonedDateTime也可以工作,但是如果您不需要时区规则来跟踪DST规则等等,那么OffsetDateTime是一个更好的选择

另一个不同之处是,从1970-01-01T00:00Z时代开始,瞬间只有纳秒数。如果需要日、月、年、小时、分钟、秒等字段,最好使用ZonedDateTime或OffsetDateTime


您也可以看看,它有一个。非常感谢您的评论,他们让我走上了一条好路

为了让其他遇到这个问题的人受益,下面是我使用的代码

修改模型类:

import java.time.LocalDateTime

class Thing {
   public UUID Id
   public LocalDateTime CreatedDate
}
Utils类还包括ZoneDateTime的方法,因为我从中获得了原始代码。结果证明我可以让LocalDateTime为我工作。setDateFormat是为了支持在其他模型类中使用的日期对象,在这些模型类中我不需要进行比较,尽管我可以看到自己很快就会反对所有这些

class Utils {
    static Gson UtilGson = new GsonBuilder()
            .registerTypeAdapter(ZonedDateTime.class, GsonHelper.ZDT_DESERIALIZER)
            .registerTypeAdapter(LocalDateTime.class, GsonHelper.LDT_DESERIALIZER)
            .registerTypeAdapter(OffsetDateTime.class, GsonHelper.ODT_DESERIALIZER)
            .setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
            .create();

    // From https://stackoverflow.com/a/36418842/276036
    static class GsonHelper {

        public static final JsonDeserializer<ZonedDateTime> ZDT_DESERIALIZER = new JsonDeserializer<ZonedDateTime>() {
            @Override
            public ZonedDateTime deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
                JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive();
                try {

                    // if provided as String - '2011-12-03T10:15:30+01:00[Europe/Paris]'
                    if(jsonPrimitive.isString()){
                        return ZonedDateTime.parse(jsonPrimitive.getAsString(), DateTimeFormatter.ISO_ZONED_DATE_TIME);
                    }

                    // if provided as Long
                    if(jsonPrimitive.isNumber()){
                        return ZonedDateTime.ofInstant(Instant.ofEpochMilli(jsonPrimitive.getAsLong()), ZoneId.systemDefault());
                    }

                } catch(RuntimeException e){
                    throw new JsonParseException("Unable to parse ZonedDateTime", e);
                }
                throw new JsonParseException("Unable to parse ZonedDateTime");
            }
        };

        public static final JsonDeserializer<LocalDateTime> LDT_DESERIALIZER = new JsonDeserializer<LocalDateTime>() {
            @Override
            public LocalDateTime deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
                JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive();
                try {

                    // if provided as String - '2011-12-03T10:15:30'
                    if(jsonPrimitive.isString()){
                        return LocalDateTime.parse(jsonPrimitive.getAsString(), DateTimeFormatter.ISO_DATE_TIME);
                    }

                    // if provided as Long
                    if(jsonPrimitive.isNumber()){
                        return LocalDateTime.ofInstant(Instant.ofEpochMilli(jsonPrimitive.getAsLong()), ZoneId.systemDefault());
                    }

                } catch(RuntimeException e){
                    throw new JsonParseException("Unable to parse LocalDateTime", e);
                }
                throw new JsonParseException("Unable to parse LocalDateTime");
            }

         public static final JsonDeserializer<OffsetDateTime> ODT_DESERIALIZER = new JsonDeserializer<OffsetDateTime>() {
        @Override
        public OffsetDateTime deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
            JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive()
            try {

                // if provided as String - '2011-12-03T10:15:30' (i.e. no timezone information e.g. '2011-12-03T10:15:30+01:00[Europe/Paris]')
                // We know our services return UTC dates without specific timezone information so can do this.
                // But if, in future we have a different requirement, we'll have to review.
                if(jsonPrimitive.isString()){
                    LocalDateTime localDateTime = LocalDateTime.parse(jsonPrimitive.getAsString());
                    return localDateTime.atOffset(ZoneOffset.UTC)
                }
            } catch(RuntimeException e){
                throw new JsonParseException("Unable to parse OffsetDateTime", e)
            }
            throw new JsonParseException("Unable to parse OffsetDateTime")
        }
    }
        };
    }
下面是比较Spock/Groovy的代码:

// ... first get the JSON text from the REST call

when:
text = response.responseBody
def thing = Utils.UtilGson.fromJson(text, Thing.class)
def now = OffsetDateTime.now(ZoneOffset.UTC)
def aMinuteAgo = now.plusSeconds(-60)

then:
thing.CreatedDate > aMinuteAgo
thing.CreatedDate < now
与运算符进行比较似乎更为自然。当我显式地将OffsetDateTime用于UTC时,它工作得很好。我只使用此模型对实际在.NET中实现的服务执行集成测试,因此对象不会在测试之外使用。

anks的评论太多了,他们让我走上了一条好路

为了让其他遇到这个问题的人受益,下面是我使用的代码

修改模型类:

import java.time.LocalDateTime

class Thing {
   public UUID Id
   public LocalDateTime CreatedDate
}
Utils类还包括ZoneDateTime的方法,因为我从中获得了原始代码。结果证明我可以让LocalDateTime为我工作。setDateFormat是为了支持在其他模型类中使用的日期对象,在这些模型类中我不需要进行比较,尽管我可以看到自己很快就会反对所有这些

class Utils {
    static Gson UtilGson = new GsonBuilder()
            .registerTypeAdapter(ZonedDateTime.class, GsonHelper.ZDT_DESERIALIZER)
            .registerTypeAdapter(LocalDateTime.class, GsonHelper.LDT_DESERIALIZER)
            .registerTypeAdapter(OffsetDateTime.class, GsonHelper.ODT_DESERIALIZER)
            .setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
            .create();

    // From https://stackoverflow.com/a/36418842/276036
    static class GsonHelper {

        public static final JsonDeserializer<ZonedDateTime> ZDT_DESERIALIZER = new JsonDeserializer<ZonedDateTime>() {
            @Override
            public ZonedDateTime deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
                JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive();
                try {

                    // if provided as String - '2011-12-03T10:15:30+01:00[Europe/Paris]'
                    if(jsonPrimitive.isString()){
                        return ZonedDateTime.parse(jsonPrimitive.getAsString(), DateTimeFormatter.ISO_ZONED_DATE_TIME);
                    }

                    // if provided as Long
                    if(jsonPrimitive.isNumber()){
                        return ZonedDateTime.ofInstant(Instant.ofEpochMilli(jsonPrimitive.getAsLong()), ZoneId.systemDefault());
                    }

                } catch(RuntimeException e){
                    throw new JsonParseException("Unable to parse ZonedDateTime", e);
                }
                throw new JsonParseException("Unable to parse ZonedDateTime");
            }
        };

        public static final JsonDeserializer<LocalDateTime> LDT_DESERIALIZER = new JsonDeserializer<LocalDateTime>() {
            @Override
            public LocalDateTime deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
                JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive();
                try {

                    // if provided as String - '2011-12-03T10:15:30'
                    if(jsonPrimitive.isString()){
                        return LocalDateTime.parse(jsonPrimitive.getAsString(), DateTimeFormatter.ISO_DATE_TIME);
                    }

                    // if provided as Long
                    if(jsonPrimitive.isNumber()){
                        return LocalDateTime.ofInstant(Instant.ofEpochMilli(jsonPrimitive.getAsLong()), ZoneId.systemDefault());
                    }

                } catch(RuntimeException e){
                    throw new JsonParseException("Unable to parse LocalDateTime", e);
                }
                throw new JsonParseException("Unable to parse LocalDateTime");
            }

         public static final JsonDeserializer<OffsetDateTime> ODT_DESERIALIZER = new JsonDeserializer<OffsetDateTime>() {
        @Override
        public OffsetDateTime deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
            JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive()
            try {

                // if provided as String - '2011-12-03T10:15:30' (i.e. no timezone information e.g. '2011-12-03T10:15:30+01:00[Europe/Paris]')
                // We know our services return UTC dates without specific timezone information so can do this.
                // But if, in future we have a different requirement, we'll have to review.
                if(jsonPrimitive.isString()){
                    LocalDateTime localDateTime = LocalDateTime.parse(jsonPrimitive.getAsString());
                    return localDateTime.atOffset(ZoneOffset.UTC)
                }
            } catch(RuntimeException e){
                throw new JsonParseException("Unable to parse OffsetDateTime", e)
            }
            throw new JsonParseException("Unable to parse OffsetDateTime")
        }
    }
        };
    }
下面是比较Spock/Groovy的代码:

// ... first get the JSON text from the REST call

when:
text = response.responseBody
def thing = Utils.UtilGson.fromJson(text, Thing.class)
def now = OffsetDateTime.now(ZoneOffset.UTC)
def aMinuteAgo = now.plusSeconds(-60)

then:
thing.CreatedDate > aMinuteAgo
thing.CreatedDate < now

与运算符进行比较似乎更为自然。当我显式地将OffsetDateTime用于UTC时,它工作得很好。我只使用此模型对实际在.NET中实现的服务执行集成测试,因此对象不会在我的测试之外使用。

您需要注册自己的反序列化程序,看看您是否对该数据源有任何影响,让他们在日期时间字符串的末尾附加一个Z,如果它确实是UTC中的一个时刻。Z是Zulu的缩写,表示UTC。您需要注册自己的反序列化程序,查看您是否对该数据源有任何影响,如果确实希望在UTC中出现某个时刻,请让他们在该日期时间字符串的末尾附加一个Z。Z是Zulu的缩写,表示UTC。关于命名:Java中的变量按约定以小写字母开头。因此id和createdDate,b createdDate的名称是不明确的,很容易与仅限日期的LocalDate值混淆。我建议使用类似于“whenCreated”的方式。如果您知道该值用于偏移量或区域,则不要在LocalDateTime中使用。你会故意丢弃有价值的信息。如果您确定该值适用于UTC,则使用分配的偏移区域offset.UTC表示为OffsetDateTime。研究雨果的答案,这很好,只是他应该使用OffsetDateTime而不是UTC的ZonedDateTime。@BasilBourque事实上,我忘了提到OffsetDateTime。我已经更新了我的答案,谢谢@BasilBourque和@Hugo,非常感谢你的帮助。关于createdDate属性的命名,这是一个公平的评论,但这适用于服务端,我只希望测试的本地模型反映该命名。@BasilBourque和@Hugo关于样式,我来自.NET背景,所以这对我来说更自然。然而,我想用正确的方式做事,所以我已经注意到了你所说的。问题在于.NET中实现的服务返回PascalCase中的属性。因此,默认情况下,Gson不会进行反序列化。尽管我后来发现我可以使用.setFieldNamingPolicyFieldNamingPolicy.UPPER_CAMEL_案例,但我测试的其他服务的大小写却不同!。我希望现在避免为单独的服务使用单独的Gson。不过我以后可能会改变主意!关于命名:Java中的变量按约定以小写字母开头。因此id和createdDate,b createdDate的名称是不明确的,很容易与仅限日期的LocalDate值混淆。我建议使用类似于“whenCreated”的方式。如果您知道该值用于偏移量或区域,则不要在LocalDateTime中使用。你会故意丢弃有价值的信息。如果您确定该值适用于UTC,则使用分配的偏移区域offset.UTC表示为OffsetDateTime。研究雨果的答案,这很好,只是他应该使用OffsetDateTime而不是UTC的ZonedDateTime。@BasilBourque事实上,我忘了提到OffsetDateTime。我已经更新了我的答案,谢谢@BasilBourque和@Hugo,非常感谢你的帮助。关于createdDate属性的命名,这是一个公平的评论,但这适用于服务端,我只希望测试的本地模型反映该命名。@BasilBourque和@Hugo关于样式,我来自.NET背景,所以这对我来说更自然。然而,我想用正确的方式做事,所以我已经注意到了你所说的。问题在于.NET中实现的服务返回PascalCase中的属性。因此,默认情况下,Gson不会进行反序列化。尽管我后来发现我可以使用.setFieldNamingPolicyFieldNamingPolicy.UPPER_CAMEL_案例,但我测试的其他服务的大小写却不同!。我希望现在避免为单独的服务使用单独的Gson。不过我以后可能会改变主意!