【问题标题】:android how to identify the same days of a weekandroid如何识别一周中的同一天
【发布时间】:2015-11-24 18:18:19
【问题描述】:

为了管理日期,我使用了日历课程。我将时间戳保存在数据库中,使用

日历时间戳 = Calendar.getInstance();

我可以使用长值来保存和检索数据库中的日期来存储日期。

这是我的问题:当我要存储时间戳时,我需要知道数据库中是否已经存在属于同一周的时间戳。为此,我认为我可以使用以下几种方法:

timestamp.get(Calendar.YEAR)timestamp.get(Calendar.WEEK_OF_YEAR)

唯一标识一周,但对于具有连续两年的日子的周来说,这不是真的。

这是我在 2014 年最后一周得到的一个例子。

2014 年 12 月 27 日,星期六 年份:2014 周:52

2014 年 12 月 28 日,星期日 年份:2014 周:1

2014 年 12 月 29 日,星期一 年份:2014 周:1

2014 年 12 月 30 日,星期二 年份:2014 周:1

2014 年 12 月 31 日,星期三 年份:2014 周:1

2015 年 1 月 1 日,星期四 年份:2015 周:1

2015 年 1 月 2 日,星期五 年份:2015 周:1

2015 年 1 月 3 日,星期六 年份:2015 周:1

2015 年 1 月 4 日,星期日 年份:2015 周:2

从 2014 年 12 月 28 日到 2015 年 3 月 1 日这几天属于同一周(在 java 中,这周从星期日开始),但他们有不同的年份,所以这对夫妇 (YEAR, WEEK_OF_YEAR) 不给我他们属于同一周的信息。

我该如何解决?

【问题讨论】:

  • Calendar 类是出了名的麻烦,应该避免(同上 java.util.Date)。在这种情况下,尤其是这个类是一个糟糕的选择:一周的定义是Locale dependent!因此,如果移动到具有不同默认语言环境的计算机上,您的应用程序的行为将会发生变化。 JVM 中的任何线程甚至可以在运行时更改语言环境。我强烈建议您考虑使用 Joda-Time 库。它使用标准 ISO 8601 定义的周和周数。
  • @basil。我将 Joda-Time 库添加到项目中,它似乎工作。现在我必须将迄今为止使用的日期和时间类转换为新的类,并找到一种获得想要的好方法。我将研究我可以使用的方法。如果您有任何建议,我将不胜感激。谢谢
  • 我的建议:在 StackOverflow 中搜索“joda”以找到数百个带有解释的示例。
  • 另外,还有一两个来自其他人的 Joda-Time 版本,旨在与 Android 一起使用,以解决首次加载时初始缓慢的问题。在真正的 Java 中没有问题,只有 Android。
  • 可能你指的是这个:dlew/joda-time-android。好的,但很抱歉我不明白如何安装这个库。我必须先删除 joda-time 库吗?然后?我正在寻找分步教程,但到目前为止我没有找到任何东西。

标签: java android date calendar


【解决方案1】:

tl;博士

您必须了解“正常”calendar yearstandard week-based year 之间的区别。日历年的第一天/最后几天可能出现在基于上一周/下一周的年份中。

当然,2014 日历年于 2014 年 12 月 31 日结束。但 2014 年的最后一天落在了 2015 年周。

2014 年的最后一天于 2014 年 12 月 28 日(日历年)结束。基于周的 2015 年的第一天是 2014 年 12 月 29 日(日历年)。上面的日历图应该清楚地说明这些事实。

2014-12-28 = 2014-W52-7

2014-12-29 = 2015-W01-1

2015-01-01 = 2015-W01-4

询问基于周和年的数字:

myZonedDateTime.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR )

……和……

myZonedDateTime.get( IsoFields.WEEK_BASED_YEAR )

简而言之:在一年的最后/最初几天,调用myZonedDateTime.getYear()myZonedDateTime.get( IsoFields.WEEK_BASED_YEAR ) 的结果可能不同

java.time

现代方法使用 java.time 类取代了麻烦的旧旧日期时间类。

为了管理日期,我使用了日历课程。我在数据库中保存时间戳,使用Calendar timestamp = Calendar.getInstance();

相反,请使用ZoneDateTime 以特定地区(时区)的人们使用的挂钟时间来捕捉当前时刻。

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;

我可以使用长值来保存和检索数据库中的日期来存储日期。

不,在定义数据库列和 Java 代码时使用适当的数据类型。

要在符合 SQL 标准的数据库中存储时间线上的特定时刻,请将列定义为 TIMESTAMP WITH TIME ZONE

