Overview

Namespaces

  • pgn
    • exceptions
    • tags
  • utils

Classes

  • pgn\Game
  • pgn\PGN
  • pgn\tags\Annotator
  • pgn\tags\Black
  • pgn\tags\BlackElo
  • pgn\tags\BlackNA
  • pgn\tags\BlackTitle
  • pgn\tags\BlackType
  • pgn\tags\BlackUSCF
  • pgn\tags\Board
  • pgn\tags\Date
  • pgn\tags\ECO
  • pgn\tags\Event
  • pgn\tags\EventCountry
  • pgn\tags\EventDate
  • pgn\tags\EventRounds
  • pgn\tags\EventSponsor
  • pgn\tags\EventType
  • pgn\tags\FEN
  • pgn\tags\Mode
  • pgn\tags\NIC
  • pgn\tags\Opening
  • pgn\tags\PlyCount
  • pgn\tags\Result
  • pgn\tags\Round
  • pgn\tags\Section
  • pgn\tags\SetUp
  • pgn\tags\Site
  • pgn\tags\Source
  • pgn\tags\SourceDate
  • pgn\tags\Stage
  • pgn\tags\SubVariation
  • pgn\tags\Tag
  • pgn\tags\Termination
  • pgn\tags\Time
  • pgn\tags\TimeControl
  • pgn\tags\UknownTag
  • pgn\tags\UTCDate
  • pgn\tags\UTCTime
  • pgn\tags\Variation
  • pgn\tags\White
  • pgn\tags\WhiteElo
  • pgn\tags\WhiteNA
  • pgn\tags\WhiteTitle
  • pgn\tags\WhiteType
  • pgn\tags\WhiteUSCF
  • utils\Parser
  • utils\String

