src.org.python.modules.time.Time Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jython-standalone Show documentation
Show all versions of jython-standalone Show documentation
Jython is an implementation of the high-level, dynamic, object-oriented
language Python written in 100% Pure Java, and seamlessly integrated with
the Java platform. It thus allows you to run Python on any Java platform.
// Copyright (c) Corporation for National Research Initiatives
// An implementation of the Python standard time module. Currently
// unimplemented:
//
// accept2dyear
//
// There may also be some incompatibilities in strftime(), because the Java
// tools for creating those formats don't always map to C's strftime()
// function.
//
// see org/python/modules/time.java for previous history.
package org.python.modules.time;
import java.text.DateFormatSymbols;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import org.python.core.ClassDictInit;
import org.python.core.Py;
import org.python.core.PyBuiltinFunctionSet;
import org.python.core.PyException;
import org.python.core.PyInteger;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PyTuple;
import org.python.core.__builtin__;
class TimeFunctions extends PyBuiltinFunctionSet
{
public TimeFunctions(String name, int index, int argcount) {
super(name, index, argcount);
}
public PyObject __call__() {
switch (index) {
case 0:
return Py.newFloat(Time.time());
case 1:
return Py.newFloat(Time.clock());
default:
throw info.unexpectedCall(0, false);
}
}
}
public class Time implements ClassDictInit
{
public static PyString __doc__ = new PyString(
"This module provides various functions to manipulate time values.\n"+
"\n"+
"There are two standard representations of time. One is the "+
"number\n"+
"of seconds since the Epoch, in UTC (a.k.a. GMT). It may be an "+
"integer\n"+
"or a floating point number (to represent fractions of seconds).\n"+
"The Epoch is system-defined; on Unix, it is generally "+
"January 1st, 1970.\n"+
"The actual value can be retrieved by calling gmtime(0).\n"+
"\n"+
"The other representation is a tuple of 9 integers giving "+
"local time.\n"+
"The tuple items are:\n"+
" year (four digits, e.g. 1998)\n"+
" month (1-12)\n"+
" day (1-31)\n"+
" hours (0-23)\n"+
" minutes (0-59)\n"+
" seconds (0-59)\n"+
" weekday (0-6, Monday is 0)\n"+
" Julian day (day in the year, 1-366)\n"+
" DST (Daylight Savings Time) flag (-1, 0 or 1)\n"+
"If the DST flag is 0, the time is given in the regular time zone;\n"+
"if it is 1, the time is given in the DST time zone;\n"+
"if it is -1, mktime() should guess based on the date and time.\n"+
"\n"+
"Variables:\n"+
"\n"+
"timezone -- difference in seconds between UTC and local "+
"standard time\n"+
"altzone -- difference in seconds between UTC and local DST time\n"+
"daylight -- whether local time should reflect DST\n"+
"tzname -- tuple of (standard time zone name, DST time zone name)\n"+
"\n"+
"Functions:\n"+
"\n"+
"time() -- return current time in seconds since the Epoch "+
"as a float\n"+
"clock() -- return CPU time since process start as a float\n"+
"sleep() -- delay for a number of seconds given as a float\n"+
"gmtime() -- convert seconds since Epoch to UTC tuple\n"+
"localtime() -- convert seconds since Epoch to local time tuple\n"+
"asctime() -- convert time tuple to string\n"+
"ctime() -- convert time in seconds to string\n"+
"mktime() -- convert local time tuple to seconds since Epoch\n"+
"strftime() -- convert time tuple to string according to "+
"format specification\n"+
"strptime() -- parse string to time tuple according to "+
"format specification\n"
);
public static void classDictInit(PyObject dict) {
dict.__setitem__("time", new TimeFunctions("time", 0, 0));
dict.__setitem__("clock", new TimeFunctions("clock", 1, 0));
dict.__setitem__("struct_time", PyTimeTuple.TYPE);
dict.__setitem__("__name__", Py.newString("time"));
// calculate the static variables tzname, timezone, altzone, daylight
TimeZone tz = TimeZone.getDefault();
tzname = new PyTuple(new PyString(tz.getDisplayName(false, 0)),
new PyString(tz.getDisplayName(true, 0)));
daylight = tz.useDaylightTime() ? 1 : 0;
timezone = -tz.getRawOffset() / 1000;
altzone = timezone - tz.getDSTSavings() / 1000;
}
public static double time() {
return System.currentTimeMillis()/1000.0;
}
/**
* @return - the seconds elapsed since the first call to this function
*/
public static double clock() {
// Check against an explicit initialization variable, clockInitialized,
// rather than a value of initialClock since the initial call to
// System.nanoTime can yield anything and that could lead to initialTime
// being set twice.
if(!clockInitialized) {
initialClock = System.nanoTime();
clockInitialized = true;
return 0;
}
return (System.nanoTime() - initialClock) / NANOS_PER_SECOND;
}
private static final double NANOS_PER_SECOND = 1000000000.0;
private static long initialClock;
private static volatile boolean clockInitialized;
private static void throwValueError(String msg) {
throw new PyException(Py.ValueError, new PyString(msg));
}
private static int item(PyTuple tup, int i) {
// knows about and asserts format on tuple items. See
// documentation for Python's time module for details.
int val = tup.__getitem__(i).asInt();
boolean valid = true;
switch (i) {
case 0: break; // year
case 1: valid = (0 <= val && val <= 12); break; // month 1-12 (or 0)
case 2: valid = (0 <= val && val <= 31); break; // day 1 - 31 (or 0)
case 3: valid = (0 <= val && val <= 23); break; // hour 0 - 23
case 4: valid = (0 <= val && val <= 59); break; // minute 0 - 59
case 5: valid = (0 <= val && val <= 61); break; // second 0 - 59 (plus 2 leap seconds)
case 6: valid = (0 <= val && val <= 6); break; // weekday 0 - 6
case 7: valid = (0 <= val && val < 367); break; // julian day 1 - 366 (or 0)
case 8: valid = (-1 <= val && val <= 1); break; // d.s. flag, -1,0,1
}
// raise a ValueError if not within range
if (!valid) {
String msg;
switch (i) {
case 1:
msg = "month out of range (1-12)";
break;
case 2:
msg = "day out of range (1-31)";
break;
case 3:
msg = "hour out of range (0-23)";
break;
case 4:
msg = "minute out of range (0-59)";
break;
case 5:
msg = "second out of range (0-59)";
break;
case 6:
msg = "day of week out of range (0-6)";
break;
case 7:
msg = "day of year out of range (1-366)";
break;
case 8:
msg = "daylight savings flag out of range (-1,0,1)";
break;
default:
// make compiler happy
msg = "ignore";
break;
}
throwValueError(msg);
}
switch (i) {
case 1:
if (val > 0) {
// Java's months are usually 0-11
val--;
}
break;
case 2:
case 7:
// 'or 0'
if (val == 0) {
val = 1;
}
break;
}
return val;
}
private static GregorianCalendar _tupletocal(PyTuple tup) {
return new GregorianCalendar(item(tup, 0),
item(tup, 1),
item(tup, 2),
item(tup, 3),
item(tup, 4),
item(tup, 5));
}
public static double mktime(PyTuple tup) {
GregorianCalendar cal;
try {
cal = _tupletocal(tup);
}
catch (PyException e) {
// CPython's mktime raises OverflowErrors... yuck!
e.type = Py.OverflowError;
throw e;
}
int dst = item(tup, 8);
if(dst == 0 || dst == 1) {
cal.set(Calendar.DST_OFFSET, dst * cal.getTimeZone().getDSTSavings());
}
return cal.getTime().getTime()/1000.0;
}
protected static PyTimeTuple _timefields(double secs, TimeZone tz) {
GregorianCalendar cal = new GregorianCalendar(tz);
cal.clear();
secs = secs * 1000;
if (secs < Long.MIN_VALUE || secs > Long.MAX_VALUE) {
throw Py.ValueError("timestamp out of range for platform time_t");
}
cal.setTime(new Date((long)secs));
int isdst = tz.inDaylightTime(cal.getTime()) ? 1 : 0;
return toTimeTuple(cal, isdst);
}
private static PyTimeTuple toTimeTuple(Calendar cal, int isdst) {
int dow = cal.get(Calendar.DAY_OF_WEEK) - 2;
if (dow < 0) {
dow += 7;
}
return new PyTimeTuple(new PyInteger(cal.get(Calendar.YEAR)),
new PyInteger(cal.get(Calendar.MONTH) + 1),
new PyInteger(cal.get(Calendar.DAY_OF_MONTH)),
new PyInteger(cal.get(Calendar.HOUR)
+ 12 * cal.get(Calendar.AM_PM)),
new PyInteger(cal.get(Calendar.MINUTE)),
new PyInteger(cal.get(Calendar.SECOND)),
new PyInteger(dow),
new PyInteger(cal.get(Calendar.DAY_OF_YEAR)),
new PyInteger(isdst));
}
/**
* Convert a time argument that may be an optional float or None value to a
* double. Throws a TypeError on failure.
*
* @param arg a PyObject number of None
* @return a double value
*/
public static double parseTimeDoubleArg(PyObject arg) {
if (arg == Py.None) {
return time();
}
Object result = arg.__tojava__(Double.class);
if (result == Py.NoConversion) {
throw Py.TypeError("a float is required");
}
return (Double)result;
}
public static PyTuple localtime() {
return localtime(Py.None);
}
public static PyTuple localtime(PyObject secs) {
return _timefields(parseTimeDoubleArg(secs), TimeZone.getDefault());
}
public static PyTuple gmtime() {
return gmtime(Py.None);
}
public static PyTuple gmtime(PyObject arg) {
return _timefields(parseTimeDoubleArg(arg), TimeZone.getTimeZone("GMT"));
}
public static PyString ctime() {
return ctime(Py.None);
}
public static PyString ctime(PyObject secs) {
return asctime(localtime(secs));
}
// Python's time module specifies use of current locale
protected static Locale currentLocale = null;
protected static DateFormatSymbols datesyms = new DateFormatSymbols();
protected static String[] shortdays = null;
protected static String[] shortmonths = null;
private static String[] enshortdays = new String[] {"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat",
"Sun"};
private static String[] enshortmonths = new String[] {"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"};
private synchronized static String _shortday(int dow) {
// we need to hand craft shortdays[] because Java and Python have
// different specifications. Java (undocumented) appears to be
// first element "", followed by 0=Sun. Python says 0=Mon
if (shortdays == null) {
shortdays = new String[7];
String[] names = datesyms.getShortWeekdays();
for (int i = 0; i < 6; i++) {
shortdays[i] = names[i + 2];
}
shortdays[6] = names[1];
}
try {
return shortdays[dow];
} catch (ArrayIndexOutOfBoundsException e) {
throw new PyException(Py.ValueError, new PyString("day of week out of range (0-6)"));
}
}
private synchronized static String _shortmonth(int month0to11) {
// getShortWeekdays() returns a 13 element array with the last item
// being the empty string. This is also undocumented ;-/
if (shortmonths == null) {
shortmonths = new String[12];
String[] names = datesyms.getShortMonths();
for (int i = 0; i < 12; i++) {
shortmonths[i] = names[i];
}
}
try {
return shortmonths[month0to11];
} catch (ArrayIndexOutOfBoundsException e) {
throw new PyException(Py.ValueError, new PyString("month out of range (1-12)"));
}
}
private static String _padint(int i, int target) {
String s = Integer.toString(i);
int sz = s.length();
if (target <= sz)
// no truncation
return s;
if (target == sz+1)
return "0"+s;
if (target == sz+2)
return "00"+s;
else {
char[] c = new char[target-sz];
Arrays.fill(c, '0');
return new String(c) + s;
}
}
private static String _twodigit(int i) {
return _padint(i, 2);
}
private static String _truncyear(int year) {
String yearstr = _padint(year, 4);
return yearstr.substring(yearstr.length()-2, yearstr.length());
}
public static PyString asctime() {
return asctime(localtime());
}
public static PyString asctime(PyTuple tup) {
StringBuilder buf = new StringBuilder(25);
buf.append(enshortdays[item(tup, 6)]).append(' ');
buf.append(enshortmonths[item(tup, 1)]).append(' ');
int dayOfMonth = item(tup, 2);
if(dayOfMonth < 10){
buf.append(' ');
}
buf.append(dayOfMonth).append(' ');
buf.append(_twodigit(item(tup, 3))).append(':');
buf.append(_twodigit(item(tup, 4))).append(':');
buf.append(_twodigit(item(tup, 5))).append(' ');
return new PyString(buf.append(item(tup, 0)).toString());
}
public static String locale_asctime(PyTuple tup) {
checkLocale();
int day = item(tup, 6);
int mon = item(tup, 1);
return _shortday(day) + " " + _shortmonth(mon) + " " +
_twodigit(item(tup, 2)) + " " +
_twodigit(item(tup, 3)) + ":" +
_twodigit(item(tup, 4)) + ":" +
_twodigit(item(tup, 5)) + " " +
item(tup, 0);
}
public static void sleep(double secs) {
try {
java.lang.Thread.sleep((long)(secs * 1000));
}
catch (java.lang.InterruptedException e) {
throw new PyException(Py.KeyboardInterrupt, "interrupted sleep");
}
}
// set by classDictInit()
public static int timezone;
public static int altzone = -1;
public static int daylight;
public static PyTuple tzname = null;
// TBD: should we accept 2 digit years? should we make this attribute
// writable but ignore its value?
public static final int accept2dyear = 0;
public static PyString strftime(String format) {
return strftime(format, localtime());
}
public static PyString strftime(String format, PyTuple tup) {
checkLocale();
// Immediately validate the tuple
int[] items = new int[9];
for (int i = 0; i < 9; i ++) {
items[i] = item(tup, i);
}
String s = "";
int lastc = 0;
int j;
String[] syms;
GregorianCalendar cal = null;
while (lastc < format.length()) {
int i = format.indexOf("%", lastc);
if (i < 0) {
// the end of the format string
s = s + format.substring(lastc);
break;
}
if (i == format.length() - 1) {
// there's a bare % at the end of the string. Python lets
// this go by just sticking a % at the end of the result
// string
s = s + "%";
break;
}
s = s + format.substring(lastc, i);
i++;
switch (format.charAt(i)) {
case 'a':
// abbrev weekday
j = items[6];
s = s + _shortday(j);
break;
case 'A':
// full weekday
// see _shortday()
syms = datesyms.getWeekdays();
j = items[6];
if (0 <= j && j < 6)
s = s + syms[j+2];
else if (j== 6)
s = s + syms[1];
else
throwValueError("day of week out of range (0 - 6)");
break;
case 'b':
// abbrev month
j = items[1];
s = s + _shortmonth(j);
break;
case 'B':
// full month
syms = datesyms.getMonths();
j = items[1];
s = s + syms[j];
break;
case 'c':
s = s + locale_asctime(tup);
break;
case 'd':
// day of month (01-31)
s = s + _twodigit(items[2]);
break;
case 'H':
// hour (00-23)
s = s + _twodigit(items[3]);
break;
case 'I':
// hour (01-12)
j = items[3] % 12;
if (j == 0)
j = 12; // midnight or noon
s = s + _twodigit(j);
break;
case 'j':
// day of year (001-366)
s = s + _padint(items[7], 3);
break;
case 'm':
// month (01-12)
s = s + _twodigit(items[1] + 1);
break;
case 'M':
// minute (00-59)
s = s + _twodigit(items[4]);
break;
case 'p':
// AM/PM
j = items[3];
syms = datesyms.getAmPmStrings();
if (0 <= j && j < 12)
s = s + syms[0];
else if (12 <= j && j < 24)
s = s + syms[1];
else
throwValueError("hour out of range (0-23)");
break;
case 'S':
// seconds (00-61)
s = s + _twodigit(items[5]);
break;
case 'U':
// week of year (sunday is first day) (00-53). all days in
// new year preceding first sunday are considered to be in
// week 0
if (cal == null)
cal = _tupletocal(tup);
cal.setFirstDayOfWeek(Calendar.SUNDAY);
cal.setMinimalDaysInFirstWeek(7);
j = cal.get(Calendar.WEEK_OF_YEAR);
if (cal.get(Calendar.MONTH) == Calendar.JANUARY && j >= 52)
j = 0;
s = s + _twodigit(j);
break;
case 'w':
// weekday as decimal (0=Sunday-6)
// tuple format has monday=0
j = (items[6] + 1) % 7;
s = s + j;
break;
case 'W':
// week of year (monday is first day) (00-53). all days in
// new year preceding first sunday are considered to be in
// week 0
if (cal == null)
cal = _tupletocal(tup);
cal.setFirstDayOfWeek(Calendar.MONDAY);
cal.setMinimalDaysInFirstWeek(7);
j = cal.get(Calendar.WEEK_OF_YEAR);
if (cal.get(Calendar.MONTH) == Calendar.JANUARY && j >= 52)
j = 0;
s = s + _twodigit(j);
break;
case 'x':
// TBD: A note about %x and %X. Python's time.strftime()
// by default uses the "C" locale, which is changed by
// using the setlocale() function. In Java, the default
// locale is set by user.language and user.region
// properties and is "en_US" by default, at least around
// here! Locale "en_US" differs from locale "C" in the way
// it represents dates and times. Eventually we might want
// to craft a "C" locale for Java and set Jython to use
// this by default, but that's too much work right now.
//
// For now, we hard code %x and %X to return values
// formatted in the "C" locale, i.e. the default way
// CPython does it. E.g.:
// %x == mm/dd/yy
// %X == HH:mm:SS
//
s = s + _twodigit(items[1] + 1) + "/" +
_twodigit(items[2]) + "/" +
_truncyear(items[0]);
break;
case 'X':
// See comment for %x above
s = s + _twodigit(items[3]) + ":" +
_twodigit(items[4]) + ":" +
_twodigit(items[5]);
break;
case 'Y':
// year w/ century
s = s + _padint(items[0], 4);
break;
case 'y':
// year w/o century (00-99)
s = s + _truncyear(items[0]);
break;
case 'Z':
// timezone name
if (cal == null)
cal = _tupletocal(tup);
// If items[8] == 1, we're in daylight savings time.
// -1 means the information was not available; treat this as if not in dst.
s = s + cal.getTimeZone().getDisplayName(items[8] > 0, 0);
break;
case '%':
// %
s = s + "%";
break;
default:
// TBD: should this raise a ValueError?
s = s + "%" + format.charAt(i);
i++;
break;
}
lastc = i+1;
i++;
}
// FIXME: This have problems with localized data:
// $ LANG=es_ES.UTF-8 jythont -c "import time; print time.strftime('%A')"
// s?bado
//
// On the other hand, returning unicode would break some doctests
// and will give problems when the return value of strftime is
// mixed with other strings and then the final result is stored
// on places which only support str and not unicode (such as
// os.environ)
//
// TODO: Check how CPython deals with this problem.
return new PyString(s);
}
private static void checkLocale() {
if (!Locale.getDefault().equals(currentLocale)) {
currentLocale = Locale.getDefault();
datesyms = new DateFormatSymbols(currentLocale);
shortdays = null;
shortmonths = null;
}
}
public static PyTuple strptime(String data_string) {
return strptime(data_string, DEFAULT_FORMAT_PY);
}
/**
* Calls _strptime.strptime(), for cases that our SimpleDateFormat backed
* strptime can't handle.
*/
private static PyTuple pystrptime(String data_string, String format) {
return (PyTuple) __builtin__.__import__("_strptime")
.__getattr__("strptime")
.__call__(Py.newUnicode(data_string),
Py.newUnicode(format));
}
public static PyTuple strptime(String data_string, String format) {
if (format == null || data_string == null) {
// this is not a very interesting error message, but it's the same
// as what CPython outputs
throw Py.TypeError("expected string of buffer");
}
String jformat = py2java_format(format);
if (jformat == null) {
// Format not translatable to java, fallback to _strptime
return pystrptime(data_string, format);
}
SimpleDateFormat d = new SimpleDateFormat(jformat);
Calendar cal = Calendar.getInstance();
try {
cal.setTime(d.parse(data_string));
} catch (ParseException e) {
throwValueError("time data did not match format: data=" + data_string + " fmt=" + format);
}
int isdst = -1;
if (jformat.contains("zzz")) {
isdst = cal.getTimeZone().inDaylightTime(cal.getTime()) ? 1 : 0;
}
return toTimeTuple(cal, isdst);
}
private static final String DEFAULT_FORMAT_PY = "%a %b %d %H:%M:%S %Y";
private static final HashMap py2java = new HashMap() {{
put('a', "EEE");
put('A', "EEEE");
put('b', "MMM");
put('B', "MMMM");
put('c', "EEE MMM dd HH:mm:ss yyyy");
put('d', "dd");
put('H', "HH");
put('I', "hh");
put('j', "DDD");
put('m', "MM");
put('M', "mm");
put('p', "a");
put('S', "ss");
put('U', "ww");
put('W', "ww"); // same as U ??
put('x', "MM/dd/yy");
put('X', "HH:mm:ss");
put('y', "yy");
put('Y', "yyyy");
put('Z', "zzz");
put('%', "%");
}};
// strptime formats not supported by SimpleDateFormat:
private static final List notSupported = new ArrayList() {{
add('w');
}};
/**
* @return the {@link SimpleDateFormat} format string equivalent to the
* strptime format string supplied as parameter, or null if there is
* no equivalent for SimpleDateFormat.
*/
private static String py2java_format(String format) {
StringBuilder builder = new StringBuilder();
boolean directive = false;
boolean inQuote = false;
if (format.length() == 0) {
return null;
}
for (int i = 0; i < format.length(); i++) {
char charAt = format.charAt(i);
if (charAt == '%' && !directive) {
directive = true;
continue;
}
if (!directive) {
// ascii letters are considered SimpleDateFormat directive patterns unless
// escaped
boolean needsQuote = (charAt >= 'A' && charAt <= 'Z') ||
(charAt >= 'a' && charAt <= 'z');
if (needsQuote && !inQuote || !needsQuote && inQuote) {
builder.append("'");
inQuote = needsQuote;
}
if (charAt == '\'') {
// a single quote always needs to be escaped, regardless
// whether already in a quote or not
builder.append("'");
}
builder.append(charAt);
continue;
} else if (inQuote) {
builder.append("'");
inQuote = false;
}
String translated = py2java.get(charAt);
if (translated == null && notSupported.contains(charAt)) {
return null;
}
builder.append(translated != null ? translated : charAt);
directive = false;
}
if (inQuote) {
builder.append("'");
}
return builder.toString();
}
}