生成 Java 中两个日期之间的日开始时间和日结束时间。夏令时问题

Generate Day start and Day end time between two dates in Java. Day light saving issue

我必须在两个日期之间生成天数。例如:

1420090495000 -> 2015 年 1 月 1 日

1430458495000 -> 2015 年 5 月 1 日

我必须为所有日子生成时间戳,例如

2015 年 1 月 1 日 00:00:00 - 2015 年 1 月 1 日23:59:59

2015 年 1 月 2 日 00:00:00 - 2015 年 1 月 2 日23:59:59

2015 年 1 月 3 日00:00:00 - 2015 年 1 月 3 日23:59:59

等等

我能做到。但是我在夏令时问题上遇到了一些问题。在行军的某个地方,它是这样产生的

2015 年 3 月 8 日 00:00:00 - 2015 年 3 月 9 日 00:01:00 这是错误的,应该是 3 月 8 日, 2015 00:00:00 - 2015 年 3 月 8 日 23:59:59

我发现它是因为夏令时问题。如何解决这个问题?

我的代码是:

public static List<String> getDatesRange(long start, long end, String tzOffset) {
//tzOffset is 420. for USA

        TimeZone tz = TimeZone.getTimeZone(tzOffset);

        List<String> dates=new ArrayList();
        Calendar calendar = Calendar.getInstance(tz);

        calendar.set(Calendar.DAY_OF_WEEK, 1);
        calendar.set(Calendar.HOUR, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);


        while (start<end) {
            calendar.setTimeInMillis(start);
            long startTime = calendar.getTimeInMillis();
            int year= calendar.getWeekYear();


            long endTime = start + (1 * 24 * 3600 * 1000L);

            if(endTime<end) {
                endTime-=1000;
                System.out.println("Start Date= " + new Date(new Timestamp(start).getTime())+" ("+startTime+"), End Date= "+new Date(new Timestamp(endTime).getTime())+"("+endTime+")");

dates.add(startTime+"-"+endTime);
                start= endTime+1000;
            }
            else{
                System.out.println("Start Date= " + new Date(new Timestamp(start).getTime()) + " (" + startTime + "), End Date= " + new Date(new Timestamp(end).getTime()) + "(" + end + ")");
                start=end;
                dates.add(startTime+"-"+end);
            }
        }
        return dates;
    }

问题是您想要打印没有时区概念的日期(尽管开始日期取决于 tzOffset 参数)。如果您使用 Java 8,新的 Java 时间 API 有一个专门为此设计的 class:LocalDate.

所以你的方法可以通过首先根据时间戳和时区确定开始和结束日期然后摆脱所有时区考虑来完成。并通过在格式化程序中硬编码 start/end 时间来打印范围 "cheat"。

public static List<String> getDatesRange(long start, long end, String tzOffsetMinutes) {
  Instant startInstant = Instant.ofEpochMilli(start);
  Instant endInstant = Instant.ofEpochMilli(end);

  int offsetSeconds = Integer.parseInt(tzOffsetMinutes) * 60;
  ZoneOffset offset = ZoneOffset.ofTotalSeconds(offsetSeconds);
  LocalDate startDate = OffsetDateTime.ofInstant(startInstant, offset).toLocalDate();
  LocalDate endDate = OffsetDateTime.ofInstant(endInstant, offset).toLocalDate();

  List<String> dates = new ArrayList<> ();
  DateTimeFormatter fmt = DateTimeFormatter.ofPattern(
                                         "MMM d, yyyy 00:00:00 - MMM d, yyyy 23:59:59");

  for (LocalDate d = startDate; !d.isAfter(endDate); d = d.plusDays(1)) {
    dates.add(fmt.format(d));
  }
  return dates;
}

您或许可以对日历执行类似的操作。

有几件事我想知道:

  • 为什么要用 Longs 来定义日期?
  • 为什么 return 字符串列表而不是日期列表?
  • 在这种情况下为什么还要考虑时区?

不过,您可以简单地使用日历和 SimpleDateFormat 来实现此目的,如下所示:

public static Calendar getDayStart(final long timeInMillis) {
    final Calendar cal = Calendar.getInstance();
    // end time as a date
    cal.setTimeInMillis(timeInMillis);

    cal.set(Calendar.HOUR, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);

    return cal;
}

public static List<String> getDatesRange(final long start, final long end) {

    final Calendar cal = getDayStart(start);
    final Date startDate = cal.getTime();

    final Calendar calEnd = getDayStart(end);
    //adding one day because of the strict comparison in the while below
    calEnd.add(Calendar.DAY_OF_YEAR, 1);
    final Date endDate = calEnd.getTime();

    final SimpleDateFormat formatter = new SimpleDateFormat("MMM d, yyyy HH:mm:ss");

    final List<String> dates = new ArrayList<String>();
    final Date dayEnd;
    String currentDay = "";

    while(cal.getTime().before(endDate)) {
        currentDay = formatter.format(cal.getTime());
        currentDay += " - ";
        //going to the end of the day
        cal.add(Calendar.DAY_OF_YEAR, 1);
        cal.add(Calendar.SECOND, -1);
        currentDay += formatter.format(cal.getTime());
        //going to next day again and continue the loop
        cal.add(Calendar.SECOND, 1);
        //add what we computed to the list of days
        dates.add(currentDay);
    }

    return dates;
}