de.frame4j.util
Class TimeHelper

java.lang.Object
  extended by de.frame4j.util.TimeHelper

@MinDoc(copyright="Copyright 2000 - 2004, 2009  A. Weinert",
        author="Albrecht Weinert",
        version="V.37",
        lastModified="10.04.2010",
        lastModifiedBy="A. Weinert",
        usage="import TimeHelper to your classes  or\n\n  java de.frame4j.util.TimeHelper [options] [time [time .. [loop]",
        purpose="common time and date utilities and constants, parser")
public abstract class TimeHelper
extends Object

Methods and (final) values for time / date handling.

This class features some methods — partly very powerful — for time and calendar tasks, as well as some relevant values.

The classes implementing the interface ConstTime rely on this class.

© Copyright 2000 - 2004, 2009   Albrecht Weinert

See Also:
ComVar, TimeRO, ConstTime, ModifTime

Field Summary
static Action ACTION_TZ0
          Action for time zone offset 0 , UTC.
static int CET_BASIC_OFFSET
          (Base-) offset in minutes of time zone CET / MEZ to UTC .
static int DAYSin4YEARS
          Days in four years (implying one of it being a leap year).
static int EU_END_MONTH
          Ending month for summer time in EU (October = 10).
static int EU_START_MONTH
          Starting month for summer time in EU (March = 3).
static int EU_START_YEAR
          First year to apply EU summer time rules.
static int EU_SWITCH_HOUR
          Start- and and hour for summer time in EU (02:00).

Remark: At the end of summer time, the politicians did not choose to prolong the end of Saturday having 24:00:00.000 to 24:59:59.000 as an uniquely numbered extra time of day.
The choice of using Sunday 02:00:00 instead, gives us the time 02:00:00.000 to 02:59:59.000 twice.
static int EU_SWITCH_WEEKDAY
          Start and end day of week for summer time in EU (Sunday = 0).
static ThreadLocal<ModifTime> helpTimeHolder
          Time object for helpers.
static Action[] RATE_CHOOSE
          Action list for duration or rates.
static Action[] TIME_CHOOSE
          Action list for date and time of day.
 
Method Summary
static StringBuilder appendDuration(StringBuilder bastel, long dur)
          Format a duration or rate.
static int daysSince1Mrz68(int d, int m, int j)
          Calculate the days since 1 March 1968 .

static Appendable displTimeZones(StringBuilder bastel)
          Generate a display of all pre-fabricated time zones.
static String formatSQL(long time)
          Formatting of date and time in SQL format.
static boolean haveSameRules(TimeZone a, TimeZone b)
          Do two time zones have the same rules.
static boolean isLeapYear(int y)
          Leap year.
