diff options
9 files changed, 246 insertions, 35 deletions
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileTest.java index 2bf5660ac1..9f397f4f5f 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileTest.java @@ -410,22 +410,17 @@ public class FileTest extends TestCase { // Test create an illegal file String sep = File.separator; - f1 = new File(sep + "a" + sep + ".." + sep + ".." + sep); + f1 = new File(sep + ".."); try { f1.createNewFile(); fail("should throw IOE"); } catch (IOException e) { // expected; } - - // Prior to kernel version 5.7, creating "/.." returns EISDIR, and in 5.7 or later, - // such syscall returns EEXIST. In the first case, IOException is thrown. In the second - // case, false is returned. The below test is modified to accept both of them. - // See http://b/176057454 for details. - f1 = new File(sep + ".."); + f1 = new File(sep + "a" + sep + ".." + sep + ".." + sep); try { - boolean result = f1.createNewFile(); - assertFalse(result); + f1.createNewFile(); + fail("should throw IOE"); } catch (IOException e) { // expected; } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HostnameVerifierTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HostnameVerifierTest.java index 44572ab3a5..96384adba2 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HostnameVerifierTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HostnameVerifierTest.java @@ -40,6 +40,104 @@ public class HostnameVerifierTest extends TestCase implements assertFalse(hv.verify("localhost", session)); } + // copied and modified from apache http client test suite. + public void testVerify() throws Exception { + HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier(); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + InputStream in; + X509Certificate x509; + // CN=foo.com, no subjectAlt + in = new ByteArrayInputStream(X509_FOO); + x509 = (X509Certificate) cf.generateCertificate(in); + mySSLSession session = new mySSLSession(new X509Certificate[] {x509}); + assertFalse(verifier.verify("foo.com", session)); + assertFalse(verifier.verify("a.foo.com", session)); + assertFalse(verifier.verify("bar.com", session)); + + // CN=花子.co.jp, no subjectAlt + in = new ByteArrayInputStream(X509_HANAKO); + x509 = (X509Certificate) cf.generateCertificate(in); + session = new mySSLSession(new X509Certificate[] {x509}); + assertFalse(verifier.verify("\u82b1\u5b50.co.jp", session)); + assertFalse(verifier.verify("a.\u82b1\u5b50.co.jp", session)); + + // CN=foo.com, subjectAlt=bar.com + in = new ByteArrayInputStream(X509_FOO_BAR); + x509 = (X509Certificate) cf.generateCertificate(in); + session = new mySSLSession(new X509Certificate[] {x509}); + assertFalse(verifier.verify("foo.com", session)); + assertFalse(verifier.verify("a.foo.com", session)); + assertTrue(verifier.verify("bar.com", session)); + assertFalse(verifier.verify("a.bar.com", session)); + + // CN=foo.com, subjectAlt=bar.com, subjectAlt=花子.co.jp + in = new ByteArrayInputStream(X509_FOO_BAR_HANAKO); + x509 = (X509Certificate) cf.generateCertificate(in); + session = new mySSLSession(new X509Certificate[] {x509}); + assertFalse(verifier.verify("foo.com", session)); + assertFalse(verifier.verify("a.foo.com", session)); + assertTrue(verifier.verify("bar.com", session)); + assertFalse(verifier.verify("a.bar.com", session)); + // The certificate has this name in the altnames section, but OkHostnameVerifier drops + // any altnames that are improperly encoded according to RFC 5280, which requires + // non-ASCII characters to be encoded in ASCII via Punycode. + assertFalse(verifier.verify("\u82b1\u5b50.co.jp", session)); + assertFalse(verifier.verify("a.\u82b1\u5b50.co.jp", session)); + + // no CN, subjectAlt=foo.com + in = new ByteArrayInputStream(X509_NO_CNS_FOO); + x509 = (X509Certificate) cf.generateCertificate(in); + session = new mySSLSession(new X509Certificate[] {x509}); + assertTrue(verifier.verify("foo.com", session)); + assertFalse(verifier.verify("a.foo.com", session)); + + // CN=foo.com, CN=bar.com, CN=花子.co.jp, no subjectAlt + in = new ByteArrayInputStream(X509_THREE_CNS_FOO_BAR_HANAKO); + x509 = (X509Certificate) cf.generateCertificate(in); + session = new mySSLSession(new X509Certificate[] {x509}); + assertFalse(verifier.verify("foo.com", session)); + assertFalse(verifier.verify("a.foo.com", session)); + assertFalse(verifier.verify("bar.com", session)); + assertFalse(verifier.verify("a.bar.com", session)); + assertFalse(verifier.verify("\u82b1\u5b50.co.jp", session)); + assertFalse(verifier.verify("a.\u82b1\u5b50.co.jp", session)); + + // CN=*.foo.com, no subjectAlt + in = new ByteArrayInputStream(X509_WILD_FOO); + x509 = (X509Certificate) cf.generateCertificate(in); + session = new mySSLSession(new X509Certificate[] {x509}); + assertFalse(verifier.verify("foo.com", session)); + assertFalse(verifier.verify("www.foo.com", session)); + assertFalse(verifier.verify("\u82b1\u5b50.foo.com", session)); + assertFalse(verifier.verify("a.b.foo.com", session)); + + // CN=*.co.jp, no subjectAlt + in = new ByteArrayInputStream(X509_WILD_CO_JP); + x509 = (X509Certificate) cf.generateCertificate(in); + session = new mySSLSession(new X509Certificate[] {x509}); + assertFalse(verifier.verify("foo.co.jp", session)); + assertFalse(verifier.verify("\u82b1\u5b50.co.jp", session)); + + // CN=*.foo.com, subjectAlt=*.bar.com, subjectAlt=花子.co.jp + in = new ByteArrayInputStream(X509_WILD_FOO_BAR_HANAKO); + x509 = (X509Certificate) cf.generateCertificate(in); + session = new mySSLSession(new X509Certificate[] {x509}); + // try the foo.com variations + assertFalse(verifier.verify("foo.com", session)); + assertFalse(verifier.verify("www.foo.com", session)); + assertFalse(verifier.verify("\u82b1\u5b50.foo.com", session)); + assertFalse(verifier.verify("a.b.foo.com", session)); + assertFalse(verifier.verify("bar.com", session)); + assertTrue(verifier.verify("www.bar.com", session)); + assertFalse(verifier.verify("a.b.bar.com", session)); + // The certificate has this name in the altnames section, but OkHostnameVerifier drops + // any altnames that are improperly encoded according to RFC 5280, which requires + // non-ASCII characters to be encoded in ASCII via Punycode. + assertFalse(verifier.verify("\u82b1\u5b50.bar.com", session)); + assertFalse(verifier.verify("\u82b1\u5b50.co.jp", session)); + assertFalse(verifier.verify("a.\u82b1\u5b50.co.jp", session)); + } + public void testSubjectAlt() throws Exception { CertificateFactory cf = CertificateFactory.getInstance("X.509"); InputStream in = new ByteArrayInputStream(X509_MULTIPLE_SUBJECT_ALT); diff --git a/luni/src/test/java/libcore/java/net/URLTest.java b/luni/src/test/java/libcore/java/net/URLTest.java index 1ff9b336cb..066442e44e 100644 --- a/luni/src/test/java/libcore/java/net/URLTest.java +++ b/luni/src/test/java/libcore/java/net/URLTest.java @@ -582,6 +582,26 @@ public final class URLTest extends TestCase { assertEquals("http://host/a/c", url.toString()); // RI doesn't canonicalize } + public void testPathContainsBackslash() throws Exception { + URL url = new URL("http://host\\path@foo"); + assertEquals("\\path@foo", url.getPath()); + assertEquals("host", url.getHost()); + } + + public void testQueryContainsForwardSlash() throws Exception { + URL url = new URL("http://host?query/foo"); + assertEquals("", url.getPath()); + assertEquals("host", url.getHost()); + assertEquals("query/foo", url.getQuery()); + } + + public void testFragmentContainsForwardSlash() throws Exception { + URL url = new URL("http://host#fragment/foo"); + assertEquals("", url.getPath()); + assertEquals("host", url.getHost()); + assertEquals("fragment/foo", url.getRef()); + } + public void testRelativePathAndFragment() throws Exception { URL base = new URL("http://host/file"); assertEquals("http://host/another#fragment", new URL(base, "another#fragment").toString()); diff --git a/ojluni/src/main/java/java/net/URLStreamHandler.java b/ojluni/src/main/java/java/net/URLStreamHandler.java index d5380c1c58..01862f6d5c 100644 --- a/ojluni/src/main/java/java/net/URLStreamHandler.java +++ b/ojluni/src/main/java/java/net/URLStreamHandler.java @@ -167,12 +167,25 @@ public abstract class URLStreamHandler { if (!isUNCName && (start <= limit - 2) && (spec.charAt(start) == '/') && (spec.charAt(start + 1) == '/')) { start += 2; + // BEGIN Android-changed: Check for all hostname termination chars. http://b/110955991 + /* i = spec.indexOf('/', start); if (i < 0 || i > limit) { i = spec.indexOf('?', start); if (i < 0 || i > limit) i = limit; } + */ + LOOP: for (i = start; i < limit; i++) { + switch (spec.charAt(i)) { + case '/': // Start of path + case '\\': // Start of path - see https://url.spec.whatwg.org/#host-state + case '?': // Start of query + case '#': // Start of fragment + break LOOP; + } + } + // END Android-changed: Check for all hostname termination chars. http://b/110955991 host = authority = spec.substring(start, i); @@ -266,7 +279,9 @@ public abstract class URLStreamHandler { // Parse the file path if any if (start < limit) { - if (spec.charAt(start) == '/') { + // Android-changed: Check for all hostname termination chars. http://b/110955991 + // if (spec.charAt(start) == '/') { + if (spec.charAt(start) == '/' || spec.charAt(start) == '\\') { path = spec.substring(start, limit); } else if (path != null && path.length() > 0) { isRelPath = true; diff --git a/ojluni/src/main/java/java/time/chrono/JapaneseEra.java b/ojluni/src/main/java/java/time/chrono/JapaneseEra.java index 0c9e3e81e1..d52f3c19c9 100644 --- a/ojluni/src/main/java/java/time/chrono/JapaneseEra.java +++ b/ojluni/src/main/java/java/time/chrono/JapaneseEra.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -73,11 +73,14 @@ import java.io.ObjectStreamException; import java.io.Serializable; import java.time.DateTimeException; import java.time.LocalDate; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.TextStyle; import java.time.temporal.ChronoField; import java.time.temporal.TemporalField; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.util.Arrays; +import java.util.Locale; import java.util.Objects; import sun.util.calendar.CalendarDate; @@ -85,10 +88,33 @@ import sun.util.calendar.CalendarDate; /** * An era in the Japanese Imperial calendar system. * <p> - * This class defines the valid eras for the Japanese chronology. - * Japan introduced the Gregorian calendar starting with Meiji 6. - * Only Meiji and later eras are supported; - * dates before Meiji 6, January 1 are not supported. + * The Japanese government defines the official name and start date of + * each era. Eras are consecutive and their date ranges do not overlap, + * so the end date of one era is always the day before the start date + * of the next era. + * <p> + * The Java SE Platform supports all eras defined by the Japanese government, + * beginning with the Meiji era. Each era is identified in the Platform by an + * integer value and a name. The {@link #of(int)} and {@link #valueOf(String)} + * methods may be used to obtain a singleton instance of JapaneseEra for each + * era. The {@link #values()} method returns the singleton instances of all + * supported eras. + * <p> + * For convenience, this class declares a number of public static final fields + * that refer to singleton instances returned by the values() method. + * + * @apiNote + * The fields declared in this class may evolve over time, in line with the + * results of the {@link #values()} method. However, there is not necessarily + * a 1:1 correspondence between the fields and the singleton instances. + * + * @apiNote + * The Japanese government may announce a new era and define its start + * date but not its official name. In this scenario, the singleton instance + * that represents the new era may return a name that is not stable until + * the official name is defined. Developers should exercise caution when + * relying on the name returned by any singleton instance that does not + * correspond to a public static final field. * * @implSpec * This class is immutable and thread-safe. @@ -120,14 +146,20 @@ public final class JapaneseEra */ public static final JapaneseEra SHOWA = new JapaneseEra(1, LocalDate.of(1926, 12, 25)); /** - * The singleton instance for the 'Heisei' era (1989-01-08 - current) + * The singleton instance for the 'Heisei' era (1989-01-08 - 2019-04-30) * which has the value 2. */ public static final JapaneseEra HEISEI = new JapaneseEra(2, LocalDate.of(1989, 1, 8)); + /** + * The singleton instance for the 'Reiwa' era (2019-05-01 - current) + * which has the value 3. The end date of this era is not specified, unless + * the Japanese Government defines it. + */ + private static final JapaneseEra REIWA = new JapaneseEra(3, LocalDate.of(2019, 5, 1)); - // the number of defined JapaneseEra constants. - // There could be an extra era defined in its configuration. - private static final int N_ERA_CONSTANTS = HEISEI.getValue() + ERA_OFFSET; + // The number of predefined JapaneseEra constants. + // There may be a supplemental era defined by the property. + private static final int N_ERA_CONSTANTS = REIWA.getValue() + ERA_OFFSET; /** * Serialization version. @@ -145,6 +177,7 @@ public final class JapaneseEra KNOWN_ERAS[1] = TAISHO; KNOWN_ERAS[2] = SHOWA; KNOWN_ERAS[3] = HEISEI; + KNOWN_ERAS[4] = REIWA; for (int i = N_ERA_CONSTANTS; i < ERA_CONFIG.length; i++) { CalendarDate date = ERA_CONFIG[i].getSinceDate(); LocalDate isoDate = LocalDate.of(date.getYear(), date.getMonth(), date.getDayOfMonth()); @@ -185,10 +218,18 @@ public final class JapaneseEra //----------------------------------------------------------------------- /** * Obtains an instance of {@code JapaneseEra} from an {@code int} value. + * <ul> + * <li>The value {@code 1} is associated with the 'Showa' era, because + * it contains 1970-01-01 (ISO calendar system).</li> + * <li>The values {@code -1} and {@code 0} are associated with two earlier + * eras, Meiji and Taisho, respectively.</li> + * <li>A value greater than {@code 1} is associated with a later era, + * beginning with Heisei ({@code 2}).</li> + * </ul> * <p> - * The {@link #SHOWA} era that contains 1970-01-01 (ISO calendar system) has the value 1 - * Later era is numbered 2 ({@link #HEISEI}). Earlier eras are numbered 0 ({@link #TAISHO}), - * -1 ({@link #MEIJI}), only Meiji and later eras are supported. + * Every instance of {@code JapaneseEra} that is returned from the {@link #values()} + * method has an int value (available via {@link Era#getValue()} which is + * accepted by this method. * * @param japaneseEra the era to represent * @return the {@code JapaneseEra} singleton, not null @@ -236,6 +277,28 @@ public final class JapaneseEra return Arrays.copyOf(KNOWN_ERAS, KNOWN_ERAS.length); } + /** + * {@inheritDoc} + * + * @param style {@inheritDoc} + * @param locale {@inheritDoc} + */ + @Override + public String getDisplayName(TextStyle style, Locale locale) { + // If this JapaneseEra is a supplemental one, obtain the name from + // the era definition. + if (getValue() > N_ERA_CONSTANTS - ERA_OFFSET) { + Objects.requireNonNull(locale, "locale"); + return style.asNormal() == TextStyle.NARROW ? getAbbreviation() : getName(); + } + + return new DateTimeFormatterBuilder() + .appendText(ERA, style) + .toFormatter(locale) + .withChronology(JapaneseChronology.INSTANCE) + .format(this == MEIJI ? MEIJI_6_ISODATE : since); + } + //----------------------------------------------------------------------- /** * Obtains an instance of {@code JapaneseEra} from a date. @@ -337,11 +400,7 @@ public final class JapaneseEra //----------------------------------------------------------------------- String getAbbreviation() { - int index = ordinal(getValue()); - if (index == 0) { - return ""; - } - return ERA_CONFIG[index].getAbbreviation(); + return ERA_CONFIG[ordinal(getValue())].getAbbreviation(); } String getName() { diff --git a/ojluni/src/main/java/java/util/JapaneseImperialCalendar.java b/ojluni/src/main/java/java/util/JapaneseImperialCalendar.java index c569981054..9bca22a57a 100644 --- a/ojluni/src/main/java/java/util/JapaneseImperialCalendar.java +++ b/ojluni/src/main/java/java/util/JapaneseImperialCalendar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,6 +49,7 @@ import sun.util.calendar.LocalGregorianCalendar; * 2 Taisho 1912-07-30 midnight local time * 3 Showa 1926-12-25 midnight local time * 4 Heisei 1989-01-08 midnight local time + * 5 Reiwa 2019-05-01 midnight local time * ------------------------------------------------------ * </tt></pre> * @@ -100,6 +101,11 @@ class JapaneseImperialCalendar extends Calendar { */ public static final int HEISEI = 4; + /** + * The ERA constant designating the Reiwa era. + */ + private static final int REIWA = 5; + private static final int EPOCH_OFFSET = 719163; // Fixed date of January 1, 1970 (Gregorian) private static final int EPOCH_YEAR = 1970; @@ -132,6 +138,9 @@ class JapaneseImperialCalendar extends Calendar { // Fixed date of the first date of each era. private static final long[] sinceFixedDates; + // The current era + private static final int currentEra; + /* * <pre> * Greatest Least @@ -227,13 +236,25 @@ class JapaneseImperialCalendar extends Calendar { // eras[BEFORE_MEIJI] and sinceFixedDate[BEFORE_MEIJI] are the // same as Gregorian. int index = BEFORE_MEIJI; + // Android-removed: Zygote could initialize this class when system has outdated time. + // int current = index; sinceFixedDates[index] = gcal.getFixedDate(BEFORE_MEIJI_ERA.getSinceDate()); eras[index++] = BEFORE_MEIJI_ERA; for (Era e : es) { + // Android-removed: Zygote could initialize this class when system has outdated time. + // Android hard-code the current era. Unlike upstream, Android does not add the new era + // in the code until the new era arrives. Thus, Android can't have newer era than the + // real world. currentEra is the latest Era that Android knows about. + // if(e.getSince(TimeZone.NO_TIMEZONE) < System.currentTimeMillis()) { + // current = index; + // } CalendarDate d = e.getSinceDate(); sinceFixedDates[index] = gcal.getFixedDate(d); eras[index++] = e; } + // Android-changed: Zygote could initialize this class when system has outdated time. + // currentEra = current; + currentEra = REIWA; LEAST_MAX_VALUES[ERA] = MAX_VALUES[ERA] = eras.length - 1; @@ -1713,12 +1734,12 @@ class JapaneseImperialCalendar extends Calendar { } } else if (transitionYear) { if (jdate.getYear() == 1) { - // As of Heisei (since Meiji) there's no case + // As of Reiwa (since Meiji) there's no case // that there are multiple transitions in a // year. Historically there was such // case. There might be such case again in the // future. - if (era > HEISEI) { + if (era > REIWA) { CalendarDate pd = eras[era - 1].getSinceDate(); if (normalizedYear == pd.getYear()) { d.setMonth(pd.getMonth()).setDayOfMonth(pd.getDayOfMonth()); @@ -1853,7 +1874,7 @@ class JapaneseImperialCalendar extends Calendar { year = isSet(YEAR) ? internalGet(YEAR) : 1; } else { if (isSet(YEAR)) { - era = eras.length - 1; + era = currentEra; year = internalGet(YEAR); } else { // Equivalent to 1970 (Gregorian) @@ -2338,7 +2359,7 @@ class JapaneseImperialCalendar extends Calendar { * default ERA is the current era, but a zero (unset) ERA means before Meiji. */ private int internalGetEra() { - return isSet(ERA) ? internalGet(ERA) : eras.length - 1; + return isSet(ERA) ? internalGet(ERA) : currentEra; } /** diff --git a/ojluni/src/main/java/java/util/Locale.java b/ojluni/src/main/java/java/util/Locale.java index c96bb1380a..434922a983 100644 --- a/ojluni/src/main/java/java/util/Locale.java +++ b/ojluni/src/main/java/java/util/Locale.java @@ -520,7 +520,7 @@ import sun.util.locale.ParseStatus; * <td><a href="http://site.icu-project.org/download/58">ICU 58.2</a></td> * <td><a href="http://cldr.unicode.org/index/downloads/cldr-30">CLDR 30.0.3</a></td> * <td><a href="http://www.unicode.org/versions/Unicode9.0.0/">Unicode 9.0</a></td></tr> - * <tr><td>Android 9.0 (TBD)</td> + * <tr><td>Android 9.0 (Pie)</td> * <td><a href="http://site.icu-project.org/download/60">ICU 60.2</a></td> * <td><a href="http://cldr.unicode.org/index/downloads/cldr-32">CLDR 32.0.1</a></td> * <td><a href="http://www.unicode.org/versions/Unicode10.0.0/">Unicode 10.0</a></td></tr> diff --git a/ojluni/src/main/java/sun/util/calendar/Era.java b/ojluni/src/main/java/sun/util/calendar/Era.java index a013c57afd..7c02cce73e 100644 --- a/ojluni/src/main/java/sun/util/calendar/Era.java +++ b/ojluni/src/main/java/sun/util/calendar/Era.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,6 +49,7 @@ import java.util.TimeZone; * Taisho 1912-07-30 midnight local time * Showa 1926-12-26 midnight local time * Heisei 1989-01-08 midnight local time + * Reiwa 2019-05-01 midnight local time * Julian calendar BeforeCommonEra -292275055-05-16T16:47:04.192Z * CommonEra 0000-12-30 midnight local time * Taiwanese calendar MinGuo 1911-01-01 midnight local time diff --git a/ojluni/src/main/resources/calendars.properties b/ojluni/src/main/resources/calendars.properties index 49f68aca49..6007d7a57d 100644 --- a/ojluni/src/main/resources/calendars.properties +++ b/ojluni/src/main/resources/calendars.properties @@ -29,12 +29,14 @@ # Taisho since 1912-07-30 00:00:00 local time (Gregorian) # Showa since 1926-12-25 00:00:00 local time (Gregorian) # Heisei since 1989-01-08 00:00:00 local time (Gregorian) +# Reiwa since 2019-05-01 00:00:00 local time (Gregorian) calendar.japanese.type: LocalGregorianCalendar calendar.japanese.eras: \ name=Meiji,abbr=M,since=-3218832000000; \ name=Taisho,abbr=T,since=-1812153600000; \ name=Showa,abbr=S,since=-1357603200000; \ - name=Heisei,abbr=H,since=600220800000 + name=Heisei,abbr=H,since=600220800000; \ + name=Reiwa,abbr=R,since=1556668800000 # # Taiwanese calendar |