我想扩展我在 Reddit Android 开发社区上开始的讨论 yesterday有一个新问题:您如何在具有过时时区信息的设备上使用 JodaTime 库管理您的应用程序附带的最新时区数据库?
手头的具体问题与特定时区“欧洲/加里宁格勒”有关。我可以重现问题:在 Android 4.4 设备上,如果我手动将其时区设置为上述时区,调用 new DateTime() 会将此 DateTime 实例设置为时间比手机状态栏上显示的实际时间早一小时。
我创建了一个示例 Activity 来说明这个问题。在其 onCreate() 上,我调用以下内容:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ResourceZoneInfoProvider.init(getApplicationContext());
ViewGroup v = (ViewGroup) findViewById(R.id.root);
addTimeZoneInfo("America/New_York", v);
addTimeZoneInfo("Europe/Paris", v);
addTimeZoneInfo("Europe/Kaliningrad", v);
}
private void addTimeZoneInfo(String id, ViewGroup root) {
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.setTimeZone(id);
//Joda does not update its time zone automatically when there is a system change
DateTimeZone.setDefault(DateTimeZone.forID(id));
View v = getLayoutInflater().inflate(R.layout.info, root, false);
TextView idInfo = (TextView) v.findViewById(R.id.id);
idInfo.setText(id);
TextView timezone = (TextView) v.findViewById(android.R.id.text1);
timezone.setText("Time zone: " + TimeZone.getDefault().getDisplayName());
TextView jodaTime = (TextView) v.findViewById(android.R.id.text2);
//Using the same pattern as Date()
jodaTime.setText("Time now (Joda): " + new DateTime().toString("EEE MMM dd HH:mm:ss zzz yyyy"));
TextView javaTime = (TextView) v.findViewById(R.id.time_java);
javaTime.setText("Time now (Java): " + new Date().toString());
root.addView(v);
}
ResourceZoneInfoProvider.init() 是 joda-time-android 的一部分库,它用于初始化 Joda 的时区数据库。 addTimeZoneInfo 覆盖设备的时区并在显示更新的时区信息的地方扩充新 View 。这是一个结果示例:
请注意“Kaliningrad”,Android 如何将其映射到“GMT+3:00”,因为直到 2014 年 10 月 26 日都是这种情况(参见 Wikipedia article )。甚至一些网站仍将此时区显示为 GMT+3:00,因为此更改相对较新。然而,正确的是 JodaTime 显示的“GMT+2:00”。
这是一个问题,因为无论我如何尝试规避它,最后我都必须格式化时间以在他们的时区中向用户显示它。当我使用 JodaTime 执行此操作时,时间格式将不正确,因为它与系统显示的预期时间不匹配。
或者,假设我用 UTC 处理所有事情。当用户在日历中添加事件并选择提醒时间时,我可以将其设置为 UTC,像这样将其存储在数据库中并完成。
但是,我需要使用 Android 的 AlarmManager 设置提醒,而不是在 UTC 时间我转换了用户设置的时间,而是在相对于他们希望触发提醒的时间。这需要时区信息才能发挥作用。
例如,如果用户在 UTC+1:00 的某个地方并且他或她设置了上午 9:00 的提醒,我可以:
DateTime 实例,设置为用户时区的上午 9:00,并将其毫秒数存储在数据库中。我也可以直接将相同的毫秒数用于 AlarmManager;DateTime 实例集,时间为 UTC 上午 9:00,并将其毫秒数存储在数据库中。这更好地解决了与该问题不完全相关的其他一些问题。但是当使用 AlarmManager 设置时间时,我需要计算其在用户时区上午 9:00 的毫秒值;DateTime 并使用 Java 的 Calendar 处理提醒设置。这将使我的应用在显示时间时依赖过时的时区信息,但至少在使用 AlarmManager 安排时间或显示日期和时间时不会出现不一致。我可能想多了,恐怕我可能遗漏了一些明显的东西。我是吗?除了向应用程序添加我自己的时区管理并完全忽略所有内置的 Android 格式化功能之外,有什么方法可以让我继续在 Android 上使用 JodaTime?
最佳答案
我认为其他答案没有捕获要点。是的,在持久化时间信息时,您应该仔细考虑您的用例以决定如何最好地做到这一点。但即使您这样做了,这个问题提出的问题仍然存在。
考虑一下 Android 的闹钟应用程序,它有自己的源代码 freely available .如果你看看它的 AlarmInstance类,这是它在数据库中的建模方式:
private static final String[] QUERY_COLUMNS = {
_ID,
YEAR,
MONTH,
DAY,
HOUR,
MINUTES,
LABEL,
VIBRATE,
RINGTONE,
ALARM_ID,
ALARM_STATE
};
要知道何时应该触发警报实例,您可以调用 getAlarmTime() :
/**
* Return the time when a alarm should fire.
*
* @return the time
*/
public Calendar getAlarmTime() {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, mYear);
calendar.set(Calendar.MONTH, mMonth);
calendar.set(Calendar.DAY_OF_MONTH, mDay);
calendar.set(Calendar.HOUR_OF_DAY, mHour);
calendar.set(Calendar.MINUTE, mMinute);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar;
}
请注意 AlarmInstance存储它应该触发的确切时间,与时区无关。这样可以确保每次调用 getAlarmTime()你得到了正确的时间来触发用户的时区。这里的问题是如果时区没有更新,getAlarmTime()无法获得正确的时间更改,例如,夏令时开始时。
JodaTime在这种情况下派上用场,因为它带有自己的时区数据库。您可以考虑其他日期时间库,例如 date4j为了方便更好地处理日期计算,但这些通常不处理自己的时区数据。
但是拥有自己的时区数据会给您的应用带来限制:您不能再依赖 Android 的时区。这意味着你不能使用它的 Calendar类或其格式化函数。 JodaTime 也提供格式化功能,使用它们。如果必须转换为 Calendar ,而不是使用 toCalendar()方法,创建一个类似于 getAlarmTime() 的方法在您经过的确切时间上方。
或者,您可以检查是否存在时区不匹配,并像 Matt Johnson 在 his comment 中建议的那样警告用户.如果您决定继续使用 Android 和 Joda 的功能,我同意他的观点:
Yes - with two sources of truth, if they're out of sync, there will be mismatches. Check the versions, show a warning, ask to be updated, etc. There's probably not much more you can do than that.
您还可以做一件事:您可以自己更改 Android 的时区。您可能应该在这样做之前警告用户,然后您可以强制 Android 使用与 Joda 相同的时区偏移量:
public static boolean isSameOffset() {
long now = System.currentTimeMillis();
return DateTimeZone.getDefault().getOffset(now) == TimeZone.getDefault().getOffset(now);
}
检查后,如果不相同,您可以使用您创建的“假”时区从 Joda 的正确时区信息的偏移量更改 Android 的时区:
public static void updateTimeZone(Context c) {
TimeZone tz = DateTimeZone.forOffsetMillis(DateTimeZone.getDefault().getOffset(System.currentTimeMillis())).toTimeZone();
AlarmManager mgr = (AlarmManager) c.getSystemService(Context.ALARM_SERVICE);
mgr.setTimeZone(tz.getID());
}
请记住,您需要 <uses-permission android:name="android.permission.SET_TIME_ZONE"/>对此的许可。
最后,更改时区会更改系统当前时间。不幸的是,只有系统应用程序可以设置时间,所以您能做的最好的事情就是为用户打开日期时间设置并提示他/她手动将其更改为正确的时间:
startActivity(new Intent(android.provider.Settings.ACTION_DATE_SETTINGS));
您还必须添加更多控件以确保在 DST 开始和结束时更新时区。正如您所说,您将添加自己的时区管理,但这是确保两个时区数据库之间一致性的唯一方法。
关于android - 如何处理 JodaTime 和 Android 的时区数据库差异?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29583662/
对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m
有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳
我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01 客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02 数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit
文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co
我正在尝试在Rails上安装ruby,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,