Exceptions

  • pgn\exceptions\InvalidClassNameException
  • pgn\exceptions\InvalidDataException
  • pgn\exceptions\InvalidGameFormatException
  • pgn\exceptions\InvalidGamePathException
  • pgn\exceptions\PGNException
  • utils\ParserException
  • Overview
  • Namespace
  • Class
  1: <?php
  2: /* 
  3:  * Copyright (c) 2016 Geraldo B. Landre
  4:  * 
  5:  * See the file LICENSE for copying permission.
  6:  */
  7: namespace pgn\tags;
  8: 
  9: use utils\Parser;
 10: 
 11: /**
 12:  * Description of Date:
 13:  * The Date tag value gives the starting date for the game.  (Note: this is not
 14:  * necessarily the same as the starting date for the event.)  The date is given
 15:  * with respect to the local time of the site given in the Event tag.  The Date
 16:  * tag value field always uses a standard ten character format: "YYYY.MM.DD".  The
 17:  * first four characters are digits that give the year, the next character is a
 18:  * period, the next two characters are digits that give the month, the next
 19:  * character is a period, and the final two characters are digits that give the
 20:  * day of the month.  If the any of the digit fields are not known, then question
 21:  * marks are used in place of the digits.
 22:  * @see pgn_standard.txt
 23:  * @author Geraldo
 24:  */
 25: class Date extends Tag {
 26:     static private $ERROR_MESSAGE = "Date should be formatted [Y.m.d] Example: 1992.08.31";
 27: 
 28:     /**
 29:      * 
 30:      * @assert ("????.??.??") === true
 31:      * Convention: year zero is valid:
 32:      * @assert ("0000.??.??") === true
 33:      * @assert ("????.??.4?") === false
 34:      * @assert ("9999.?9.31") === false
 35:      * @assert ("9999.?9.30") === true
 36:      * @assert ("????.2?.??") === false
 37:      * @assert ("????.1?.??") === true
 38:      * @assert ("????.0?.??") === true
 39:      * @assert ("????.03.??") === true
 40:      * @assert ("????.02.??") === true
 41:      * @assert ("0001.02.?1") === true
 42:      * @assert ("????.02.3?") === false
 43:      * @assert ("????.02.1?") === true
 44:      * @assert ("????.02.2?") === true
 45:      * @assert ("????.02.29") === true
 46:      * @assert ("1992.08.31") === true
 47:      * @assert ("2015.11.2?") === true
 48:      * @assert ("2015.11.??") === true
 49:      * @assert ("2015.1?.??") === true
 50:      * @assert ("2015.??.??") === true
 51:      * @assert ("201?.??.??") === true
 52:      * @assert ("20??.??.??") === true
 53:      * @assert ("2???.??.??") === true
 54:      * @assert (new \DateTime) === true
 55:      * @assert ("1992.0?.??") === true
 56:      * @assert ("1992.02.3?") === false
 57:      * @assert ("1992.4?.??") === false
 58:      * @assert ("1992.1?.??") === true
 59:      * @assert ("1992.31.08") === false
 60:      * @assert ("08.31.1992") === false
 61:      * @assert ("08/31/1992") === false
 62:      * @assert ("1992/08/31") === false
 63:      * @assert (null) === false
 64:      * @assert (new \stdClass) === false
 65:      * @assert (array (1988,11,02)) === false
 66:      * @assert ("aabb.vv.aa") === false
 67:      * Testes relacionados a anos bissextos
 68:      * @assert ("??11.02.29") === false
 69:      * @assert ("??12.02.29") === true
 70:      * @assert ("??14.02.29") === false
 71:      * @assert ("??16.02.29") === true
 72:      * @assert ("??18.02.29") === false
 73:      * @assert ("??20.02.29") === true
 74:      * @assert ("??22.02.29") === false
 75:      * @assert ("??24.02.29") === true
 76:      * @assert ("??26.02.29") === false
 77:      * @assert ("??28.02.29") === true
 78:      * @assert ("2015.02.29") === false
 79:      * 
 80:      * @todo to assert errorMsg contents after each test
 81:      * 
 82:      * @param \DateTime $date
 83:      * @return boolean returns true if data is a valid Date
 84:      */
 85:     public function validate($date) {
 86: 
 87:         if ($date instanceof \DateTime) {
 88:             return true;
 89:         }
 90: 
 91:         if (!$this->checkFormat($date)) {
 92:             return false;
 93:         }
 94: 
 95:         list($yearPartial, $monthPartial, $dayPartial) = explode(".", strval($date));
 96: 
 97:         // 1 is better for default values in day and month
 98:         $day = str_replace('?', '1', $dayPartial);
 99:         $month = $this->monthReplaceDefaults($monthPartial);
100:         $year = $this->yearReplaceDefaults($yearPartial);
101: 
102:         // special case
103:         if ($month == 2 && $day == 29) {
104:             return $this->isBissextile($year);
105:         }
106:         
107:         $d = \DateTime::createFromFormat('Y.m.d', "$year.$month.$day");
108:         $errors = \DateTime::getLastErrors();
109:         
110:         $isValid = ($d !== false && empty($errors['warnings']));
111: 
112:         $this->errorMsg = ($isValid) ? self::$ERROR_MESSAGE : '';
113:         return $isValid;
114:     }
115: 
116:     /**
117:      * @assert () == "????.??.??"
118:      * 
119:      * @return string Default Date Value
120:      */
121:     public function getDefaultValue() {
122:         return '????.??.??';
123:     }
124: 
125:     /**
126:      * @assert () == "Date"
127:      * 
128:      * @return string the name of the tag
129:      */
130:     public function getName() {
131:         $parsed = Parser::parseClassName(get_class());
132:         return $parsed['className'];
133:     }
134: 
135:     /**
136:      * @assert (null) === '????.??.??'
137:      * @assert ("1988.02.11") === '1988.02.11'
138:      * @assert (\DateTime::createFromFormat('d/m/Y', '02/11/1988')) === '1988.11.02'
139:      * 
140:      * @param \DateTime|string $data
141:      * @return string
142:      */
143:     protected function formatted($data) {
144:         if (isset($data)) {
145:             if ($data instanceof \DateTime) {
146:                 return $data->format('Y.m.d');
147:             } else {
148:                 return strval($data);
149:             }
150:         }
151:         return $this->getDefaultValue();
152:     }
153: 
154:     /**
155:      * WARNING: this method takes on $year having only numbers or question marks
156:      * 
157:      * @assert ("????") === '1124'
158:      * @assert ("19??") === '1924'
159:      * @assert ("1???") === '1124'
160:      * @assert ("2???") === '2124'
161:      * @assert ("?0??") === '1024'
162:      * @assert ("20?6") === '2016'
163:      * @assert ("20?4") === '2024'
164:      * @assert ("198?") === '1984'
165:      * @assert ("??11") === '1111'
166:      * @assert ("??12") === '1112'
167:      * @assert ("??14") === '1114'
168:      * @assert ("??16") === '1116'
169:      * @assert ("??18") === '1118'
170:      * @assert ("??20") === '1120'
171:      * @assert ("??22") === '1122'
172:      * @assert ("??24") === '1124'
173:      * @assert ("??26") === '1126'
174:      * @assert ("??28") === '1128'
175:      * @assert ("0000") === '0000'
176:      * 
177:      * @param string $year
178:      * @return string the year with question marks replaced by default values
179:      */
180:     protected function yearReplaceDefaults($year) {
181:         // first two digits are useless on verification
182:         // that's why they are replaced by 1
183:         $century = str_replace('?', 1, substr($year, 0, 2));
184: 
185:         // the third digit can be even or odd
186:         // if it's even (or zero) and with the 4th digit being (0, 4 or 8) than 
187:         //     it may be bissextile
188:         // if it's odd and with the 4th digit being (2 or 6) than 
189:         //     it also may be bissextile
190:         // default value is 1, because odd is easier
191:         $decade = $year[2];
192:         if ($year[2] == '?') {
193:             if ($year[3] != '?') {
194:                 $decade = ($year[3] == 2 || $year[3] == 6) ? 1 : 2;
195:             } else {
196:                 $decade = 2;
197:             }
198:         }
199: 
200:         // assumes that third digit is even...
201:         $defaultUnity = 4;
202:         // if third digit is odd
203:         if ($decade % 2 != 0) {
204:             $defaultUnity = 2;
205:         }
206: 
207:         $unity = ($year[3] != '?') ? $year[3] : $defaultUnity;
208: 
209:         return $century . $decade . $unity;
210:     }
211: 
212:     /**
213:      * @assert ('??') === '11'
214:      * @assert ('?0') === '10'
215:      * @assert ('?2') === '12'
216:      * @assert ('?3') === '03'
217:      * @assert ('?9') === '09'
218:      * 
219:      * @param string $monthPartial with two characters
220:      * @return string
221:      */
222:     protected function monthReplaceDefaults($monthPartial) {
223:         $tens = $monthPartial[0];
224:         $unity = $monthPartial[1] != '?' ? $monthPartial[1] : '1';
225:         
226:         if ($tens == '?') {
227:             if ($unity <= 2) {
228:                 $tens = '1';
229:             } else {
230:                 $tens = '0';
231:             }
232:         }
233: 
234:         $newMonth = $tens . $unity;
235:         return $newMonth;
236:     }
237: 
238:     /**
239:      * WARNING: this method takes on $year having only numbers or question marks
240:      * 
241:      * @assert ("2015") === false
242:      * @assert ("2014") === false
243:      * @assert ("0004") === true
244:      * @assert ("0008") === true
245:      * @assert ("1000") === false
246:      * @assert ("2000") === true
247:      * @assert ("2016") === true
248:      * @assert ("2012") === true
249:      * @param string $year
250:      * @return boolean true if a given year is bissextile
251:      */
252:     protected function isBissextile($year) {
253: 
254:         // return ( ( ano % 4 == 0 && ano % 100 != 0 ) || ano % 400 == 0 )
255:         if ($year % 400 == 0) {
256:             return true;
257:         }
258: 
259:         if ($year % 4 == 0) {
260:             if ($year % 100 != 0) {
261:                 return true;
262:             }
263:         }
264: 
265:         return false;
266:     }
267: 
268:     private function checkFormat($date) {
269:         if (!is_string($date) || !preg_match_all("/^" . self::validPattern() . "$/", $date)) {
270:             $this->errorMsg = self::$ERROR_MESSAGE;
271:             return false;
272:         }
273:         return true;
274:     }
275:     
276:     /**
277:      * 
278:      * @return string Valid Regular Expression Pattern for PGN Dates
279:      */
280:     static public function validPattern() {
281:         return "(\d|\?){4}\.(\d|\?){2}\.(\d|\?){2}";
282:     }
283: }
284: 
API documentation generated by ApiGen