如何处理JodaTime的和Android的时区数据库的差异? [英] How to handle JodaTime's and Android's timezone database differences?

查看:491
本文介绍了如何处理JodaTime的和Android的时区数据库的差异?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想延长的讨论,我开始对reddit的Andr​​oid开发社区<一href="http://www.reddit.com/r/androiddev/comments/320rz5/im_convinced_jodatime_is_not_a_good_library_for/">yesterday一个新的问题:如何管理随使用JodaTime库已过时的时区信息在设备上你的应用程序向上最新的时区数据库

I want to extend a discussion I started on the Reddit Android Dev community yesterday with a new question: How do you manage an up-to-date timezone database shipped with your app using the JodaTime library on a device that has outdated timezone information?

手边的具体问题涉及到一个特定的时区,欧洲/加里宁格勒。我可以重现该问题:在Android 4.4设备,如果我手动设置其时区以上,调用新的日期时间()将设置该日期时间实例时间对手机的状态栏中显示的实际时间前一小时。

The specific issue at hand relates to a particular timezone, "Europe/Kaliningrad". And I can reproduce the problem: On an Android 4.4 device, if I manually set its time zone to the above, calling new DateTime() will set this DateTime instance to a time one hour before the actual time displayed on the phone's status bar.

我创建了一个示例活动来说明这个问题。在它的的onCreate()我所说的以下内容:

I created a sample Activity to illustrate the problem. On its onCreate() I call the following:

@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()乔达时间部分-Android 图书馆,它的目的是初始化乔达的时区数据库。 addTimeZoneInfo 将覆盖设备的时区和膨胀,其中显示了更新的时区信息的新观点。下面是结果的一个例子:

ResourceZoneInfoProvider.init() is part of the joda-time-android library and it is meant to initialize Joda's time zone database. addTimeZoneInfo overwrites the device's time zone and inflates a new view where the updated time zone information is displayed. Here is an example of result:

请注意如何对加里宁格勒,Android地图为GMT + 3:00,因为是这样的话,直到2014年10月26日(见的维基百科的文章)。即使是一些网站仍然显示该时区为GMT + 3:因为这个变化是如何比较近是00。正确的,但是,GMT + 2:00所显示JodaTime

Note how for "Kaliningrad", Android maps it to "GMT+3:00" because that was the case until 26 October 2014 (see Wikipedia article). Even some web sites still show this time zone as GMT+3:00 because of how relatively recent this change is. The correct, however, is "GMT+2:00" as displayed by JodaTime.

这是一个问题,因为无论我如何努力规避它,到最后,我必须格式化,以显示它在他们的时区的用户的时间。当我这样做,使用JodaTime,时间将格式不正确的,因为它会不匹配的预期时间的系统显示。

This is a problem because no matter how I try to circumvent it, in the end, I have to format the time to display it to the user in their time zone. And when I do that using JodaTime, the time will be incorrectly formatted because it will mismatch the expected time the system is displaying.

另外,假设我处理一切的UTC。当用户添加在日历中的事件,并选取一个时间的提醒,我可以将其设置为UTC,它存储在这样的数据库,并用它做。

Alternatively, suppose I handle everything in UTC. When the user is adding an event in the calendar and picks a time for the reminder, I can set it to UTC, store it in the db like that and be done with it.

不过,我需要设置的提醒与Android的 AlarmManager 不是在UTC时间我转换的用户,但在一个相对他们想要的时间设定的时间提醒触发。这需要的时区信息来发挥作用。

However, I need to set that reminder with Android's AlarmManager not at the UTC time I converted the time set by the user but at the one relative to the time they want the reminder to trigger. This requires the time zone info to come into play.

例如,如果用户在某个地方UTC + 1:00,他或她设置一个提醒,上午9点,我可以:

For example, if the user is somewhere at UTC+1:00 and he or she sets a reminder for 9:00am, I can:

  • 创建一个新的的DateTime 实例上午09:00在设置用户的时区和存储其毫秒分贝。我也可以直接使用相同的毫秒与 AlarmManager ;
  • 创建一个新的的DateTime 实例上午09:00在设置UTC和存储其毫秒分贝。这更好地处理一些其他的问题不完全涉及到这个问题。但设置与 AlarmManager ,我需要在用户的时区计算的毫秒值上午09:00的时候;
  • 忽略完全乔达的DateTime 和处理使用Java的日历提醒的设置。这将使显示的时候,我的应用程序依赖于过时的时区信息,但至少在调度时也不会有不一致的 AlarmManager 或显示的日期和时间。
  • Create a new DateTime instance set for 09:00am at the user's timezone and store its milliseconds in the db. I can also directly use the same milliseconds with the AlarmManager;
  • Create a new DateTime instance set for 09:00am at UTC and store its milliseconds in the db. This better addresses a few other issues not exactly related to this question. But when setting the time with the AlarmManager, I need to compute its millisecond value for 09:00am at the user's timezone;
  • Ignore completely Joda DateTime and handle the setting of the reminder using Java's Calendar. This will make my app rely on outdated time zone information when displaying the time but at least there won't be inconsistencies when scheduling with the AlarmManager or displaying the dates and times.

