Short history of time

Tomasz Nurkiewicz

nurkiewicz@gmail.com | @tnurkiewicz

2013-07-06T11:40:00.000Z

Tomasz Nurkiewicz

nurkiewicz.blogspot.com

Bad day at Microsoft


blogs.msdn.com/b/windowsazure/archive/2012/03/09/summary-of-windows-azure-service-disruption-on-feb-29th-2012.aspx

Program result?

Java:


Calendar cal = new GregorianCalendar(2012, FEBRUARY, 29, 15, 0);
cal.add(YEAR, 1);
System.out.println(cal.getTime());
                    

C#:


DateTime cal = new DateTime(2012, 2, 29, 15, 0, 0).AddYears(1);
Console.WriteLine(cal);
                    
  • February 28th, 2013, 15:00
  • February 29th, 2013, 15:00
  • March 1st, 2013, 00:00
  • March 1st, 2013, 15:00
  • IllegalArgumentException

True story

“[...] in accordance with Terms and Conditions [...] interest is paid for actual number of days funds were on account [...] however it is assumed that year consists of 365 days.

Year 2012 has 366 days, thus
interest is not paid for 29th of February.

Yours faithfully, [some] Bank

samcik.blox.pl/2012/03/Dzien-ktorego-nie-ma-Sprawdz-co-bank-wykreslil.html

Bad second for Linux


www.greenprophet.com/2012/07/leap-second-bug-consumes-megawatts-of-electricity/

Leap seconds


en.wikipedia.org/wiki/Leap_second

Inne błędy

  • Problem of year 2000 (Y2K)
  • Problem of year 2011 (Taiwan)
  • Problem of year 2038 (Integer.MAX_VALUE)
  • Problem of year 2042 (IBM S/370)
  • Problem of year 2107 (MS-DOS FAT)
  • Problem of September 9th, '99 (9/9/99)

en.wikipedia.org/wiki/Time_formatting_and_storage_bugs

Understand the domain

...of every problem

Date representation


www.edali.org/persistence-of-memory.jsp
  • Seconds since arbitrary moment in time
  • Calendar system

Time axis

Time "0"?

Date
Used in
0. January 0 MATLAB
1. January 0 Symbian, Turbo DB
1. January 1 Microsoft .NET, Go
1. January 1601 NTFS, COBOL, Win32/Win64
1. January 1753 Microsoft SQL Server
31. December 1840 MUMPS
17. November 1858 VMS, United States Naval Observatory, DVB SI, astronomia
30. December 1899 Microsoft COM DATE, Object Pascal
0. January 1900 Microsoft Excel, Lotus 1-2-3
1. January 1900 NTP, IBM CICS, Mathematica, RISC OS, Common Lisp
1. January 1904 LabVIEW, Mac OS 9, Palm OS, MP4
1. January 1950 SEGA Dreamcast
Date
Used in
1. January 1960 S-Plus, SAS
31. December 1967 Pick OS
1. January 1970 Linux, Mac OS X, C, Java, JavaScript,
Perl, PHP, Python, Tcl, ActionScript
1. January 1978 AmigaOS
1. January 1980 DOS, OS/2, FAT16 I FAT32, VOS
6. January 1980 Qualcomm BREW, GPS
1. January 1981 Acorn NetFS
1. January 1984 CiA® CANopen®
22. August 1999 Satelita Galileo
1. January 2000 PostgreSQL, AppleSingle, AppleDouble
1. January 2001 Apple Cocoa

en.wikipedia.org/wiki/Epoch_date

java.util.Date

en.wikipedia.org/wiki/Calendar_date:
“A date in a calendar is a reference to a particular day represented within a calendar system. [...] A particular day may be represented by a different date in another calendar”

vs.

docs.oracle.com/javase/7/docs/api/java/util/Date.html:
“The class Date represents a specific instant in time, with millisecond precision.”

Time zones

java.util.Timezone

Time difference between Warsaw and Sydney?


www.travel.com.hk/region/timezone.htm

DST (Daylight saving time)


en.wikipedia.org/wiki/Daylight_saving_time

Daylight saving time

Winter → Summer Summer → Winter
en.wikipedia.org/wiki/Daylight_saving_time

...so?


www.travel.com.hk/region/timezone.htm

Representation


final TimeZone tz = TimeZone.getTimeZone("Europe/Warsaw");
                    

WRONG!


TimeZone.getTimeZone("GMT+01:00");

TimeZone.getTimeZone("Europe/warsaw");
                    

Calendar date

java.util.Calendar

Leap years - WRONG!


    def leapYear(year: Int): Boolean = 
        year % 4 == 0
                        

Leap years - poor


    def leapYear(year: Int): Boolean = 
        (year % 4 == 0 && year % 100 != 0) || 
        (year % 400 == 0)
                        

Leap years


    def leapYear(year: Int): Boolean = 
        new GregorianCalendar(year, JANUARY, 1).
            getActualMaximum(DAY_OF_YEAR) > 365
                        

Puzzle


Calendar c = Calendar.getInstance();

System.out.println(c.get(Calendar.YEAR));
                    

We have (Gregorian) year 2013


$ java ...
2013
                    

Meanwhile in Thailand...


$ java -Duser.country=TH -Duser.language=th ...
2556
                    

...and in Japan...


$ java -Duser.country=JP -Duser.language=ja -Duser.variant=JP ...
25
                    

Better:


Calendar c = new GregorianCalendar();
                    

Even better:


Calendar c = new GregorianCalendar(timeZone);
                    