static long javaTimetoMS(long javaTime)
          Transform Java time (long, ms to Microsoft time stamp (long, ns).
static void main(String[] args)
          Display some info about class, time zones, explore the parser etc.
static long msToJavaTime(long msTimeStamp)
          Transform Microsoft time stamp (long, ns) to Java time (long, ms).
static long parse(CharSequence time)
          Get / parse a time from String representation.
static long parse(CharSequence time, boolean endDay)
          Get / parse a time from String representation.
static long parseDuration(CharSequence rate)
          Parsing a rate (or duration).
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

CET_BASIC_OFFSET

public static final int CET_BASIC_OFFSET
(Base-) offset in minutes of time zone CET / MEZ to UTC .

Adding this offset multiplied by 60000 to the absolute time in ms gives an absolute local time (in ms). The value for CET (MEZ) is +60 (meaning +1 hour). This holds for standard or winter time.

Value: 60

See Also:
Constant Field Values

EU_START_YEAR

public static final int EU_START_YEAR
First year to apply EU summer time rules.

Since 1980 all European Union countries (+ some others) obey to unified daylight saving time rules. They can be described by five parameters.

Value: 1980

See Also:
EU_START_MONTH, EU_END_MONTH, EU_SWITCH_WEEKDAY, EU_SWITCH_HOUR, Constant Field Values

EU_START_MONTH

public static final int EU_START_MONTH
Starting month for summer time in EU (March = 3).

Value: 3

See Also:
EU_START_YEAR, Constant Field Values

EU_END_MONTH

public static final int EU_END_MONTH
Ending month for summer time in EU (October = 10).

Value: 10

See Also:
EU_START_YEAR, Constant Field Values

EU_SWITCH_WEEKDAY

public static final int EU_SWITCH_WEEKDAY
Start and end day of week for summer time in EU (Sunday = 0).

The switching occurs on the last EU_SWITCH_WEEKDAY at EU_SWITCH_HOUR:00:00.000 within the EU_START_MONTH and EU_END_MONTH.
Value: 0

See Also:
EU_START_YEAR, Constant Field Values

EU_SWITCH_HOUR

public static final int EU_SWITCH_HOUR
Start- and and hour for summer time in EU (02:00).

Remark: At the end of summer time, the politicians did not choose to prolong the end of Saturday having 24:00:00.000 to 24:59:59.000 as an uniquely numbered extra time of day.
The choice of using Sunday 02:00:00 instead, gives us the time 02:00:00.000 to 02:59:59.000 twice. There is no uniform (standardised, supported) way to handle this in parsing and formatting.

One aspect of the problem is:
If you see (in an EU time zone) "25.10.2009 01:31:53" or "25.10.2009 03:31:53", you implicitly know if this is daylight saving by the algorithm (EU rule; yes and no in this case).
But in the case of "25.10.2009 02:31:53" you don't. Some process control systems than use something like "25.10.2009 02A:31:53" and "25.10.2009 02B:31:53" for logging. Other field of applications use other habits, most of them being an nightmare for parsing, (String sorting) and formating, and, of course, none of them really supported by Java, PHP or whatever.

Value: 2

See Also:
EU_START_YEAR, Constant Field Values

DAYSin4YEARS

public static final int DAYSin4YEARS
Days in four years (implying one of it being a leap year).

Value: 1461

See Also:
Constant Field Values

helpTimeHolder

public static final ThreadLocal<ModifTime> helpTimeHolder
Time object for helpers.

This ThreadLocal's method get() returns a ModifTime object that may be freely used in the calling thread without any further synchronisation.
The ModifTime object returned by get() is a TimeST with default (local) time zone.


RATE_CHOOSE

public static final Action[] RATE_CHOOSE
Action list for duration or rates.

Languages:

See Also:
parseDuration(CharSequence)

ACTION_TZ0

public static final Action ACTION_TZ0
Action for time zone offset 0 , UTC.

Action to set time zone (raw) offset to 0; like in UTC.


TIME_CHOOSE

public static final Action[] TIME_CHOOSE
Action list for date and time of day.

See Also:
parse(CharSequence), main(String[])
Method Detail

formatSQL

public static String formatSQL(long time)
Formatting of date and time in SQL format.

This method formats the point of time defined by the parameter in the way defined by ConstTime.toSqlString().

Hint: This method uses the thread local helper object returned by helpTimeHolder.get(). Re-using this helper object is explicitly recommended.


daysSince1Mrz68

public static int daysSince1Mrz68(int d,
                                  int m,
                                  int j)
Calculate the days since 1 March 1968 .

Parameters:
d - day in month, 1 .. max, but not restricted
    (using d-=100 before would go 100 days before the given date).
m - Month, 1..12, (other values are illegal and default to March of year j or year j-1)
j - full year like 1871, 1953, 1998, 2009, ...)

parse

public static long parse(CharSequence time)
                  throws IllegalArgumentException
Get / parse a time from String representation.

Interpret a String as date / time of day (overview)

This method tries to interpret the String parameter as time, that is date and / or time of day — h:m:s, h:m or h:m:s.ms —. No time of day acts like 00:00:00.000

parse(CharSequence) and parse(CharSequence, boolean) understands a big variety of (syntactically) different formats. Likewise time zone offsets are understood in a variety of forms. The parsed formats include those usual in (North-) America, Europe and Germany, as well as some Internet and operating system formats as well as those usual in version control systems (cvs[NT] and SVN; see CVSkeys).

Should, in spite of this flexibility, all interpretation of the parameter s fail, an clearly worded IllegalArgumentException is thrown. In the case of success, the absolute UTC/Java milliseconds (1.1.1968 based) are returned (see getMillis()). This class as application (see main(String[])) can be used to explore the time parser.

Without any time zone settings in the parameter, the default zone of the underlying runtime is assumed.

Many three or four letter time zone abbreviations are recognised, like
UTC, GMT / WET, CET / MEZ, BST / IST / WEST, CEST / MES, MST / PST, EDT, CDT, MDT / PDT, HKT, VET and many more.
Furthermore every time zone may be given as offset to UTC by + or - in the style of "Sat, 12 Aug 1995 13:30:00 +0430" (4 hours 30 minutes west of Greenwich) or "Sat, 12 Aug 1995 13:30:00 +04:30".
A prefix GMT, UT or UTC
("Sat, 12 Aug 1995 13:30:00 GMT+0430")
or a prefix signifying the same offset (as often used by Internet applications, like CET+01:00) is allowed.

Besides the numeric formats many keywords in English, French and German (so far, and partly other languages) are correctly interpreted for setting dates and or times like:
"Tagesende", "end-of-day", "fin-du-jour", "Mittag", "noon", "midi", "Mitternacht", "midnight", "minuit", "heute", "today", "aujourd'hui", "morgen", "tomorrow", "demain", "gestern", "yesterday", "hier", "vorgestern", "beforeyesterday avant-hier", "vorvorgestern", "jetzt", "now", "maintenant and some more.
All those keywords are not case sensitive and they may be abbreviated, like "mañana" or "mañ", "mañ." as long as no ambiguity arises by that.
Running this class as application without programm parameters (java de.frame4j.utils.TimeHelper) shows all understood time keywords of Frame4J's installed version.