我可能是在想这和我怕我可能会丢失一些东西明显。我是谁?有什么办法,我可以继续使用JodaTime上添加自己的时区管理的应用程序,并完全无视Android的短所有内置Android格式化的功能呢?

I may be over thinking this and I'm afraid I might be missing something obvious. Am I? Is there any way I could keep using JodaTime on Android short of adding my own time zone management to the app and completely disregard all built-in Android formatting functions?

推荐答案

我觉得其他的答案是缺少了这一点。是的,持续时间信息的时候,您应该谨慎考虑您的使用情况,以决定如何最好地做到这一点。但是,即使你做了它,这个问题这个问题的姿态将仍然存在。

I think the other answers are missing the point. Yes, when persisting time information, you should consider carefully your use cases to decide how best to do so. But even if you had done it, the problem this question poses would still persist.

考虑Android的闹钟应用程序,它有它的源$ C ​​$ C 免费提供。如果你看一下它的 AlarmInstance 类,这是它是如何在模型数据库:

Consider Android's alarm clock app, which has its source code freely available. If you look at its AlarmInstance class, this is how it is modeled in the database:

private static final String[] QUERY_COLUMNS = {
        _ID,
        YEAR,
        MONTH,
        DAY,
        HOUR,
        MINUTES,
        LABEL,
        VIBRATE,
        RINGTONE,
        ALARM_ID,
        ALARM_STATE
};

和当警报实例应该火,你叫知道 getAlarmTime()

And to know when an alarm instance should fire, you call 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()不能得到正确的时间变化,例如,DST开始的时候。

Note how an AlarmInstance stores the exact time it should fire, regardless of time zone. This ensures that every time you call getAlarmTime() you get the correct time to fire on the user's time zone. The problem here is if the time zone is not updated, getAlarmTime() cannot get correct time changes, for example, when DST starts.

JodaTime 就派上用场了在这种情况下,因为它附带了自己的时区数据库。你可以考虑其他的日期时间库,如 date4j 以更好地处理日期计算的方便,但这些通常不要' T柄自己的时区数据。

JodaTime comes in handy in this scenario because it ships with its own time zone database. You could consider other date time libraries such as date4j for the convenience of better handling date calculations, but these typically don't handle their own time zone data.

但拥有自己的时区,数据引入了约束,以您的应用程序:你可以再在Android上的时区不能依靠。这意味着你不能使用它的日历类或它的格式化功能。 JodaTime提供的格式化功能,以及,使用它们。如果必须转换为日历,而不是使用 toCalendar()方法,创建一个类似于 getAlarmTime()上面,你通过你想要的确切时间。

But having your own time zone data introduces a constraint to your app: you cannot rely anymore on Android's time zone. That means you cannot use its Calendar class or its formatting functions. JodaTime provides formatting functions as well, use them. If you must convert to Calendar, instead of using the toCalendar() method, create one similar to the getAlarmTime() above where you pass the exact time you want.

另外,你可以检查是否有一个时区不匹配,并警告用户在<一个像马特·约翰逊建议href="http://stackoverflow.com/questions/29583662/how-to-handle-jodatimes-and-androids-timezone-database-differences#comment47546902_29705093">his评论。如果你决定继续使用这两个Android的和乔达的功能,我同意他的观点:

Alternatively, you could check whether there is a time zone mismatch and warn the user like Matt Johnson suggested in his comment. If you decide to keep using both Android's and Joda's functions, I agree with him:

是 - 与真理的两个来源,如果他们不同步,将有   错配。检查版本,显示警告,要求进行更新,   等有可能不是更多,你可以比做的。

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的使用相同的时区偏移量为乔达的:

Except there is one more thing you can do: You can change Android's time zone yourself. You should probably warn the user before doing so but then you could force Android to use the same time zone offset as Joda's:

public static boolean isSameOffset() {
    long now = System.currentTimeMillis();
    return DateTimeZone.getDefault().getOffset(now) == TimeZone.getDefault().getOffset(now);
}

检查,如果它是不一样后,您可以更改Android的时区,您创建一个假区的乔达的正确的时区信息偏差:

After checking, if it is not the same, you can change Android's time zone with a "fake" zone you create from the offset of Joda's correct time zone information:

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

记住,你需要的&LT;使用-权限的Andr​​oid:名称=android.permission.SET_TIME_ZONE/&gt;作为该许可

最后,更改时区,将改变系统当前时间。遗憾的是只有系统的应用程序可以设置时间,所以最好你能做的就是打开日期和时间设置的用户,并提示他/她以手动更改为正确的:

Finally, changing the time zone will change the system current time. Unfortunately only system apps can set the time so the best you can do is open the date time settings for the user and prompt him/her to change it manually to the correct one:

startActivity(new Intent(android.provider.Settings.ACTION_DATE_SETTINGS));

您还需要添加一些更多的控制,以确保当DST开始和结束的时间段得到更新。就像你说的,你会加入自己的时区管理,但它保证了两地时区数据库的一致性的唯一途径。

You will also have to add some more controls to make sure the time zone gets updated when DST starts and ends. Like you said, you will be adding your own time zone management but it's the only way to ensure the consistency between the two time zone databases.

这篇关于如何处理JodaTime的和Android的时区数据库的差异?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