Just in case...


Calendar c = new GregorianCalendar(timeZone, locale);
                    

Date vs. Calendar?

  • "...after some another event"
  • "...within 10 seconds"
  • "...within an hour"
  • "...within 24 hours"
  • "...within one day"
  • "...in 2013"
  • "...after 17:00"
  • "...on Friday"

Practice

Joda Time


final DateTime yearLater = new DateTime(2012, 2, 29, 15, 0).plusYears(1);
						
joda-time.sourceforge.net

Joda Time and JAX-WS


import org.joda.time.DateTime;
import javax.xml.bind.DatatypeConverter;

public class XsdJodaTimeConverter {
    public static DateTime unmarshal(String dateTime) {
        final long millis = DatatypeConverter.
                parseDate(dateTime).
                getTimeInMillis();
        return new DateTime(millis);
    }

    public static String marshal(DateTime calendar) {
        return DatatypeConverter.printDate(
                calendar.toGregorianCalendar()
        );
    }
}
                    

JAX-WS: .xjb file


<bindings version="1.0" xmlns="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <globalBindings>
        <javaType xmlType="xs:dateTime"
                  name="org.joda.time.DateTime"
                  parseMethod="XsdJodaTimeConverter.unmarshal"
                  printMethod="XsdJodaTimeConverter.marshal"/>
    </globalBindings>
</bindings>
					

Joda Time and JPA 2.1


import org.joda.time.Instant;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.util.Date;

@Converter(autoApply = true)
public class JodaTimeConverter implements AttributeConverter<Instant, Date> {

    @Override
    public Date convertToDatabaseColumn(Instant attr) {
        return attr != null? attr.toDate(): null;
    }

    @Override
    public Instant convertToEntityAttribute(Date dbData) {
        return dbData != null? new Instant(dbData): null;
    }
}
						

Testing - tools

Testing - edge cases

  • Beginning/end of month/year
  • Weekends
  • 29th of February
  • Time zones, DST

ScalaCheck and ScalaTest


implicit override val generatorDrivenConfig = 
	PropertyCheckConfig(minSuccessful = 10000, workers = 4)

test("any date +1 year and -1 year should yield same date back") {
    check { 
        random: Date => {
            val plusMinusYear = new GregorianCalendar
            plusMinusYear.setTime(random)
            plusMinusYear.add(YEAR, 1)
            plusMinusYear.add(YEAR, -1)
            random == plusMinusYear.getTime
        }
    }
}
                    

Negative result


Falsified after 2665 passed tests:
    arg0 = Mon Feb 29 03:21:22 CET 73843340
                    

Events in the future

Quartz scheduler


newTrigger() 
    .startAt(futureDate(1, YEAR))
    .build();
                    
quartz-scheduler.org/documentation/quartz-2.1.x/tutorials/tutorial-lesson-05

JMS with delay


MessageProducer producer = session.createProducer(destination);
TextMessage message      = session.createTextMessage("...hello, delayed!");
message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, oneYearMillis);
producer.send(message);
                    
http://activemq.apache.org/delay-and-schedule-message-delivery.html

Bonus / Computus


(defn easter [year]
  (let [
    a       (mod year 19)
    b       (Math/floor (/ year 100))
    c       (mod year 100)
    d       (Math/floor (/ b 4))
    e       (mod b 4)
    f       (Math/floor (/ (+ b 8) 25))
    g       (Math/floor (/ (inc (- b f)) 3))
    h       (mod (+ (- (+ (* 19 a) b) d g) 15) 30)
    i       (Math/floor (/ c 4))
    k       (mod c 4)
    L       (mod (- (+ 32 (* 2 e) (* 2 i)) h k) 7)
    m       (Math/floor (/ (+ a (* 11 h) (* 22 L)) 451))
    n       (- (+ h L 114) (* 7 m))
    month   (dec (Math/floor (/ n 31)))
    day     (inc (mod n 31))]
            (java.util.GregorianCalendar. year month day)))
                    
en.wikipedia.org/wiki/Computus

Bugs, more bugs...

  1. "Due to the lack of [time] synchronization [...] a car bomb went off [...] one hour earlier than expected" (catless.ncl.ac.uk/Risks/20.58.html#subj12)
  2. "F-22 Raptors [...] experienced multiple computer crashes coincident with their crossing of [...] the International Date Line" (en.wikipedia.org/wiki/List_of_software_bugs)
  3. "Damage to a German steel facility occurred during a DST transition" (en.wikipedia.org/wiki/Daylight_Savings_Time)
  4. www.wired.com/wiredenterprise/2012/07/leap-second-bug-wreaks-havoc-with-java-linux
  5. www.theregister.co.uk/2012/07/02/leap_second_crashes_airlines
  6. Unjustified fraud accusation (www.cs.tau.ac.il/~nachumd/horror.html - [37])
  7. Catalog of few hundred bugs, up to year 2000 (!) (www.csl.sri.com/users/neumann/cal.html)

Interesting and useful

  1. www.timeanddate.com - everything about time
  2. www.epochconverter.com - UNIX time
  3. www.odi.ch/prog/design/datetime.php - foundation for this presentation
  4. tycho.usno.navy.mil/systime.html - definitions of TAI, UT, UTC...
  5. www.bbc.co.uk/news/world-asia-16351377 - Samoa and Tokelau skip a day for dateline change

Thank you for your... time!


nurkiewicz@gmail.com
Twitter: @tnurkiewicz

nurkiewicz.github.io/talks/confitura2013