Background

This method may a bit remind to the less flexible, yet extremely useful long parse(String)
in java.util.Date.

java.util.Date.parse(String) was, since JDK1.1, deprecated, without giving a real substitute — which java.text.DateFormat clearly is not with respect to parsing.

This desideratum alone was inducement for the development of de.a_weinert.Zeithelper.parse() — the predecessor in use for years. This method has all abilities (but not all faults) of the then deprecated Date.parse(), featuring much more flexibility.

Two fundamental assumptions — both wrong — govern all Java format (java.text-) classes, inappropriately offered as substitute for the deprecated Date.parse(): In the case of formating, of course, one often wants to control every field of the output, its sequence, its language and so on (see Time.toString(CharSeq/String)). Controlling the output "to the last letter" means defining a unique format or pattern. On the other hand, at parsing, you are perfectly within your rights when expecting, that all unambiguous notations for the same values or states are understood:
"15. Januar 2009 Mittag" and "2009-1-15 12:00" denote the same date and time of day and should be (are here) understood so without switching formats or locales.

Some accepted / parsed formats (examples)

  Sat, 12 Aug 1995 13:21:00.321 GMT+1
  Samst., 12.8.95 13:21 MEZ
  Samstag 12. aug. 95 13:21 MEZ
  Samedi, août 12 95 13:21 CET
  Sat. 8/12/95 13:21 GMT+0100
  Sat. 8/12/95 13:21 +0100
  Aug. 12 95 11 PM +1
  12 Aug 95 11 PM +1
  Dienstag 13:46
  jetzt
  vorgestern
  gestern mittag
  now
  yesterday noon
  2200-02-28T09:30:11.100+01:00
  2200-02-28T08:30:11.100Z
This is all understood as well as the same in some other sequences, parts of it and so on.

Names of months, days of week and others are (so far) accepted in the above mentioned languages, full length abbreviated and with and without accents. Ambiguous abbreviations are illegal.

Days of week have to be written correctly or abbreviated in a way not to be mixed up with abbreviated months or times zones; Saturday is wrong. If a date (12.7.03) is given, nevertheless, the day of week is ignored as redundant information. If there is no date, a given day of week denotes the latest possible day in the past, i.e. 1 to 7 days back. This fits to the habit of some file and mail programmes, to omit the date and just give the day of week, if the mail or the modification date is less than eight days back.

All key word are case insensitive (CET cet CeT).

Operation

The String s is processed left to right. White spaces, underline _ and comma as well as all within braces ( ) has no significance besides the separation of fields. Brace pairs can be embedded ( Co(mme)nt ). This is a reminiscence (i.e. compatibility) to the late Date.parse(), that — why on earth — did so.

Any sequence of digits 1..9 is interpreted as number.
Any sequence of character s up to the next separator or digit or sign (+ -) is regarded as keyword, naming a month, a day of week, a relative date, a time of day or a time zone offset.

This is used to sift out on one hand and, on the other side Besides a perhaps ignored day of week, no part may explicitly or implicitly given more than once and contradictory.

The interpretation of a number is ruled by surrounding signs and indications already made; i.e. sequence and partly magnitude.
Points (.) lead to German standard interpretation day.month.year tag.monat.jahr (in this sequence if no other info was present).
An unsigned number > 62 (except following seconds) will be taken as year. (Limit of 62 is taking leap seconds into consideration). So a year can have any position (2003/04/24).

Colons (:) lead to time of day interpretation hour:minute:second or hour:minute:second.millisecond .
An AM or vormittags is ignored if an hour 1..12 is given.
A PM or nachmittags is accepted if the hour is in the range 1..12, for 1..11 then 12 will be added to have (internally) the non-ambiguous 0..23 range.

If no year was given signs + - lead to the interpretation as year (- as BC).
If a year was given signs + - lead to the interpretation as time zone offset in the range of -16 .. +16 (hours) or in the four digit hour-minute form (with or without separating colon :) that allow for exotic half (and even quarter) hour offsets or in the two digit hour form. Time zone offsets can be made redundantly by digits and time zone abbreviations as long as they are not contradictory.

Besides those cases of AM, time zones and the ignored day of week, all else redundancy like "12:00:00 noon" is an error, as are most range violations. All errors give an expressive IllegalArgumentException.

Keywords are understood in English, French, German, Italian and Spanish (until January 2009). Spaces in keywords have to be replaced by hyphen and accents may be omitted, examples: "l'après-midi" and "l'apres-midi", "end-of-day", "août", "aout"
Names of months and day of week are also understood in Portuguese and Dutch.


