[メモ]
public static final int PERIOD_COMP_RECRUITING = 0;	⇒ 準備期間
public static final int PERIOD_COMPETITION = 1;		⇒ 競争期間
public static final int PERIOD_COMP_RESULTS = 2;	⇒ 集計期間
public static final int PERIOD_SEAL_VALIDATION = 3;	⇒ 封印有効期間

ピリオドのはじまり 月曜日の18:00
public static final int PERIOD_START_HOUR = 18;	⇒ 18:00
public static final int PERIOD_START_MINS = 00;	⇒
public static final int PERIOD_START_DAY = Calendar.MONDAY;	⇒2

// The quest event and seal validation periods last for approximately one week
// with a 1 hour "interval" period sandwiched between them.
public static final int PERIOD_MINOR_LENGTH = 3600000;		⇒1時間 -- 準備期間と集計期間
public static final int PERIOD_MAJOR_LENGTH = 604800000 - PERIOD_MINOR_LENGTH;

	次の月曜日まで、あと何日か求める
	private final int getDaysToPeriodChange() 
    {
		int numDays = _calendar.get(Calendar.DAY_OF_WEEK) - PERIOD_START_DAY;

		if (numDays < 0) 
			return 0 - numDays;
        
        return 7 - numDays;


		日月火水木金土	現在の曜日
		１２３４５６７	_calendar.get(Calendar.DAY_OF_WEEK)
		２２２２２２２	PERIOD_START_DAY
		-1０１２３４５	numDays
		　０１２３４５	numDays
		１７６５４３２	(7 - numDays) ⇒ return
	}

    protected void setCalendarForNextPeriodChange() 
    {
        // Calculate the number of days until the next period
        // A period starts at 18:00 pm (local time), like on official servers.
        switch (getCurrentPeriod())
        {
        	競争期間・封印有効期間
            case PERIOD_SEAL_VALIDATION:
            case PERIOD_COMPETITION:
                int daysToChange = getDaysToPeriodChange();	次のピリオドは何日後か？
                
				月火水木金土日	現在の曜日
				７６５４３２１	daysToChange

				_calendar に次のピリオド（準備期間・集計期間）の始まりをセット
                現在時刻が月曜日で１８：００以前なら、当日の１８：００をセット
                現在時刻が月曜日で１８：００以後なら、次の週の月曜日の１８：００をセット ←バグ
                それ以外の曜日も、次の週の月曜日の１８：００をセット ←バグ

				↓↓↓↓↓↓改良案↓↓↓↓↓
				前回ＤＢにセーブした`calendar` を過ぎていれば、１分後にセット

                if (daysToChange == 7)
                	if (_calendar.get(Calendar.HOUR_OF_DAY) < PERIOD_START_HOUR)
                		daysToChange = 0;
                	else if (_calendar.get(Calendar.HOUR_OF_DAY) == PERIOD_START_HOUR && _calendar.get(Calendar.MINUTE) < PERIOD_START_MINS)
                		daysToChange = 0;
                
                // Otherwise...
                if (daysToChange > 0)
                    _calendar.add(Calendar.DATE, daysToChange);
                
                _calendar.set(Calendar.HOUR_OF_DAY, PERIOD_START_HOUR);
                _calendar.set(Calendar.MINUTE, PERIOD_START_MINS);
                break;

			準備期間・集計期間
            case PERIOD_COMP_RECRUITING:
            case PERIOD_COMP_RESULTS:
				１時間後をセット
                _calendar.add(Calendar.MILLISECOND, PERIOD_MINOR_LENGTH);
                break;
        }
    }
    
	protected class SevenSignsPeriodChange implements Runnable
	{
        public void run() 
        {
            /* 
             * Remember the period check here refers to the period just ENDED!
             */
            final int periodEnded = getCurrentPeriod();
            ----------------
            _activePeriod++;
            ----------------
            
	        switch (periodEnded) 
	        { 
	        	準備期間終了 → 競争期間開始
	            case PERIOD_COMP_RECRUITING: // Initialization
                    
                    // Start the Festival of Darkness cycle.
                    SevenSignsFestival.getInstance().startFestivalManager();                
                    
                    // Send message that Competition has begun.
                    sendMessageToAll(SystemMessage.QUEST_EVENT_PERIOD_BEGUN);
	                break;
	                
	            競争期間終了 → 集計期間開始
	            case PERIOD_COMPETITION: // Results Calculation
                    
	                // Send message that Competition has ended.
	                sendMessageToAll(SystemMessage.QUEST_EVENT_PERIOD_ENDED);
	                
	                int compWinner = getCabalHighestScore();				
	                
	                // Schedule a stop of the festival engine.
	                SevenSignsFestival.getInstance().getFestivalManagerSchedule().cancel(false);
	                
                    calcNewSealOwners();
                    
                    switch (compWinner)
                    {
                    	case CABAL_DAWN:
                    		sendMessageToAll(SystemMessage.DAWN_WON);
                    		break;
                    	case CABAL_DUSK:
                    		sendMessageToAll(SystemMessage.DUSK_WON);
                    		break;
                    }

                    _previousWinner = compWinner;
	                break;
	                
	            集計期間終了 → 封印有効期間開始
	            case PERIOD_COMP_RESULTS: // Seal Validation
                    
                    // Perform initial Seal Validation set up.
                    initializeSeals();
                    
                    // Send message that Seal Validation has begun.
                    sendMessageToAll(SystemMessage.SEAL_VALIDATION_PERIOD_BEGUN);
                    
                    _log.info("SevenSigns: The " + getCabalName(_previousWinner) + " have won the competition with " + getCurrentScore(_previousWinner) + " points!");
	                break;			

	            封印有効期間終了 → 準備期間開始
	            case PERIOD_SEAL_VALIDATION: // Reset for New Cycle
                    
	                // Ensure a cycle restart when this period ends.
                    _activePeriod = PERIOD_COMP_RECRUITING;

	                // Send message that Seal Validation has ended.
	                sendMessageToAll(SystemMessage.SEAL_VALIDATION_PERIOD_ENDED);
	                
	                // Reset all data
	                resetPlayerData();
	                resetSeals();

                    // Reset all Festival-related data and remove any unused blood offerings.
                    // NOTE: A full update of Festival data in the database is also performed.
                    SevenSignsFestival.getInstance().resetFestivalData(false);
	                
	                _dawnStoneScore = 0;
	                _duskStoneScore = 0;
	                
	                _dawnFestivalScore = 0;
	                _duskFestivalScore = 0;
	                
	                _currentCycle++;
	                break;
	        }
	    	// [L2J_JP ADD START]
	        removeNecropolisAndCatacomb();
	        spawnNecropolisAndCatacomb();
	    	// [L2J_JP ADD END]

	        // Make sure all Seven Signs data is saved for future use.
	        saveSevenSignsData(null, true);
	        
            teleLosingCabalFromDungeons(getCabalShortName(getCabalHighestScore()));

            SignsSky ss = new SignsSky();
	        
	        for (L2PcInstance player : L2World.getInstance().getAllPlayers())
	            player.sendPacket(ss);
	        
            spawnSevenSignsNPC();

            _log.info("SevenSigns: The " + getCurrentPeriodName() + " period has begun!");
	        
            次のピリオドの日時を計算する
            setCalendarForNextPeriodChange();
            
            次のピリオドへタイマーをセットする
	        SevenSignsPeriodChange sspc = new SevenSignsPeriodChange();
	        ThreadPoolManager.getInstance().scheduleGeneral(sspc, getMilliToPeriodChange());
	    }
	}
------------------------------------------------------------------------------------------------------
デバッグ：カレンダー表示
		System.out.println("SevenSigns._calendar="
    +        _calendar.get(Calendar.YEAR)
    + "/" + (_calendar.get(Calendar.MONTH) + 1)
    + "/" +  _calendar.get(Calendar.DATE)
    + " " +  _calendar.get(Calendar.HOUR_OF_DAY)
    + ":" +  _calendar.get(Calendar.MINUTE)
    + ":" +  _calendar.get(Calendar.SECOND));

		SimpleDateFormat dfm = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss (E)");
		System.out.println("SevenSigns._calendar=" + dfm.format(_calendar.getTime().getTime()));

		System.exit(0);
------------------------------------------------------------------------------------------------------
