0

I met strange behavior in PHP:

(strtotime("2017-11-05 00:00:00") - strtotime("2017-11-06 00:00:00")) / 24//3600
(strtotime("2017-11-06 00:00:00") - strtotime("2017-11-05 00:00:00")) / 24//3750
(strtotime("2017-11-07 00:00:00") - strtotime("2017-11-06 00:00:00")) / 24//3600

As I discover, it related to time shift from summer to winter time (https://www.timeanddate.com/time/change/usa/new-york). Is it correct behavior? I can't see anything related to it in docs. Anybody have a link?

At least, in java it work as I expect:

System.out.println(((new Date(2017, 11, 6).getTime()) - (new Date(2017, 11, 5).getTime())) / 24);//3600000

And now a question. How I can define, that this day is 25 hours, and I should handle this date by special approach?

degr
  • 1,559
  • 1
  • 19
  • 37
  • 6
    Yes, it is correct behaviour: Certain days of the year have 25 hours, others have 23.... it all depends on your timezone, and whether daylight savings applies or not. – Mark Baker Nov 24 '17 at 11:26
  • 4
    The easiest way to handle it is to work purely with UTC timezone internally, and adjust to a user's timezone only for actual display or input – Mark Baker Nov 24 '17 at 11:27
  • Or you can check if you are in daylight saving time or not and adjust your code accordingly : `if (date('I', time())){ //In DST }else{ //Not in DST }` – Hamza Abdaoui Nov 24 '17 at 11:30
  • Thanks, `date('I'` looks good. – degr Nov 24 '17 at 11:33
  • 1
    Unix timestamps are UTC, but PHP adjusts their display based on the timezone setting in php.ini (or modified by code). It's generally a better idea to work with DateTime objects that are timezone-aware, and where you have much more control over how they handle timezone differences and the shifts to/from daylight savings – Mark Baker Nov 24 '17 at 11:33

1 Answers1

1

While I cannot speak to the PHP portion of your Question, I can correct your Java code.

tl;dr

Duration.between(
    LocalDate.of( 2017 , Month.NOVEMBER , 5 ).atStartOfDay( ZoneId.of( "America/New_York" ) ) , 
    LocalDate.of( 2017 , Month.NOVEMBER , 5 ).plusDays( 1 ).atStartOfDay( ZoneId.of( "America/New_York" ) ) 
).toString()

PT25H

Duration.between(
    LocalDate.of( 2017 , Month.NOVEMBER , 5 ).atStartOfDay( ZoneOffset.UTC ) , 
    LocalDate.of( 2017 , Month.NOVEMBER , 5 ).plusDays( 1 ).atStartOfDay( ZoneOffset.UTC ) 
).toString()

PT24H

A day in UTC is always 24-hours in length. A day in some time zones may be 23, 24, 25 or some other number of hours, and may even include a partial (fractional) hour.

Details

You are using the wrong classes for Java, and you were ignoring the crucial issue of time zone. The java.util.Date class is confusing, troublesome, and fortunately, now legacy. The modern java.time classes supplant those old date-time classes.

You ignore the crucial issue of time zone in your example code. That legacy Date class is always in UTC, but you care about America/New_York time zone. Your code is calculating 24 hours for a generic 24-hour day (UTC) while actually the 2017-11-05 in America/New_York is 25 hours rather than 24.

Your example code for Java makes no sense to me, as you are calculating a duration in milliseconds and then dividing by 24. I do not see how that division helps.

The number of milliseconds in a generic 24 hour day = ( 24 * 60 * 60 * 1,000 ) = 86400000. That is number returned by your code using java.util.Date if we eliminate the division by 24.

long millis = ((new Date(2017, 11, 6).getTime()) - (new Date(2017, 11, 5).getTime())) ;
System.out.println( millis ); 

86400000

See that code run live at IdeOne.com.

LocalDate

The LocalDate class represents a date-only value without time-of-day and without time zone.

LocalDate ld = LocalDate.of( 2017 , Month.NOVEMBER , 5 ) ;

Time zone

A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec. Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZoneId z = ZoneId.of( "America/New_York" ) ; 

ZonedDateTime

Let java.time determine the first moment of the day. Do not assume the day starts at 00:00. Anomalies such as Daylight Saving Time (DST) mean the day may start at a time such as 01:00.

ZonedDateTime start = ld.atStartOfDay( z ) ;

Usually the Half-Open approach is best for defining a span of time. In this approach the beginning is inclusive while the ending is exclusive. So a full day is defined as starting at the first moment of the day and running up to, but not including, the first moment of the following day.

ZonedDateTime stop = ld.plusDays( 1 ).atStartOfDay( z ) ;

Calculate the length of that day, November 5th in New York zone.

Duration d = Duration.between( start , stop ) ;

PT25H

So, yes, we see a day that is 25 hours long because of the DST cut-over.

See this code run live at IdeOne.com.

That output from Duration::toString is in standard ISO 8601 format.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154