This method is equivalent to parse(time, false).

To do: Try to understand the non human readable ISO 8601 formats without most field separators like 20991231T235959.9942 .

Hint: Executing this class as application by
    java de.frame4j.util.TimeHelper
lists all understood keywords and names; see main(String[]).
Doing so with arguments tries to parse those as time.


Parameters:
time - the string to be interpreted as a indication of time
Returns:
the absolute milliseconds since 1.1.1970 00:00:00 UTC.
Throws:
IllegalArgumentException, - if time can't be parsed.
IllegalArgumentException
See Also:
ModifTime.setMillis(long), set(int,,,), Action, TextHelper

parse

public static long parse(CharSequence time,
                         boolean endDay)
                  throws IllegalArgumentException
Get / parse a time from String representation.

Description

This method is equivalent to parse(CharSequence) with the extra possibility to specify the end of the day (near midnight) as default if no time of day is given.
So with this method you can choose the default time of day either as 00:00:00.0 (endDay false) or 23:59:59.999 (endDay true).

Non regarding a leap second, the intervals "beginDay..endDay" of two consecutive days adjoin without gap.

Hint: Having a keyword Tagesende, end-of-day oder fin-du-jour in the parameter String would have the same effect as endDay == true without giving time of day time.

Parameters:
endDay - true: no time of day in time defaults to 23:59:59.999
time - the string to be interpreted as a indication of time
Returns:
the absolute milliseconds since 1.1.1970 00:00:00 UTC.
Throws:
IllegalArgumentException, - if time can't be parsed.
IllegalArgumentException
See Also:
Action.selectedBy(CharSequence, boolean)

parseDuration

public static long parseDuration(CharSequence rate)
                          throws IllegalArgumentException
Parsing a rate (or duration).

Syntax: [white space] number [white space] [unit] [white space]
Or: [white space] keyword [white space]

Unit: ms (default), s, m, h, d, w
for milliseconds (default) seconds, minutes, hours, days or weeks.
Keyword: "sekündlich", "secondly", "minütlich", "minutely", "stündlich", "hourly", "horaire", "ogni-ora", "por-hora", " cada-hora", "täglich", "dayly", "quotidien", "giornaliero", "por-día", "por-dia", "wöchendlich", "weekly", "hebdomadaire", "settimanale", "semanal".

Number: Defined by Long.decode().

Hint: months and years don't give a unique duration in the sense of a constant frequency.

Parameters:
rate - the duration or rate
Returns:
rate in ms.
Throws:
IllegalArgumentException - if syntax problems.

appendDuration

public static StringBuilder appendDuration(StringBuilder bastel,
                                           long dur)
Format a duration or rate.

Returned syntax/form is: 2w3d5h12m30,123s or 987ms
Not applicable parts are omitted; a negative parameter will get a leading - .

Hint: The form delivered by this method appendDuration(StringBuilder, long) is not understood by the parsing method parseDuration(CharSequence).

Parameters:
bastel - the StringBuilder to append to. If null, it will be generated with starting capacity 56.
dur - duration or rate to format in ms
Returns:
bastel

msToJavaTime

public static long msToJavaTime(long msTimeStamp)
Transform Microsoft time stamp (long, ns) to Java time (long, ms).

Microsoft's long time stamp in in nanoseconds and differs in start time.

See Also:
javaTimetoMS(long)

javaTimetoMS

public static long javaTimetoMS(long javaTime)
Transform Java time (long, ms to Microsoft time stamp (long, ns).

See Also:
msToJavaTime(long)

isLeapYear

public static boolean isLeapYear(int y)
Leap year.

This method applies Gregorian rules for y > 1000.


haveSameRules

public static boolean haveSameRules(TimeZone a,
                                    TimeZone b)
Do two time zones have the same rules.

This method gives the result, that the documentation of TimeZone.hasSameRules() describes.
Background: Most, usually buggy, implementations in TimeZone return false, even if all described conditions are perfectly fulfilled. Hence this substitute.

Returns:
true if same offset and using daylight saving

displTimeZones

public static Appendable displTimeZones(StringBuilder bastel)
Generate a display of all pre-fabricated time zones.

For there use in some methods this class features some pre-fabricated zones. This method generates a readable and detailed list.

Parameters:
bastel - StringBuilder to append to, generated if null
Returns:
bastel
See Also:
main(String[])

main

public static void main(String[] args)
Display some info about class, time zones, explore the parser etc.

If no arguments are given, this application displays info about time zones and the keywords defined or understood by this classes parsers.

Any given arguments are used to demonstrate the date/time parser.

Parameters:
args - date / time string to parse and redisplay
See Also:
parse(CharSequence)