Android设备系统时间将其转换为Date时更改服务器日期字符串的TimeZone

如何解决Android设备系统时间将其转换为Date时更改服务器日期字符串的TimeZone

我必须检查服务器时间与Android设备系统时间之间的时差是否超过1小时。 现在我有来自服务器的这个字符串:

2020-08-24T11:50:18.613+0300

这些是我用于SimpleDateFormat的常量:

private static final String SYSTEM_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"

问题是当我尝试将字符串日期转换为Date类时,我得到了这个提示:

Mon Aug 24 13:50:18 GMT+05:00 2020

据我了解,+ 05:00是因为这是在Android设备上默认设置的时区。这就是我得到这个日期的方式:

Locale locale =  new Locale("ru","RU");
DateFormat df = new SimpleDateFormat(SYSTEM_FORMAT,locale);
df.setTimeZone(TimeZone.getTimeZone("Europe/Moscow"));
Date date = df.parse(serverDate);

您可以看到,即使将时区设置为+3(莫斯科时间)也无法达到我期望的日期。我知道我可以只使用字符串进行比较,但要求是比较日期

解决方法

您实际上没有时间 zone ,而是一个偏移量。

要正确转换从服务器获得的String,应使用java.time而不是过时的java.util.Datejava.util.Calendar

下面是一个使用适合您的情况的类的示例(java.time.OffsetDateTime):

public static void main(String[] args) {
    String stringFromServer = "2020-08-24T11:50:18.613+0300";
    OffsetDateTime timeFromServer = OffsetDateTime.parse(
                        stringFromServer,DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSZ")
    );
    
    System.out.println(timeFromServer.format(
            DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss XXX uuuu",Locale.ENGLISH))
    );
}

此输出(使用模仿您发布的格式的OffsetDateTime格式的输出)

Mon Aug 24 11:50:18 +03:00 2020

您也可以使用ZonedDateTime,但是很可能必须添加ZoneIdZoneId.of("Europe/Moscow") ...
可以使用已经创建的OffsetDateTime timeFromServer这样完成:

ZonedDateTime moscowTime = timeFromServer.atZoneSameInstant(ZoneId.of("Europe/Moscow"));
ZonedDateTime berlinTime = timeFromServer.atZoneSameInstant(ZoneId.of("Europe/Berlin"));

,然后System.out对它们进行特殊格式化将为您提供以下几行:

2020-08-24T11:50:18.613+03:00[Europe/Moscow]
2020-08-24T10:50:18.613+02:00[Europe/Berlin]

请注意: 由于存在API Desugaring for Android,因此您可以在Android 26以下的API级别中使用Java 8(+)功能。

,

在日期时间字符串中,您对Zone-Offset的理解似乎有些差距。日期时间字符串2020-08-24T11:50:18.613+0300表示它是+0300 hours的区域偏移处的日期时间,即UTC上的相应日期时间字符串将为2020-08-24T8:50:18.613+0000。 / p>

请注意,java.util.Date缺少时区和时区偏移信息。它仅具有自1970年1月1日UTC时间00:00:00以来的毫秒数。当使用java.util.Date的实例打印SimpleDateFormat时,会将表示时区中日期时间的字符串打印到SimpleDateFormat的实例中。您可以从以下示例中了解它:

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

public class Main {
    public static void main(String[] args) throws ParseException {
        // Parse the date-time string to java.util.Date
        String serverDate = "2020-08-24T11:50:18.613+0300";
        final String SYSTEM_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
        DateFormat dfSource = new SimpleDateFormat(SYSTEM_FORMAT);
        Date date = dfSource.parse(serverDate);
        System.out.println(date);

        // Get the date-time string for the time-zone of Europe/Moscow from
        // java.util.Date
        Locale locale = new Locale("ru","RU");
        DateFormat dfTarget = new SimpleDateFormat(SYSTEM_FORMAT,locale);
        dfTarget.setTimeZone(TimeZone.getTimeZone("Europe/Moscow"));
        System.out.println(dfTarget.format(date));
    }
}

我建议您停止使用过时且容易出错的java.util日期时间API和SimpleDateFormat。切换到modern java.time日期时间API和相应的格式API(java.time.format)。从 Trail: Date Time 了解有关现代日期时间API的更多信息。如果您的Android版本与Java-8不兼容,则可以使用ThreeTen-BackportCheck向后移植。选中How to use ThreeTenABP in Android Project

使用现代日期时间API:

import java.text.ParseException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) throws ParseException {
        // Given date-time string
        String serverDate = "2020-08-24T11:50:18.613+0300";

        // Date-time formatter
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");

        ZonedDateTime zdtServer = ZonedDateTime.parse(serverDate,formatter);
        System.out.println("Given date-time: " + zdtServer);
        System.out.println("Zone Offset of the given date-time: " + zdtServer.getOffset());

        // Convert it to some other time-zone e.g Etc/UTC
        ZonedDateTime zdtatUTC = zdtServer.withZoneSameInstant(ZoneId.of("Etc/UTC"));
        System.out.println("Equivalent date-time at UTC: " + zdtatUTC);

        // Convert it to some other time-zone e.g Europe/London
        ZonedDateTime zdtInLondon = zdtServer.withZoneSameInstant(ZoneId.of("Europe/London"));
        System.out.println("Equivalent date-time in London: " + zdtInLondon);
    }
}