使用 JDBC 4.2 及更高版本,您可以直接与数据库交换 java.time 对象。

myPreparedStatement.setObject( … , zdt ) ;

Instant 的形式检索,UTC 时刻,可能是另一个方向的最佳路线。然后调整到您想要的时区。

Instant instant = myResultSet.getObject( … , Instant.class ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

这是我的问题:当我要存储时间戳时,我需要知道数据库中是否已经存在属于同一周的时间戳。为此,我想我可以使用以下两种方法:timestamp.get(Calendar.YEAR)timestamp.get(Calendar.WEEK_OF_YEAR)

不幸的是,令人困惑的Calendar 类中一周的含义因地区而异。

也许你自己的定义符合标准ISO 8601definition of a week

  • 第一天是星期一,一直持续到星期日。
  • 一周年的第一周包含日历年的第一个星期四。
  • 基于周的一年有 52 周或 53 周。
  • 日历年的第一天/最后几天可能出现在基于上一周/下一周的年份中。

请注意,确定一周需要确定日期。确定日期需要时区。对于任何给定的时刻,日期在全球范围内因区域而异。因此,如上所示,请清楚您对时区 (ZoneId) 的使用。换句话说,周数是上下文相关的;这取决于特定区域的日期。

ZonedDateTime::get 中访问TemporalFieldIsoFields 类定义了您需要的常量。

int week = zdt.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) ;
int weekBasedYear = zdt.get( IsoFields.WEEK_BASED_YEAR ) ;

更好的是,将ThreeTen-Extra 库添加到您的项目中以使用YearWeek 类。

org.threeten.extra.YearWeek yw = YearWeek.from( zdt ) ;

唯一标识一周,但对于具有连续两年的日子的周来说,这不是真的。

这是我在 2014 年最后一周得到的一个例子。

2014 年 12 月 27 日星期六 年份:2014 周:52

在标准周中,2014-12-27 是第 52 周。

YearWeek.from( LocalDate.parse( "2014-12-27" )  ).toString()

2014-W52

2014 年底恰好在接下来的周年(2015 年)。

YearWeek.from( LocalDate.parse( "2014-12-31" ) ).toString()

2015-W01

您可以比较YearWeek。调用equalsisBeforeisAfter 方法。

boolean sameYearWeek = YearWeek.from( someZdt ).equals( YearWeek.from( otherZdt ) ) 

术语

因此,请确保在您的文档、代码 cmets 和讨论中,始终区分日历年和基于周的年。永远不要说“年份”,因为那是非常模棱两可的。

加上财政年度会更加混乱。有些行业对年份和季节等有自己的定义。所以要小心你的条款。

注意日历软件设置

永远不要假设周数的定义。请确保此类数字的来源与您的星期定义相同,例如 ISO 8601 定义。

例如,macOS 的 Calendar app supplied by Apple 默认为“公历”日历定义的星期。至于这意味着什么,我不知道,因为我找不到任何关于他们的意图/定义的文档。对于 ISO 8601 周,您必须change a setting away from default


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

  • Java SE 8Java SE 9 及更高版本
    • 内置。
    • 标准 Java API 的一部分,带有捆绑实现。
    • Java 9 添加了一些小功能和修复。
  • Java SE 6Java SE 7
    • ThreeTen-Backport 中的大部分 java.time 功能都向后移植到 Java 6 和 7。
  • Android
    • java.time 类的 Android 捆绑包实现的更高版本。
    • 对于早期的 Android (ThreeTenABP 项目采用 ThreeTen-Backport(如上所述)。见How to use ThreeTenABP…

【讨论】:

    【解决方案2】:

    按照@basil 的建议,我尝试了 Joda-Time 库,并在那里找到了解决问题的方法。 要获取一年中的第几周,我可以使用 getWeekOfWeekyear() 方法,而要获取年份,我可以使用方法 getWeekyear()

    通过这种方式,这两个值唯一地标识了属于同一周的日期,该周从星期一开始,到星期日结束(与 Java 日历类相比,这是另一个附加值)。

    按照相同的问题示例。

    DateTime[] timestamp = new DateTime[10];
    
    for(int i=0; i<10; i++){
       //set the start of the period
       DateTime time = new DateTime(2014, 12, 27, 18, 30, 50);
       //add i days to the start date
       timestamp[i] = time.plus(Period.days(i));
    
       int weekYear = timestamp[i].getWeekyear();
       int weekOfWeekyear = timestamp[i].getWeekOfWeekyear();
    
       Log.d(Constants.LOG_TAG, timestamp[i].toString("EEEE, MMMM d, yyyy") + " | year.week = " + weekYear + "." + weekOfWeekyear);
    }
    

    结果是:

    2014 年 12 月 27 日,星期六 | year.week = 2014.52

    2014 年 12 月 28 日,星期日 | year.week = 2014.52

    2014 年 12 月 29 日,星期一 | year.week = 2015.1

    2014 年 12 月 30 日,星期二 | year.week = 2015.1

    2014 年 12 月 31 日,星期三 | year.week = 2015.1

    2015 年 1 月 1 日,星期四 | year.week = 2015.1

    2015 年 1 月 3 日,星期六 | year.week = 2015.1

    2015 年 1 月 4 日,星期日 | year.week = 2015.1

    2015 年 1 月 5 日,星期一 | year.week = 2015.2

    看起来使用这个库还有很多其他的优点,它对我必须执行的其他计算很有用。

    谢谢@basil。

    【讨论】:

    • 您可以编辑您的答案以包含一些示例代码吗?
    • 我建议您将时区 (DateTimeZone) 传递给该构造函数。如果省略,您的 JVM 的当前默认值将自动应用。这意味着您的结果可能会有所不同。更好的做法是始终指定您的预期/期望时区。
    【解决方案3】:

    要获取相对于日期的一周的第一天和最后一天,请执行以下操作:

    Calendar cal = Calendar.getInstance();
    cal.setTime(date); // date to check
    cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
    Date startOfWeek = cal.getTime();
    cal.add(Calendar.DAY_OF_MONTH, 6);
    Date endOfWeek = cal.getTime();
    

    免责声明:我从How to get the first day of the current week and month?偷了部分内容

    然后使用where 子句检查日期范围。

    SELECT * FROM mytable WHERE mydate >= ? AND mydate <= ?
    

    如果您存储的日期包括时间,您希望下周开始作为上排边界,通过添加7 而不是6 天以上。

    SELECT * FROM mytable WHERE mydate >= ? AND mydate < ?
    

    为了显示日期逻辑,这里有一些测试代码:

    // Start on 12/27/2014
    Calendar cal = Calendar.getInstance();
    cal.clear();
    cal.set(2014, Calendar.DECEMBER, 27);
    
    // Collect 10 dates to test (start date + next 9 days)
    Date[] dates = new Date[10];
    dates[0] = cal.getTime();
    for (int i = 1; i < dates.length; i++) {
        cal.add(Calendar.DAY_OF_MONTH, 1);
        dates[i] = cal.getTime();
    }
    
    // Test loop    
    SimpleDateFormat fmt = new SimpleDateFormat("EEE d/M/yyyy");
    for (Date date : dates) {
    
        // Code to test
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
        Date startOfWeek = cal.getTime();
        cal.add(Calendar.DAY_OF_MONTH, 6);
        Date endOfWeek = cal.getTime();
    
        // Show result
        System.out.println(fmt.format(date) + "  ->  " + fmt.format(startOfWeek) +
                                                 " - " + fmt.format(endOfWeek));
    }
    

    输出

    Sat 27/12/2014  ->  Sun 21/12/2014 - Sat 27/12/2014
    Sun 28/12/2014  ->  Sun 28/12/2014 - Sat 3/1/2015
    Mon 29/12/2014  ->  Sun 28/12/2014 - Sat 3/1/2015
    Tue 30/12/2014  ->  Sun 28/12/2014 - Sat 3/1/2015
    Wed 31/12/2014  ->  Sun 28/12/2014 - Sat 3/1/2015
    Thu 1/1/2015  ->  Sun 28/12/2014 - Sat 3/1/2015
    Fri 2/1/2015  ->  Sun 28/12/2014 - Sat 3/1/2015
    Sat 3/1/2015  ->  Sun 28/12/2014 - Sat 3/1/2015
    Sun 4/1/2015  ->  Sun 4/1/2015 - Sat 10/1/2015
    Mon 5/1/2015  ->  Sun 4/1/2015 - Sat 10/1/2015
    

    【讨论】:

    • 感谢您的回答。所以每次我必须管理时间戳时,我都需要添加一些逻辑。我认为会有一些方法可以完成这项工作。因为我必须进行其他日期和时间计算,所以我想我会按照@Basil Bourque 的建议使用 Joda-Time 库。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-25
    • 1970-01-01
    • 2018-02-08
    • 2015-08-24
    • 1970-01-01
    相关资源
    最近更新 更多