输出:

Given date-time: 2020-08-24T11:50:18.613+03:00
Zone Offset of the given date-time: +03:00
Equivalent date-time at UTC: 2020-08-24T08:50:18.613Z[Etc/UTC]
Equivalent date-time in London: 2020-08-24T09:50:18.613+01:00[Europe/London]

请注意,现代的日期时间API具有一个名为ZonedDateTime的类,该类具有时区信息以及日期和时间信息。

,

比较时间时,无需担心与UTC或时区的偏差。偏移量之间的比较比较顺利。

java.time

我正在使用java.time(现代Java日期和时间API)。首先定义几个有用的常量:

private static final Duration TOLERANCE = Duration.ofHours(1);

private static final DateTimeFormatter serverFormatter = new DateTimeFormatterBuilder()
        .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
        .appendPattern("XX")
        .toFormatter();

现在我们可以做到:

    String serverDateTimeString = "2020-08-24T11:50:18.613+0300";
    OffsetDateTime serverTime = OffsetDateTime.parse(serverDateTimeString,serverFormatter);
    System.out.println("Server time: " + serverTime);
    
    ZoneId deviceTimeZone = ZoneId.of("Asia/Yekaterinburg");
    OffsetDateTime deviceTime = OffsetDateTime.now(deviceTimeZone);
    System.out.println("Device time: " + deviceTime);
    
    if (deviceTime.isBefore(serverTime.minus(TOLERANCE)) || deviceTime.isAfter(serverTime.plus(TOLERANCE))) {
        System.out.println("Difference between time on server and Android device system time more than 1 hour");
    } else {
        System.out.println("Difference between time on server and Android device system time 1 hour or less");
    }

我已经从问题中使用了您的字符串,因此当我现在运行该代码段时,这段代码告诉我们这两者之间存在很大差异,我们不会感到惊讶:

Server time: 2020-08-24T11:50:18.613+03:00
Device time: 2020-08-24T23:08:57.939565+05:00
Difference between time on server and Android device system time more than 1 hour

为了进行实验,我们还尝试将设备时间设置为您提出问题的时间:

    OffsetDateTime deviceTime = OffsetDateTime.of(2020,8,24,13,50,18,ZoneOffset.ofHours(5));
Server time: 2020-08-24T11:50:18.613+03:00
Device time: 2020-08-24T13:50:18+05:00
Difference between time on server and Android device system time 1 hour or less

问题:java.time是否不需要Android API级别26?

java.time在较新和较旧的Android设备上均可正常运行。它只需要至少 Java 6

  • 在Java 8和更高版本以及更新的Android设备(API级别26以上)中,内置了现代API。
  • 在非Android Java 6和7中,获得ThreeTen Backport,这是现代类的backport(JSR 310的ThreeTen;请参见底部的链接)。
  • 在较旧的Android上,请使用废除旧书或Android版本的ThreeTen Backport。称为ThreeTenABP。在后一种情况下,请确保使用子包从org.threeten.bp导入日期和时间类。

链接

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-